# Asynchronous model endpoints and completion webhooks

Queue base URL: `https://api.revana.si/v1/queue`.

Every model returned by `GET https://api.revana.si/v1/models` has an endpoint at
`POST /v1/queue/revana/{model_id}`. Send the model input directly, without a `{model, input}` wrapper.
Use `Authorization: Key sr_live_…` or `Authorization: Bearer sr_live_…`.
Existing `/v1/generations` and `/v1/videos` continue to work unchanged.

```sh
curl 'https://api.revana.si/v1/queue/revana/seedance-2.5?fal_webhook=https%3A%2F%2Fyour-app.example%2Fcomplete' \
  -H "Authorization: Key $REVANA_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: my-unique-request' \
  -d '{"prompt":"A fox walks through snow","duration":5,"resolution":"720p","max_cost_usd":2}'
```

New requests return HTTP 202 with `request_id`, `gateway_request_id`, `status`, `status_url`, `response_url`,
and `cancel_url`. A matching Idempotency-Key replay returns HTTP 200 and the same request ID; changing the
input, endpoint or webhook with that key returns 409. Keys are scoped to your account. Persist your
Idempotency-Key before submitting and reuse it on network retries to avoid duplicate paid jobs.

Poll `status_url` using your key. Status is `IN_QUEUE`, `IN_PROGRESS` or `COMPLETED`. A failed request is also
`COMPLETED`, with `error` and `error_type`; its result endpoint returns 422. Jobs awaiting reconciliation stay
`IN_PROGRESS`. `?logs=1` returns an empty log list; internal provider logs are private.

GET `response_url` for the output: `{ "images": [{ "url", "content_type", "file_name", "file_size", ... }] }`
for images; `{ "video": { "url", ... } }` for video; `{ "audio": { "url", ... }, "audios": [...] }` for audio.
Before completion this returns 409 `not_ready`. **Download output URLs with your Revana API key**; they retain
account access controls. A URL can redirect to a short-lived storage link after authentication. Do not forward
your Authorization header to a different host when following that redirect.

## Completion webhooks

Optional `fal_webhook=<url>` queues a POST after success or final failure:

```json
{
  "request_id": "gen_…",
  "gateway_request_id": "gen_…",
  "status": "OK",
  "payload": { "images": [{ "url": "https://api.revana.si/v1/queue/revana/z-image/requests/gen_…/content" }] }
}
```

Failure uses `status: "ERROR"`, a safe `error` message and `payload: {"detail", "error_type"}`.
Return any 2xx promptly. Delivery is at least once: deduplicate on `request_id`. Failed deliveries retry up to
12 attempts over 24 hours, starting at 5 seconds and increasing to at most one hour between tries. Redirects
and private addresses are permanently refused. Use a public HTTPS endpoint on port 443; credentials in URLs,
fragments and IPv6 targets are unsupported. URLs must resolve to public IPv4 addresses on every attempt.

Webhooks require a customer account. If callback delivery is not available on a deployment, submission with
`fal_webhook` returns 503 before creating or charging a job; polling submissions remain available.
Callbacks are unsigned notifications: check the request ID against your own records and retrieve the result
with your own Revana key before trusting it. Do not use fal's JWKS to verify a Revana callback.

## Switching from fal

The queue lifecycle, `Authorization: Key`, raw input body and `fal_webhook` query follow fal's HTTP convention.
For these documented aliases and the supported input subset, keep the endpoint path and change the queue
base URL and API key:

| fal endpoint | Revana model | Supported common inputs |
| --- | --- | --- |
| `fal-ai/nano-banana-2` | `nano-banana-2` | prompt, resolution, aspect_ratio, seed, num_images: 1 |
| `fal-ai/nano-banana-pro` | `nano-banana-pro` | prompt, resolution, aspect_ratio, seed, num_images: 1 |
| `fal-ai/veo3.1/fast` | `veo-3.1-fast` | prompt, duration, resolution, aspect_ratio, generate_audio, negative_prompt, seed |

Availability and accepted option values still come from Revana's live model catalog. String durations such as
`"5"` and `"8s"` are accepted. `generate_audio` maps to `audio`. Nano aliases default to 1K; the Veo alias defaults
to 8 seconds, 720p, 16:9 and audio enabled. Only text-generation aliases listed above are provided; use canonical
`revana/{model_id}` endpoints and catalog input fields for editing or other models.

Unsupported fields return 422 before billing, including `output_format`, `sync_mode`, safety controls, search,
conversation/history and batch counts above one. Images have a placeholder empty `description`, not generated
text. Output files require authenticated download, unlike fal's publicly accessible media links. Seed behavior
and available values follow the selected Revana route. Cancellation returns 405 and never cancels billing;
SSE, synchronous fal.run(), fal storage uploads, fal signature verification and exact full-model schema parity
are not supported. Use polling (including SDK polling mode), not streamStatus().

The JavaScript fal SDK does not expose a simple `baseUrl` setting. Its request middleware can rewrite queue
URLs; use server-side code and keep your key off the browser. Configure credentials to your Revana key and
rewrite only `https://queue.fal.run/` to `https://api.revana.si/v1/queue/`. For other SDKs, use their supported
queue URL override or call the HTTP endpoints directly. Do not send your Revana key to fal's storage endpoints.

Revana extensions `max_cost_usd` (default cap $100, maximum $100) and `max_wait_s` may be included alongside model
input. The actual hold is the eligible routes' reserved price, not the cap. Existing credit, account and burst
limits apply. Queue errors include `detail` and `{ "error": { "code", "message" } }`.
