GitHub

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.

Base URL
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.

Request Header
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

Resource

Schema

Field Type Description
idstringPrimary key (UUID)
userIdstringOwner's Clerk user ID (auto-set)
namestringProject name — required
colorstringHex color for the project label — required
descriptionstring | nullOptional project description
createdAttimestampISO 8601 datetime
updatedAttimestampISO 8601 datetime
GET /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"
  }
]
POST /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

PUT /api/projects/:id Update a project

Send any subset of project fields to update. The record must belong to the authenticated user.

DELETE /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

Resource

Schema

Field Type Values / Notes
idstringPrimary key (UUID)
userIdstringOwner's Clerk user ID (auto-set)
titlestringRequired
descriptionstringRequired (can be empty string)
status string Todo In Progress Done Canceled
priority string No Priority Low Medium High Urgent
projectIdstring | nullFK → projects.id (cascade delete)
dueDatetimestamp | nullISO 8601 or null
createdAttimestampAuto-set on insert
updatedAttimestampAuto-updated on PUT
GET /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"
  }
]
POST /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
}
PUT /api/issues/:id Update an issue

Partial update — send only the fields you want to change. updatedAt is automatically set to the current timestamp.

DELETE /api/issues/:id Delete an issue

Deletes the issue and writes a tombstone to sync_deletions.

Pages

Resource

Schema

Field Type Notes
idstringPrimary key
userIdstringAuto-set
parentIdstring | nullSelf-referential — enables nested pages
titlestringRequired
iconstring | nullEmoji or icon identifier
contentjsonb | nullRich text as JSON (editor block format)
isFavoritebooleanDefault false
isDeletedbooleanSoft-delete flag; default false
coverColorstring | nullHex color for page cover
projectIdstring | nullFK → projects.id (set null on delete)
positioninteger | nullSort order (results ordered ASC by position)
GET /api/pages List all pages

Returns all pages for the user, ordered by position ASC. Nested hierarchy (parentId) must be reconstructed client-side.

POST /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
}
PUT /api/pages/:id Update a page

Partial update. updatedAt is auto-set. Use this to move pages (update parentId or position).

DELETE /api/pages/:id Delete a page

Hard-deletes the page and writes a tombstone to sync_deletions.

Notifications

Resource

Schema

Field Type Notes
idstringPrimary key
userIdstringAuto-set
titlestringRequired
messagestringRequired
isReadbooleanDefault false — use PUT to mark as read
createdAttimestampOrdered DESC in list
GET /api/notifications List notifications

Ordered by createdAt DESC.

PUT /api/notifications/:id Mark as read
{ "isRead": true }
DELETE /api/notifications/:id Delete a notification

Tombstoned in sync_deletions.

WatermelonDB Sync

Advanced

Powers 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.

GET /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

ParamTypeDescription
sincenumberUnix 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.

POST /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 }