Why Async
A generation job runs AI models, writes code, and saves files — far beyond any reasonable HTTP timeout. Making the job a first-class resource means:- Your client gets an immediate answer (
202) and a durablejob_id. - The job survives client disconnects, retries, and app restarts — you can always come back and ask for its status.
- The API stays fast and cheap: requests do validation and bookkeeping, the heavy lifting happens in a managed runner.
The Flow
- Create —
POST /v1/jobsvalidates the request, runs the prompt through a server-side AI evaluation (rejecting unusable prompts with400 invalid_prompt, choosing the model tier), creates the job row withstatus: queued, and dispatches the work to the runner. The response returns immediately with the job snapshot. - Execute — the managed runner performs the generation with no HTTP timeout constraints. While it works, the job moves to
running. - Finalize — when done, the runner finalizes the job row with the outcome. Jobs already in a terminal state cannot be finalized again (
409 job_terminal). - Observe — your client polls
GET /v1/jobs/{jobId}until the status is terminal.
States
Terminal states are final: a job never leaves
succeeded, failed, or canceled.
Job Shape and Result
result contains project_id (the numeric id of the project the generation wrote to) and files_saved (how many files the generation produced). On failure, the error object follows the standard {code, message, details?} shape.
Note the two ids: the job id is a UUID returned by POST /v1/jobs; the project id is a numeric int64 that you pass in and read back.
Polling
PollGET /v1/jobs/{jobId} with exponential backoff, starting at 2 seconds and capping at 30 seconds:
- A generation usually takes minutes — polling faster than 2s wastes quota.
- Stop polling on any terminal status, and back off further on
429(honor theRetry-Afterheader). - Treat
404 job_not_foundas fatal for that id: the job doesn’t exist or isn’t visible to your token.
Realtime delivery (SSE / realtime subscriptions) may be offered in a future version as an alternative to polling, without changing this contract.
Retries and Idempotency
There is no idempotency key in v0.1: everyPOST /v1/jobs creates a new job. Practical guidance:
- Persist the
job_idas soon as you receive the202. If your client crashes mid-flow, resume by polling that id instead of resubmitting. - If
POST /v1/jobsitself fails with502 job_create_failedor a network error before you have a job id, it is safe to retry the POST — the job may or may not have been created, but creating a duplicate is recoverable (an extra generation on the same project). - Never retry
400 invalid_promptor429 rate_limitedblindly: fix the prompt, or wait for theRetry-Afterwindow. - Polling is idempotent by definition —
GETas often as your backoff schedule allows.
Current Limits (Early Access)
- Job types: only
type: "prompt"is supported in v0.1. The catalog grows with the runner. - Existing projects only:
project_idis effectively required today. Creating a brand-new project from scratch via the API (first generation) is not yet supported — create the project in the app, then iterate on it through the API. - Rate limits are being tuned during early access; quota snapshots appear in
whoamiwhen available, and over-quota requests return429withRetry-After.
Next Steps
Examples
A complete happy-path workflow, step by step.
Authentication
Get a token via OAuth 2.1 + PKCE.

