API Reference · Projects
User Guide
Developer
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.
/projectsJWT BearerLists your projects, most recently updated first.
curl http://localhost/api/projects \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"{
"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
}/projectsJWT BearerCreates 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).
| Field | Type | Description |
|---|---|---|
| name* | string | 1–200 characters; trimmed. |
| templateId* | string | A catalog template id, e.g. mailer-box. |
| widthMm | number | 1–3000 mm — the canonical range the editor UI and share links also enforce. Same bounds for heightMm and depthMm. |
| heightMm | number | Millimetres; falls back to artwork dimensions, then template default. |
| depthMm | number | Millimetres; falls back to artwork dimensions, then template default. |
| artworkJson | object | An EditorState document (shape below), stored verbatim. |
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"}'{
"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"
}
}| Field | Type | Description |
|---|---|---|
| 400 | ApiError | “Unknown templateId '<id>'.” — or schema validation failure, including dimensions outside 1–3000 mm. |
| 401 | ApiError | Missing or expired JWT. |
| 403 | ApiError | “The free plan includes up to 3 projects. Upgrade to create more.” — the server-enforced free-tier cap. |
/projects/:idJWT Bearercurl http://localhost/api/projects/cml4kb3ef0002jq8f7d6e5c4b \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"{ "project": { "id": "cml4kb3ef0002jq8f7d6e5c4b", … } }| Field | Type | Description |
|---|---|---|
| 404 | ApiError | “Project not found.” — also the response for projects owned by another account. |
/projects/:idJWT BearerPartial update — send only the fields to change (same schema and bounds as create, all optional). An unknown templateId is rejected with 400.
curl -X PUT http://localhost/api/projects/cml4kb3ef0002jq8f7d6e5c4b \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{"name":"Spring mailer — final","depthMm":90}'{ "project": { "id": "cml4kb3ef0002jq8f7d6e5c4b", "depthMm": 90, … } }| Field | Type | Description |
|---|---|---|
| 404 | ApiError | “Project not found.” |
/projects/:idJWT BearerDeletes permanently. The response body is empty.
curl -X DELETE http://localhost/api/projects/cml4kb3ef0002jq8f7d6e5c4b \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
# → 204 No Content| Field | Type | Description |
|---|---|---|
| 404 | ApiError | “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:
| Field | Type | Description |
|---|---|---|
| templateId | string | Matches the project templateId. |
| dimensions | object | { widthMm, heightMm, depthMm } in millimetres. |
| material | string | A material id allowed by the template. |
| thicknessMm | number | Board thickness; the editor exposes 0.2–1 mm for boxes. |
| finish | string | matte · gloss · soft-touch · foil. |
| background | object | { kind: "color" | "gradient" | "image", value: string }. |
| lighting | string | studio · softbox · dramatic · outdoor. |
| artworks | array | ArtworkPlacement[] — { id, panelId, imageUrl, offsetX, offsetY, scale, rotationDeg } per piece. |