Docs

Working with the API

Jobs

Two hosts, one job. Submit and follow it on the queue; read, cancel and download it on the platform.

A generation is a job. You submit it, it runs without you, and you collect it later.

#Two hosts, one job

The queue host follows the common IN_QUEUE / IN_PROGRESS / COMPLETED queue protocol, so a client written for it works unchanged (Compatibility). The platform host holds the account's view of the same job. They share every id.

HostDoes
POST /<address>queue.auray.runSubmits. Answers the queue envelope below.
GET /<address>/requests/{id}/statusqueue.auray.runWhere it is: IN_QUEUE, IN_PROGRESS, COMPLETED. Add ?logs=1 for progress lines.
GET /<address>/requests/{id}/status/streamqueue.auray.runThe same, as server-sent events until it completes. Each stream closes after a few minutes with no final frame; reconnect and keep reading, as the official clients do.
GET /<address>/requests/{id}queue.auray.runThe result once COMPLETED. Before then it answers 400 request_in_progress, as that protocol does.
PUT /<address>/requests/{id}/cancelqueue.auray.runAsks it to stop: 202 requested, 400 already completed, 404 unknown.
POST /<address>auray.runThe same submit, held open until the job settles or 300 seconds pass.
GET /v1/jobs/{id}api.auray.aiThe job as the account sees it: five statuses, settled, the charge.
GET /v1/jobs/{id}/assetsapi.auray.aiSigned download URLs. Metered by the megabyte.
DELETE /v1/jobs/{id}api.auray.aiCancel, and answer what the job became.
GET /v1/jobsapi.auray.aiThe account's jobs, newest first.

{id} is the request_id the submit answered: <product>_<your idempotency key>.

#Five statuses, three states

queued ──▶ running ──▶ succeeded
                   ├─▶ failed
                   └─▶ cancelled
StatusQueue stateMeans
queuedIN_QUEUEAccepted and waiting. Nothing has started.
runningIN_PROGRESSRunning now.
succeededCOMPLETEDFinished. assets holds the output.
failedCOMPLETEDStopped. error_code says why, and the charge is reversed.
cancelledCOMPLETEDYou stopped it. What hadn't been made yet is refunded.

Only queued and running are open. Once a job reaches any of the other three it never changes again, and settled: true on the platform read says the charge is final too.

A failed job is COMPLETED, which reads wrong and is right: it has finished queueing and finished running, and the failure travels in error rather than in the state. A client that loops until COMPLETED terminates on every outcome. A fourth FAILED state is exactly the change that would make such a client loop for ever.

#Submitting

curl -X POST https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image \
  -H "Authorization: Bearer $AURAY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "prompt": "…", "aspect_ratio": "16:9", "resolution": 1024 }'

You get 200 with the queue envelope straight away. The connection closing does not affect the job. Quickstart shows the whole body; the fields that matter here are request_id, status, queue_position (how many are ahead while the platform is full), credits_charged, credits_quoted, expected_seconds, and the three URLs. A job that waits in the queue is charged when it starts, not when it is submitted: until then its credits_charged is 0, and credits_quoted is what it will be charged.

Two options travel as query parameters on the submit: ?webhook=<url> (or ?fal_webhook=) asks for a signed delivery when the job settles, and ?wait=<seconds> holds the request open on the queue host for up to that long. The synchronous host does the second by default.

#Polling

curl "https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1" \
  -H "Authorization: Bearer $AURAY_API_KEY"
200 OK, while it runs
{
  "status": "IN_PROGRESS",
  "request_id": "photo_docs-mtpb1vnh",
  "job_id": "photo_docs-mtpb1vnh",
  "logs": [],
  "metrics": {},
  "status_url": "…", "response_url": "…", "cancel_url": "…"
}

metrics.inference_time appears once the runner has reported how long the work itself took. Video reports its phase as a log line while it runs.

The platform read is the same job from the account's side, and it tells you how long to wait:

curl https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh \
  -H "Authorization: Bearer $AURAY_API_KEY"
200 OK, settled
{
  "id": "photo_docs-mtpb1vnh",
  "job_id": "photo_docs-mtpb1vnh",
  "object": "job",
  "product": "photo",
  "model": "auray-ai/krea-2-turbo/text-to-image",
  "engine": "Krea 2 Turbo",
  "status": "succeeded",
  "settled": true,
  "provider_job_id": "f30c5f765770468097181c2bcb31fd4d",
  "credits_charged": 4,
  "credits_reserved": 4,
  "created_at": "2026-09-06T04:21:58.245391+00:00",
  "error": null,
  "error_code": null,
  "assets": [
    { "index": 0, "kind": "image", "key": "photo/krea-2-turbo/f30c5f765770468097181c2bcb31fd4d/0.png", "width": 1344, "height": 768, "bytes": 1065131 }
  ],
  "request_id": "fd85461a-706a-4c71-9d0d-5391319b442a"
}

While a job is open the read carries poll_after_seconds; honour it. Reads are rate-limited on their own bucket, and a 429 carries Retry-After. Both RateLimit-* and X-RateLimit-* headers say where you stand on every answer.

For anything slower than a few seconds, a webhook is better: you get told once, at the moment it settles, instead of asking repeatedly.

#Collecting the output

The job's assets name what was made. The signed URLs to download it come from one route, on the platform host, because signing them is metered against the egress bucket and a status poll is not. The bucket counts the signing, not the fetch: a signed link can be fetched again until it expires, and asking for the links again counts their size again:

curl https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets \
  -H "Authorization: Bearer $AURAY_API_KEY"

Each asset comes back with filename, content_type, bytes and a url that is good for 15 minutes; the answer's expires_at says when. Download what you intend to keep — treat the URL as a handle to fetch through, not as permanent storage. Fetching assets needs assets:read.

A job is kept 3 days. After that its request and its results are deleted, and both GET /v1/jobs/{id} and its assets answer 404 not_found — fetch what you need before then. In the minutes between the end of its 3 days and the sweep that deletes it, its assets answer 410 expired with reason: retention_ended instead: no link is signed past the life of the file it points at.

#Cancelling

curl -X DELETE https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh \
  -H "Authorization: Bearer $AURAY_API_KEY"

The answer says what the job became, whichever way it went:

200 OK — asked to cancel a job that had already finished
{
  "id": "photo_docs-mtpb1vnh",
  "object": "job",
  "product": "photo",
  "status": "succeeded",
  "settled": true,
  "cancelled": false,
  "credits_charged": 4,
  "credits_reserved": 4,
  "request_id": "9caaaf7e-d181-4892-90b1-6a7a74032d9a"
}

The queue host's PUT …/cancel answers the protocol's three: 202 with CANCELLATION_REQUESTED, 400 already_completed, or 404.

#Money

A refused job is never charged. Anything caught before inference — a bad scope, a malformed body, no credits, a ceiling reached, an address that is not open yet — costs nothing.

A job that fails after starting is refunded automatically. You do not file anything and there is nothing to reconcile.

Every case, product by product — read from the same declaration the refunds are tested against:

CaseImagesMusicVideo3DCatalogue models
Refused before it startsNothing is charged.Nothing is charged.Nothing is charged.Nothing is charged.Nothing is charged.
Waiting for a slotNothing is charged until it is dispatched.Nothing is charged until it is dispatched.Nothing is charged until it is dispatched.Nothing is charged until it is dispatched.No queue: it runs or is refused.
FailedEverything comes back.Everything comes back.Everything comes back.Everything comes back.Everything comes back.
Partly withheld or shortWhat did not arrive comes back; if nothing arrived, everything does.What did not arrive comes back; if nothing arrived, everything does.What did not arrive comes back; if nothing arrived, everything does.What did not arrive comes back; if nothing arrived, everything does.What did not arrive comes back; if nothing arrived, everything does.
Stopped before it beganEverything comes back.Everything comes back.Everything comes back.Everything comes back until the service starts it; from then — which can be while it still reads queued — the first stage's start-up share is kept.A job cannot be stopped.
Stopped while runningEverything comes back.The base price is kept, and so is what already ran, rounded down; the rest comes back.What already ran is kept, rounded down; the rest comes back.What already ran is kept, rounded down; the rest comes back.A job cannot be stopped; it finishes and is charged for what it delivers.
Stopped respondingClosed 30 minutes after it started, and everything comes back.Closed 2 hours after it started, and everything comes back.Closed 210 minutes after it started, and everything comes back.Closed 2 hours after it started, and everything comes back.Closed 2 hours after it started, and everything comes back.

So the only credits you keep paying for are the ones that produced something.

#Next

Start typing to search every page of the documentation.

Up arrow Down arrow to moveEnter to openEscape to close