Spine-Zai API
Book-generation engine. Creates a "project" (book) from a premise, bootstraps a spine/outline, then generates chapters one at a time.
Version: Spine-Zai App 0.17.0
Access
| From | Base URL |
|---|---|
| The .21 host itself | http://127.0.0.1:9120 |
| The .22 server (forgeloops) | http://172.24.0.1:19120 (SSH tunnel) |
A container on offertest_web |
http://zaispine-proxy:80 |
| Public | https://zaispine.forgeloops.com (Traefik basic auth) |
The API runs as spine-zai-prod-api:8000 on 192.168.0.21, host-published on
port 9120. The books.forgeloops.com tool reaches it via zaispine-proxy.
Auth: dev bypass is on in prod (AUTH_DEV_BYPASS=true) — no bearer token
needed. The seed user 00000000-0000-0000-0000-000000000001 owns most projects.
Endpoints
Projects
| Method | Path | Notes |
|---|---|---|
| GET | /projects |
List all projects (books). |
| POST | /projects |
Create a project. |
| GET | /projects/{project_id} |
Project detail. |
| PUT | /projects/{project_id} |
Update a project. |
| DELETE | /projects/{project_id} |
Delete a project. |
Bootstrap & generation
| Method | Path | Notes |
|---|---|---|
| POST | /projects/{project_id}/bootstrap |
Build spine/outline from a premise. Streams SSE events. |
| POST | /projects/{project_id}/generate |
Generate the whole book synchronously over SSE (~20 min — blocks the connection). |
| POST | /projects/{project_id}/chapters/{chapter_index}/generate |
Generate one chapter. |
| POST | /projects/{project_id}/chapters/{chapter_index}/regenerate?cascade=<bool> |
Re-run a chapter. index=0 + cascade=true rebuilds the whole book. Returns 202 + a queued Generation. |
| GET | /projects/{project_id}/generations |
List generation jobs. |
| POST | /projects/{project_id}/generations |
Queue a generation job. |
| GET | /projects/{project_id}/generations/{generation_id} |
Job status. |
Content
| Method | Path | Notes |
|---|---|---|
| GET | /projects/{project_id}/chapters |
List chapters. |
| GET | /projects/{project_id}/chapter/{chapter_id} |
One chapter (prose + metadata). |
| GET | /projects/{project_id}/characters |
Character list. |
| GET | /projects/{project_id}/export |
Export the book. |
Cover
| Method | Path | Notes |
|---|---|---|
| POST | /projects/{project_id}/cover |
Upload a cover image. |
| GET | /projects/{project_id}/cover |
Fetch the cover image. |
| DELETE | /projects/{project_id}/cover |
Remove the cover. |
| POST | /projects/{project_id}/cover/generate |
Generate a cover. |
| GET | /projects/{project_id}/cover/prompt-preview |
Preview the cover prompt. |
| GET | /projects/{project_id}/cover/with_text |
Cover with title text rendered. |
System
| Method | Path | Notes |
|---|---|---|
| GET | /health |
Liveness check. |
| GET | /me |
Current (seed) user. |
| GET | /system/status |
DB health, worker activity, 24h job counts. |
| GET | /title-pool |
Candidate title pool. |
Request bodies
POST /projects — name is required.
{
"name": "motif-registry-verify",
"title": "Motif Registry Verify",
"subtitle": null,
"author_name": null,
"chapters_total": 10
}
POST /projects/{project_id}/bootstrap — all three fields required.
{
"premise": "Three estranged siblings empty their late mother's lake house...",
"genre": "Family drama, Literary fiction",
"chapter_count": 10
}
Streams Server-Sent Events; each carries a "type" (e.g. motif_registry,
error). A run with no "type": "error" event succeeded.
Object shapes
Project
{
"id": "d8f70bd0-486d-4165-acc5-77cda0f95a44",
"user_id": "00000000-0000-0000-0000-000000000001",
"name": "motif-registry-verify",
"title": "Motif Registry Verify",
"subtitle": null,
"state_version": "STATE_V2",
"chapters_total": 10,
"cover_image_mime": "image/png",
"created_at": "2026-05-18T01:06:44.291876Z",
"updated_at": "2026-05-18T01:17:23.669522Z"
}
Chapter
{
"id": "34e8327d-0263-4b5d-a148-a82519698aaf",
"project_id": "d8f70bd0-486d-4165-acc5-77cda0f95a44",
"chapter_index": 0,
"title": "Arrivals",
"summary": "Nora arrives first on Friday afternoon...",
"primary_beat": "setup",
"status": "done",
"attempts": 2
}
Notes
- The worker is single-threaded — one generation job at a time; a second queued
job for the same project returns
409. - A 10-chapter book takes ~18–25 min (Sonnet 4.6, ~2 min/chapter wall time).
- Transient Anthropic API 500s fail the whole job — there is no auto-retry;
re-trigger with
regenerate?cascade=trueto restart from chapter 0. - Full live spec:
GET /openapi.json· interactive docs:/docs.