API Reference · Projects
API Reference

Projects: editor documents, stored

A project is a named editor document: template, dimensions in mm, and the full editor state as artworkJson. Every route requires a JWT and is scoped to the caller — other accounts' projects are indistinguishable from missing ones.

GET/projectsJWT Bearer

Lists your projects, most recently updated first.

curl
curl http://localhost/api/projects \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
200 OK
{
  "projects": [
    {
      "id": "cml4kb3ef0002jq8f7d6e5c4b",
      "userId": "cml4k9x2b0000jq8f3h5a1d2e",
      "name": "Spring mailer — 230×160×70",
      "templateId": "mailer-box",
      "widthMm": 230,
      "heightMm": 160,
      "depthMm": 70,
      "artworkJson": {
        "templateId": "mailer-box",
        "dimensions": { "widthMm": 230, "heightMm": 160, "depthMm": 70 },
        "material": "kraft",
        "thicknessMm": 0.5,
        "finish": "matte",
        "background": { "kind": "color", "value": "#0b1020" },
        "lighting": "studio",
        "artworks": []
      },
      "createdAt": "2026-08-06T10:20:11.000Z",
      "updatedAt": "2026-08-06T10:24:48.000Z"
    }
  ],
  "total": 1
}
POST/projectsJWT Bearer

Creates a project. templateId must exist in the catalog. Dimensions resolve in this order: explicit body values → artworkJson.dimensions → template defaults. If artworkJson is omitted, a default editor state is synthesized from the template (first material and finish, 3 mm thickness, studio lighting, no artwork).

FieldTypeDescription
name*string1–200 characters; trimmed.
templateId*stringA catalog template id, e.g. mailer-box.
widthMmnumber1–3000 mm — the canonical range the editor UI and share links also enforce. Same bounds for heightMm and depthMm.
heightMmnumberMillimetres; falls back to artwork dimensions, then template default.
depthMmnumberMillimetres; falls back to artwork dimensions, then template default.
artworkJsonobjectAn EditorState document (shape below), stored verbatim.
curl
curl -X POST http://localhost/api/projects \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
  -H "Content-Type: application/json" \
  -d '{"name":"Spring mailer — 230×160×70","templateId":"mailer-box"}'
201 Created
{
  "project": {
    "id": "cml4kb3ef0002jq8f7d6e5c4b",
    "userId": "cml4k9x2b0000jq8f3h5a1d2e",
    "name": "Spring mailer — 230×160×70",
    "templateId": "mailer-box",
    "widthMm": 230,
    "heightMm": 160,
    "depthMm": 70,
    "artworkJson": { "templateId": "mailer-box", "material": "cardboard", "thicknessMm": 3, … },
    "createdAt": "2026-08-06T10:20:11.000Z",
    "updatedAt": "2026-08-06T10:20:11.000Z"
  }
}
FieldTypeDescription
400ApiError“Unknown templateId '<id>'.” — or schema validation failure, including dimensions outside 1–3000 mm.
401ApiErrorMissing or expired JWT.
403ApiError“The free plan includes up to 3 projects. Upgrade to create more.” — the server-enforced free-tier cap.
GET/projects/:idJWT Bearer
curl
curl http://localhost/api/projects/cml4kb3ef0002jq8f7d6e5c4b \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
200 OK
{ "project": { "id": "cml4kb3ef0002jq8f7d6e5c4b", … } }
FieldTypeDescription
404ApiError“Project not found.” — also the response for projects owned by another account.
PUT/projects/:idJWT Bearer

Partial update — send only the fields to change (same schema and bounds as create, all optional). An unknown templateId is rejected with 400.

curl
curl -X PUT http://localhost/api/projects/cml4kb3ef0002jq8f7d6e5c4b \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
  -H "Content-Type: application/json" \
  -d '{"name":"Spring mailer — final","depthMm":90}'
200 OK
{ "project": { "id": "cml4kb3ef0002jq8f7d6e5c4b", "depthMm": 90, … } }
FieldTypeDescription
404ApiError“Project not found.”
DELETE/projects/:idJWT Bearer

Deletes permanently. The response body is empty.

curl
curl -X DELETE http://localhost/api/projects/cml4kb3ef0002jq8f7d6e5c4b \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
# → 204 No Content
FieldTypeDescription
404ApiError“Project not found.”

The stored EditorState shape

artworkJson is an EditorState — the exact document the editor serializes. The api stores it verbatim; the shape is:

FieldTypeDescription
templateIdstringMatches the project templateId.
dimensionsobject{ widthMm, heightMm, depthMm } in millimetres.
materialstringA material id allowed by the template.
thicknessMmnumberBoard thickness; the editor exposes 0.2–1 mm for boxes.
finishstringmatte · gloss · soft-touch · foil.
backgroundobject{ kind: "color" | "gradient" | "image", value: string }.
lightingstringstudio · softbox · dramatic · outdoor.
artworksarrayArtworkPlacement[] — { id, panelId, imageUrl, offsetX, offsetY, scale, rotationDeg } per piece.

Continue to asset endpoints