Generation API
A generation job is a row in the generation_requests queue. You create it, a worker fulfills it, and you poll it for a result. This page is accurate to the shipping pipeline (lib/genrequests.php, worker/gen_worker.php).
X-API-Key) is PLANNED and not yet live. The request fields, status lifecycle, and result shape documented below are exactly what the pipeline stores and returns; the JSON bodies show that contract.
The job object
Every job is stored with these columns. A poll returns this shape.
| Field | Type | Description |
|---|---|---|
id | bigint | Job id. Use it to poll. |
user_id | bigint | Owner. Jobs are scoped to the user who created them. |
mode | string | text | image | video. (The mode enum today is these three; audio is planned.) |
prompt | string | The generation prompt. Trimmed and capped at 4000 characters. |
init_image | string|null | Docroot-relative path to an init / first-frame image. Only kept for image and video jobs. |
last_frame | string|null | Docroot-relative path to a last-frame image. Only kept for video jobs. |
model | string|null | Requested model id (see the catalog). Optional; the worker picks a default per mode when null. |
status | string | queued | running | done | failed. |
result | string|null | On success: the text reply (text mode) or a media URL (media mode). On failure: a short error message. |
error_code | string|null | Machine failure code when a job fails or is retrying. See Error codes. |
provider | string|null | Which provider handled it (e.g. runpod), set for async media jobs. |
external_job_id | string|null | The provider's job id for async media renders (what the worker polls). |
retries_left | int | Remaining automatic retries (starts at 3). |
next_retry_time | timestamp|null | When a retrying job becomes eligible to claim again. |
created_at | timestamp | Submit time. |
started_at | timestamp|null | When processing began. |
completed_at | timestamp|null | When it reached done or a permanent failed. |
updated_at | timestamp | Last change. |
Submitting a job
A submission carries the fields below. Validation: a job must carry at least a prompt or an init_image, else it is rejected. An unknown mode defaults to image. init_image is dropped on text jobs; last_frame is kept only on video jobs.
| Field | Type | Required | Notes |
|---|---|---|---|
mode | string | no | One of text, image, video. Defaults to image if missing/invalid. |
prompt | string | conditional | Required unless init_image is supplied. Trimmed, capped at 4000 chars. |
model | string | no | A model id from the catalog. Null lets the worker choose per mode. |
init_image | string | conditional | Relative path to an uploaded first-frame / from-image source. Image and video jobs only. |
last_frame | string | no | Relative path to an uploaded last-frame image. Video jobs only. |
Request body (the captured job payload)
{
"mode": "image",
"prompt": "a neon fox mascot, transparent background",
"model": "gpt-image-1.5",
"init_image": null,
"last_frame": null
}
Response (the stored job row)
{
"id": 5127,
"user_id": 42,
"mode": "image",
"prompt": "a neon fox mascot, transparent background",
"model": "gpt-image-1.5",
"init_image": null,
"last_frame": null,
"status": "queued",
"result": null,
"error_code": null,
"retries_left": 3,
"created_at": "2026-07-12T18:00:00Z",
"updated_at": "2026-07-12T18:00:00Z"
}
Polling a job
Read a single job by id, or list the newest jobs for a user (newest first, limit clamped to 1..100). Poll on a few-second interval until status is done or failed.
| Status | Display | Meaning |
|---|---|---|
queued | Queued | Captured, waiting for a worker. |
running | Processing | Claimed by a worker; running inline or awaiting an async provider render. |
done | Completed | result holds the output (text or media URL). |
failed | Failed | Permanent failure after retries. error_code + result explain why. |
Poll response - completed text job
{
"id": 5119,
"mode": "text",
"status": "done",
"result": "Here are three name ideas for your mascot: ...",
"error_code": null,
"completed_at": "2026-07-12T18:00:12Z"
}
Poll response - completed media job
{
"id": 5127,
"mode": "image",
"model": "gpt-image-1.5",
"status": "done",
"result": "/uploads/gen/gen_5127.png",
"provider": null,
"completed_at": "2026-07-12T18:00:40Z"
}
Result shape
- Text jobs:
resultis the model's reply text. - Media jobs (inline Azure gpt-image): the image is decoded and saved to
/uploads/gen/gen_<id>.png;resultis that docroot-relative URL. - Media jobs (async GPU provider): when the provider returns inline base64 (
image_base64,webm_base64,video_base64, ...), it is decoded to/uploads/gen/gen_<id>_<hash>.<ext>andresultis that URL. If the provider hosts the file,resultis the provider URL (looked up from keys likeurl,video_url,image_url,audio_url, ...). - Failed jobs:
resultis a short error message anderror_codeis the machine code.
Attachments (init image and last frame)
Image and video jobs may carry an init_image (from-image / first frame). Video jobs may additionally carry a last_frame. Both are stored as docroot-relative paths under /uploads/gen (a no-exec upload dir). A job needs a prompt or an init_image to be valid.
image or a video job; it does not change the job's mode.
Retries and timeouts
Failures are handled automatically. A retryable job is re-queued with tiered backoff by remaining retries: 3 -> 2 minutes, 2 -> 10 minutes, 1 -> 1 hour. Only bad_request is non-retryable. Jobs that sit in running past a per-mode wall-clock limit are reaped as timeout:
| Mode | Running limit |
|---|---|
text | 300 s (5 min) |
image | 900 s (15 min) |
video | 2400 s (40 min) |
Critical codes (auth, out_of_credits) also email the operator and are logged to the fleet error bar. See Error codes for the full taxonomy and resolutions.