Jobs and polling
The lifecycle of a generation, and how to wait for one politely.
A job moves through five states, three of them terminal:
queued → running → completed
↘ failed
(cancel) ↘ cancelledCreating
POST /v1/jobs charges credits and starts the generation.
The charge and the job row are written in one transaction — there is no
state in which credits are gone and no job exists.
Idempotency-Key is required on every create. Retry the request with the
same key and you get the same job back rather than a second charge; the
plugin retries on flaky editor networks for exactly this reason. Quote
before you commit with POST /v1/jobs/quote: same body,
answers the price, moves nothing.
Polling
GET /v1/jobs/{id} reads our own database and never calls
a provider, which is what makes polling cheap enough to be the primary mode
rather than a fallback. The response carries an X-Poll-After header —
wait that many seconds between asks. Poll until status is terminal.
In a browser there is a push alternative:
GET /v1/jobs/{id}/events streams the same job object
over server-sent events, one per change. It exists for the web frontend;
anything else — the coming Godot plugin and MCP server included — should
poll instead.
Failing and refunding
A generation that fails refunds itself. Every movement of credits — charges, purchases, refunds and grants alike — writes exactly one row to the ledger, so the account balance is always the sum of that list and never an independently maintained number that could drift.
Cancelling
POST /v1/jobs/{id}/cancel is accepted while a job is
queued or running. A job that already reached a terminal state answers
job_not_cancellable.
The files
A completed job carries its output as files, each with a role (which side
or frame it is), a url and a content_type. Files expire eventually —
files_expired on the job says when they have — but the job itself, its
input and its record in the ledger do not. The drawing you put in is kept
with the generation, so a second run at different settings never means
finding the file again.