A storyboard is a card of visual tiles the Creature Console can tap to do things. The server is a dumb persistence layer — it stamps id and timestamps, stores the document, broadcasts a cache-invalidation, and does not interpret tiles[].action. The client owns the action type vocabulary and the server preserves unknown shapes verbatim so old and new clients can interoperate as the vocabulary grows.
GET /api/v1/storyboard — List all storyboards (newest first by updated_at). Returns {count, items: [...]}.
GET /api/v1/storyboard/{id} — Fetch one storyboard by its UUID.
POST /api/v1/storyboard — Create a new storyboard. Server generates the UUID and stamps created_at / updated_at. Any id / created_at / updated_at the client sends in the body is silently ignored.
{
"title": "Halloween Front Porch",
"notes": "Beaky greets; Mango heckles.",
"tiles": [
{
"id": "uuid",
"x": 0.06, "y": 0.08, "width": 0.26, "height": 0.20,
"label": "Greet",
"sf_symbol": "hand.wave.fill",
"tint_color_hex": "#34C759",
"action": { "type": "ad_hoc_speech", "creature_id": "uuid", "resume_playlist": true }
}
]
}
PUT /api/v1/storyboard/{id} — Replace an existing storyboard. created_at is preserved; updated_at bumps to now. 404 if no storyboard exists at that id.
DELETE /api/v1/storyboard/{id} — Delete a storyboard.
Caps: title ≤ 256 chars, notes ≤ 16384 chars, ≤ 200 tiles, tile label ≤ 256 chars, tile id must be UUID-shaped. Tile action (when present) must be a JSON object — the server checks that and nothing else inside, on purpose.