API Reference
Overview
The DevWannaSpace REST API lets you interact with your workspace programmatically. All API routes are scoped to the authenticated user — you can only access your own data.
https://api.devwannaspace.com
Runtime
Cloudflare Workers
Framework
Hono
Database
NeonDB + Drizzle
Authentication
Required on all /api/* routes
DevWannaSpace uses Clerk for authentication. Every request to an /api/* route must include a valid Bearer token obtained from Clerk's frontend SDK.
Authorization: Bearer <clerk_session_token>
All data is automatically scoped to the authenticated user's ID. You cannot access or modify another user's resources.
Errors
All error responses return a JSON object with an error field.
| Status | Meaning | Example body |
|---|---|---|
401 |
Missing or invalid token | {"error": "Unauthorized"} |
500 |
Internal server / sync error | {"error": "Sync pull failed"} |
Projects
ResourceSchema
| Field | Type | Description |
|---|---|---|
| id | string | Primary key (UUID) |
| userId | string | Owner's Clerk user ID (auto-set) |
| name | string | Project name — required |
| color | string | Hex color for the project label — required |
| description | string | null | Optional project description |
| createdAt | timestamp | ISO 8601 datetime |
| updatedAt | timestamp | ISO 8601 datetime |
/api/projects
List all projects
Returns all projects belonging to the authenticated user, ordered by createdAt DESC.
Response 200
[
{
"id": "proj_01j...",
"userId": "user_2abc...",
"name": "DevWannaSpace V2",
"color": "#5e6ad2",
"description": "Next major release",
"createdAt": "2026-08-01T10:00:00.000Z",
"updatedAt": "2026-08-02T09:30:00.000Z"
}
]
/api/projects
Create a project
Request body
{
"id": "proj_01j...", // required — client-generated
"name": "My Project", // required
"color": "#5e6ad2", // required
"description": "Optional"
}
Response 200 — created project object
/api/projects/:id
Update a project
Send any subset of project fields to update. The record must belong to the authenticated user.
/api/projects/:id
Delete a project
Deletes the project and records it in sync_deletions for offline-capable clients. All issues with projectId referencing this project are cascade-deleted.
Response 200
{"success": true}
Issues
ResourceSchema
| Field | Type | Values / Notes |
|---|---|---|
| id | string | Primary key (UUID) |
| userId | string | Owner's Clerk user ID (auto-set) |
| title | string | Required |
| description | string | Required (can be empty string) |
| status | string |
Todo
In Progress
Done
Canceled
|
| priority | string |
No Priority
Low
Medium
High
Urgent
|
| projectId | string | null | FK → projects.id (cascade delete) |
| dueDate | timestamp | null | ISO 8601 or null |
| createdAt | timestamp | Auto-set on insert |
| updatedAt | timestamp | Auto-updated on PUT |
/api/issues
List all issues
Returns all issues for the authenticated user, ordered by createdAt DESC.
[
{
"id": "iss_01j...",
"title": "Fix dark mode flicker",
"description": "...",
"status": "In Progress",
"priority": "High",
"projectId": "proj_01j...",
"dueDate": "2026-08-15T00:00:00.000Z",
"createdAt": "2026-08-01T10:00:00.000Z",
"updatedAt": "2026-08-02T09:30:00.000Z"
}
]
/api/issues
Create an issue
{
"id": "iss_01j...",
"title": "My new issue",
"description": "",
"status": "Todo",
"priority": "Medium",
"projectId": "proj_01j...", // optional
"dueDate": "2026-09-01T00:00:00.000Z" // optional
}
/api/issues/:id
Update an issue
Partial update — send only the fields you want to change. updatedAt is automatically set to the current timestamp.
/api/issues/:id
Delete an issue
Deletes the issue and writes a tombstone to sync_deletions.
Pages
ResourceSchema
| Field | Type | Notes |
|---|---|---|
| id | string | Primary key |
| userId | string | Auto-set |
| parentId | string | null | Self-referential — enables nested pages |
| title | string | Required |
| icon | string | null | Emoji or icon identifier |
| content | jsonb | null | Rich text as JSON (editor block format) |
| isFavorite | boolean | Default false |
| isDeleted | boolean | Soft-delete flag; default false |
| coverColor | string | null | Hex color for page cover |
| projectId | string | null | FK → projects.id (set null on delete) |
| position | integer | null | Sort order (results ordered ASC by position) |
/api/pages
List all pages
Returns all pages for the user, ordered by position ASC. Nested hierarchy (parentId) must be reconstructed client-side.
/api/pages
Create a page
{
"id": "page_01j...",
"title": "Meeting Notes",
"content": { "blocks": [] }, // rich-text JSON
"parentId": null, // or another page id
"projectId": null,
"position": 0,
"isFavorite": false,
"coverColor": "#5e6ad2" // optional
}
/api/pages/:id
Update a page
Partial update. updatedAt is auto-set. Use this to move pages (update parentId or position).
/api/pages/:id
Delete a page
Hard-deletes the page and writes a tombstone to sync_deletions.
Notifications
ResourceSchema
| Field | Type | Notes |
|---|---|---|
| id | string | Primary key |
| userId | string | Auto-set |
| title | string | Required |
| message | string | Required |
| isRead | boolean | Default false — use PUT to mark as read |
| createdAt | timestamp | Ordered DESC in list |
/api/notifications
List notifications
Ordered by createdAt DESC.
/api/notifications/:id
Mark as read
{ "isRead": true }
/api/notifications/:id
Delete a notification
Tombstoned in sync_deletions.
WatermelonDB Sync
AdvancedPowers offline-first sync across Desktop, Web, and Mobile
The sync endpoint implements a last-write-wins strategy compatible with WatermelonDB's synchronize() API. All four resource tables (projects, issues, pages, notifications) are synced in one round-trip.
/api/sync?since=<timestamp_ms>
Pull changes
Returns all records created or updated since the given Unix timestamp (milliseconds). Pass since=0 for the initial full pull.
Query parameters
| Param | Type | Description |
|---|---|---|
| since | number | Unix ms timestamp of last successful pull. Default: 0 |
Response 200
{
"changes": {
"projects": {
"created": [ { ...projectObject, "created_at": 1754000000000, "updated_at": 1754000000000 } ],
"updated": [ { ...projectObject } ],
"deleted": ["proj_01j..."]
},
"issues": { "created": [], "updated": [], "deleted": [] },
"pages": { "created": [], "updated": [], "deleted": [] },
"notifications": { "created": [], "updated": [], "deleted": [] }
},
"timestamp": 1754100000000
}
Field names in the sync response use snake_case (e.g. created_at, project_id) to match WatermelonDB's column mapping convention.
/api/sync
Push local changes
Pushes local changes to the server. Each table's created, updated, and deleted arrays are processed in order. Conflicts resolve via last-write-wins.
Request body
{
"changes": {
"projects": {
"created": [ { "id": "proj_01j...", "name": "...", "color": "#5e6ad2", "created_at": 1754000000000, "updated_at": 1754000000000 } ],
"updated": [ { "id": "proj_01j...", "name": "Renamed", "updated_at": 1754100000000 } ],
"deleted": ["proj_old..."]
},
"issues": { "created": [], "updated": [], "deleted": [] },
"pages": { "created": [], "updated": [], "deleted": [] },
"notifications": { "created": [], "updated": [], "deleted": [] }
}
}
Response 200
{ "success": true }