Skip to content

Projects API

Reference for the /projects and /workspaces/:wid/projects route families. Source: services/api/src/routes/projects.ts and services/api/src/routes/projects/.

All endpoints require auth (Authorization: Bearer … or session cookie). All return JSON unless noted.

Project shape

{
  id: string;                 // uuid
  workspaceId: string;
  folderId: string | null;
  slug: string;               // url-safe; unique within workspace
  name: string;
  description: string | null;
  template: string | null;    // 'blank' | 'todo-app' | …
  visibility: 'private' | 'workspace' | 'public';
  thumbnailUrl: string | null;
  publishedAt: string | null;
  customDomain: string | null;
  createdAt: string;
  updatedAt: string;
}

Create

POST /workspaces/:wid/projects
Content-Type: application/json

{
  "name": "My new app",
  "template": "blank",            // optional, default 'blank'
  "folderId": null,               // optional
  "prompt": "A todo app with…"    // optional β€” kicks off an initial AI message
}

201 Created β†’ returns the project. Side effects:

  • Creates an empty project directory under PROJECTS_ROOT/<id>/.
  • Scaffolds files from the template registry (services/api/src/templates/registry.ts).
  • Runs pnpm install (sandboxed) and starts a Vite dev server on demand.
  • Inserts a row in projects and a default chat in chats.
  • If prompt is set, queues the first chat message asynchronously.

List

GET /projects?workspaceId=<wid>&folderId=<fid>&cursor=<c>&limit=20

Returns { items: Project[], nextCursor: string | null }.

Supported filters: workspaceId, folderId, visibility, q (full-text on name/description, uses pg_trgm).

Get

GET /projects/:id

Update

PATCH /projects/:id
Content-Type: application/json

{
  "name": "Renamed",
  "description": "New text",
  "folderId": "…",
  "visibility": "public"
}

Delete

DELETE /projects/:id

Soft-deletes by default. Pass ?hard=true (workspace owner only) to also remove files from disk.

Duplicate / fork

POST /projects/:id/duplicate
{ "workspaceId": "…", "name": "Copy of …" }

Copies files, version history, and chat seed.

Thumbnails

GET  /thumbnails/:projectId.png
POST /thumbnails/:projectId/regenerate

Thumbnails are rendered by Puppeteer in services/api/src/thumbnails/ and stored under services/api/thumbnails/.

Versioning

GET  /projects/:id/versions                  # List Git commits
POST /projects/:id/versions/:sha/checkout    # Roll back

Each project has its own bare Git repo under PROJECTS_ROOT/<id>/.git. See services/api/src/version-control/.

Publishing

POST /deploy/:projectId

Builds the project and writes to SITES_DIR/<slug>/. Caddy/nginx serves the site at <slug>.<DOABLE_DOMAIN>. See Custom Domains for the BYO domain flow.

Permissions

Action Required role
List/get Workspace member, or visibility = public
Create/update/delete Workspace member or higher
Hard-delete, transfer ownership Workspace owner
Publish to a custom domain Workspace admin+

The middleware that enforces this is in services/api/src/middleware/.

See also