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).

What is implemented today. Jobs are created for a signed-in user on flopco.in and captured into the queue. A public, key-authenticated HTTP endpoint (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.

FieldTypeDescription
idbigintJob id. Use it to poll.
user_idbigintOwner. Jobs are scoped to the user who created them.
modestringtext | image | video. (The mode enum today is these three; audio is planned.)
promptstringThe generation prompt. Trimmed and capped at 4000 characters.
init_imagestring|nullDocroot-relative path to an init / first-frame image. Only kept for image and video jobs.
last_framestring|nullDocroot-relative path to a last-frame image. Only kept for video jobs.
modelstring|nullRequested model id (see the catalog). Optional; the worker picks a default per mode when null.
statusstringqueued | running | done | failed.
resultstring|nullOn success: the text reply (text mode) or a media URL (media mode). On failure: a short error message.
error_codestring|nullMachine failure code when a job fails or is retrying. See Error codes.
providerstring|nullWhich provider handled it (e.g. runpod), set for async media jobs.
external_job_idstring|nullThe provider's job id for async media renders (what the worker polls).
retries_leftintRemaining automatic retries (starts at 3).
next_retry_timetimestamp|nullWhen a retrying job becomes eligible to claim again.
created_attimestampSubmit time.
started_attimestamp|nullWhen processing began.
completed_attimestamp|nullWhen it reached done or a permanent failed.
updated_attimestampLast 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.

FieldTypeRequiredNotes
modestringnoOne of text, image, video. Defaults to image if missing/invalid.
promptstringconditionalRequired unless init_image is supplied. Trimmed, capped at 4000 chars.
modelstringnoA model id from the catalog. Null lets the worker choose per mode.
init_imagestringconditionalRelative path to an uploaded first-frame / from-image source. Image and video jobs only.
last_framestringnoRelative 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.

StatusDisplayMeaning
queuedQueuedCaptured, waiting for a worker.
runningProcessingClaimed by a worker; running inline or awaiting an async provider render.
doneCompletedresult holds the output (text or media URL).
failedFailedPermanent 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: result is the model's reply text.
  • Media jobs (inline Azure gpt-image): the image is decoded and saved to /uploads/gen/gen_<id>.png; result is 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> and result is that URL. If the provider hosts the file, result is the provider URL (looked up from keys like url, video_url, image_url, audio_url, ...).
  • Failed jobs: result is a short error message and error_code is 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.

from-image is a source flag, not a mode. An init image attaches to an 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:

ModeRunning limit
text300 s (5 min)
image900 s (15 min)
video2400 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.