Docs

Working with the API

Idempotency

Send a key with every submit. Replay it and you get the same job back, not a second charge — and the key names the job.

A network timeout does not tell you whether the request arrived. Retrying blindly is how you get charged twice for one image.

Send an Idempotency-Key and the retry is safe.

curl -X POST https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image \
  -H "Authorization: Bearer $AURAY_API_KEY" \
  -H "Idempotency-Key: 8f14e45f-ea8f-4d3c-9b21-7c3e0f5a1d92" \
  -H "Content-Type: application/json" \
  -d '{ "prompt": "…", "aspect_ratio": "1:1", "resolution": 1024 }'

The header and an idempotency_key field in the body are the same thing; send either. One to 64 characters of letters, digits, dot, dash or underscore. A UUID per submit is the simple choice.

#The key is the job's name

The submit answers request_id: "photo_8f14e45f-…" — the product, an underscore, and the key you sent. That is the id every other route takes: the status poll, the result, GET /v1/jobs/{id}, the assets, the cancel. There is no second id to store.

#What a replay does

Send the same key twice and the second request returns the same job, at whatever status it has now, with "replayed": true and nothing charged:

200 OK, the second time
{
  "status": "IN_QUEUE",
  "request_id": "photo_docs-mtpb1vnh",
  "job_id": "photo_docs-mtpb1vnh",
  "queue_position": 0,
  "logs": [],
  "metrics": {},
  "product": "photo",
  "provider_job_id": "f30c5f765770468097181c2bcb31fd4d",
  "model": "auray-ai/krea-2-turbo/text-to-image",
  "engine": null,
  "attribution": null,
  "credits_charged": 0,
  "credits_remaining": 38807,
  "expected_seconds": 39,
  "replayed": true,
  "status_url": "…", "response_url": "…", "cancel_url": "…"
}

That holds for the whole life of the job. A replay that arrives while the job is still waiting in the queue gets that waiting job — "status": "IN_QUEUE", its queue_position, "replayed": true — and the job keeps the body it was first sent with: whatever the replay carries is not read, so a different prompt under the same key never replaces the one that is queued, and the replay's credits_quoted and expected_seconds are that first body's — what the job will be charged when it starts, and how long it will take. Replaying after it has finished gives you the finished job — one that failed before it ever reached a model included, answered with its failure and nothing charged. And it is the job that answers, wherever the replay was sent: the same key sent to another address of the same product — two catalogue models, say — hands back the first job, naming the address it was submitted to and pointing its status_url, response_url and cancel_url there.

#A key belongs to the key that created it

An idempotency key is owned by the API key that first used it. If a different API key on the same account sends the same idempotency key, it is refused — whether that job has started or is still waiting in the queue, where it is charged to the key that queued it when it starts:

409 Conflict
{
  "error": "idempotency_key_owned",
  "error_type": "ConflictError",
  "retry": "new_key",
  "charged": "no",
  "docs": "https://auray.ai/docs/api/errors#idempotency_key_owned",
  "request_id": "54b4754a-3e17-4629-adb1-7d3f067e97d3"
}

Without that rule, two services that happen to generate the same identifier would silently collect each other's jobs. A 409 says the collision happened; a 200 would have hidden it.

Same key, same job, every time. Different key, refused. Two 409s are about the key rather than a job: in_flight while the first request with it is still being charged and handed to its model — poll the job, it is on its way — and key_settled for a key whose first request was refunded before any job was recorded under it, which nothing will ever answer: send a new key.

Generate keys per submit, not per user or per prompt

An idempotency key derived from something stable — a user id, a hash of the prompt — will collide the moment the same input is submitted twice on purpose, and the second one will hand back the first job instead of generating anything.

#When to send one

Always, on every submit. It costs one header.

It matters most where a retry is automatic and invisible: a job queue, a serverless function that may be replayed, an HTTP client with retries switched on. Those are exactly the places a duplicate charge appears without anyone deciding to retry.

#What it does not cover

Idempotency protects the submit, and the upload that requests somewhere to put a file. Reads, cancellations and asset fetches are already safe to repeat and take no key.

Start typing to search every page of the documentation.

Up arrow Down arrow to moveEnter to openEscape to close