> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aurous-labs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Differences from the native API

> The honest delta list: what the Aurous raw surface changes vs. the native Seedance video API.

The raw surface is a faithful passthrough — request and response bodies are shape-identical (with task identifiers Aurous-native), and the Ark SDK works by swapping `base_url` + key. But a passthrough that runs on a multi-tenant platform has a handful of documented differences. Here is the complete list, so nothing surprises you in production.

## Model identifiers

You address models by **model id or alias** (`seedance-2.0`, `seedance-2.0-fast`, `seedance-2.0-mini`, or the full provider slug). Endpoint-style ids are **not** accepted — passing one, or any unknown model, returns **`404`** [`model_not_found`](/errors#model_not_found). See [Create a task → model](/api-reference/seedance/create-task#model).

## Rate & concurrency limits

Limits are **per team**, sized to protect shared capacity:

* **Request rate:** roughly **30 requests / minute sustained** across `POST` and `DELETE` (a burst bucket sits above that). Over the limit → [`too_many_requests`](/errors#too_many_requests) (429).
* **Active-task cap:** at most **5 concurrent non-terminal raw tasks** (queued or running) per team. Over the cap → [`concurrency_limit_exceeded`](/errors#concurrency_limit_exceeded) (429).

The request-rate 429 (`too_many_requests`) and any `503` carry a **`Retry-After`** header — sleep that many seconds and retry. The active-task 429 (`concurrency_limit_exceeded`) is **count-based**, so it carries **no** `Retry-After`: retry once an in-flight task reaches a terminal state, or cancel a queued/pending task to free a slot. See [Rate limits](/rate-limits) for headers and how buckets refill. Need a higher tier? Email **[support@aurous-labs.com](mailto:support@aurous-labs.com)**.

## Callbacks

Your `callback_url` is **accepted and relayed through Aurous** — you do not have to poll. The relayed callback differs from a bare provider callback in a few honest ways:

* **Body shape.** Aurous POSTs a JSON object `{ "event", "data" }`, where `event` is `video.completed`, `video.failed`, or `video.cancelled`, and `data` is the normalized task object — the same shape [Retrieve a task](/api-reference/seedance/retrieve-task#fields) returns. The task id is `data.id` (the Aurous `vid_…` value); on **this unsigned relay** there is **no** separate `task_id` field — the id already *is* the `vid_…` handle (the signed-webhook path is different — see [Signed webhooks for raw tasks](#signed-webhooks-for-raw-tasks)). A task that reaches the terminal `expired` status arrives as a **`video.failed`** callback (there is no `video.expired` event) — the provider status is preserved verbatim on `data.status`, so read that (or the authoritative retrieve) to tell `expired` apart from a plain `failed`. Both charge **0**.

  ```json theme={null}
  {
    "event": "video.completed",
    "data": { "id": "vid_01J9Z3K7Q0XY2ABCDEFGHJKMNP", "status": "succeeded", "content": { "video_url": "https://api.aurous-labs.com/v1/contents/generations/tasks/vid_01J9Z3K7Q0XY2ABCDEFGHJKMNP/output?token=…" }, "…": "…" }
  }
  ```

* **Unsigned.** The relayed POST carries only `Content-Type: application/json` — there is **no** `Aurous-Webhook-Signature` and no other `Aurous-*` headers. This is **not** the signed [registered-webhook-endpoints](/webhooks) envelope, so do not attempt signature verification on it.

* **At-least-once, best-effort.** Delivery is attempted up to 3 times (5-second timeout per attempt), and the upstream may itself re-post — so you can receive **duplicates**. **De-duplicate on `data.id`.** Aurous **never bills from the posted body**; the charge always settles from a trusted server-side read-back.

<Warning>
  Because the callback is unsigned and arrives over the public internet, treat it
  as a **hint to fetch authoritative status**, not as trusted data. On each
  callback, read the record with
  [`GET /v1/contents/generations/tasks/{id}`](/api-reference/seedance/retrieve-task)
  — that `GET` is the source of truth for status and result.
</Warning>

## Signed webhooks for raw tasks

Raw tasks **also** fire your signed [registered webhook endpoints](/webhooks) (created with `POST /v1/webhook_endpoints`) — separately from, and in addition to, the inline `callback_url` relay above. The events are `video.completed`, `video.failed`, and `video.cancelled`, each carrying the `Aurous-Webhook-Signature` header and verified exactly like every other Aurous webhook (see [Webhooks](/webhooks) for the signature mechanics — they are identical for raw and platform events).

Correlate terminal webhooks via the **`data.id`** field (a `vid_…` task identifier).

## Safety identifier

Your `safety_identifier` is **namespaced per team** before it reaches the provider — this keeps per-end-user abuse attribution scoped to your team and prevents collisions across tenants. It is **echoed back unchanged** on every retrieve and list item, so you always read your own original value. See [Create a task → parameters](/api-reference/seedance/create-task#parameters).

## List & retention supersets

* **No 7-day list cutoff.** The native list only returns the last 7 days; the Aurous [list](/api-reference/seedance/list-tasks) has no such window and is served from your own team-scoped records (never proxied).
* **Records outlive the provider purge.** A task stays retrievable on Aurous **past the provider's 7-day history purge**. The catch: `video_url` / `last_frame_url` inside a record still **expire 24 hours after generation** (a provider constraint), so an old record remains readable but its media links go stale. Download what you keep.
* **Output URLs are Aurous-proxied.** `video_url` and `last_frame_url` point at Aurous streaming endpoints (`…/v1/contents/generations/tasks/{id}/output[/last_frame]?token=…`), not an upstream object store — the bytes stream through Aurous, so the underlying storage host is never exposed. They stay valid \~24 h after generation.

## Idempotency

The raw `POST` accepts an **`Idempotency-Key`** header (a bonus over the native API) — reuse the same key to make a create safe to retry. A replay returns the original result and carries the **same `Aurous-Credits-Held` header**, so you are never double-held or double-charged. A same-key retry with a different body returns [`idempotency_key_in_use`](/errors#idempotency_key_in_use) (409). See [Idempotency](/idempotency).

## Error envelope

Aurous checks some conditions **before** your request reaches the provider (auth, scope, balance, rate, request shape, model resolution). Those are returned with **Aurous snake\_case codes** inside the standard envelope:

```json theme={null}
{
  "error": {
    "type": "invalid_request",
    "code": "balance_too_low",
    "message": "…",
    "param": null,
    "doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
    "request_id": "req_…"
  }
}
```

This is a **strict superset** of the provider's `{ "error": { "code", "message" } }`: same two keys, plus `type`, `param`, `doc_url`, and `request_id`.

<Warning>
  If your integration switches on the provider's **PascalCase** codes
  (`InvalidParameter`, `MissingParameter`, …), you will meet these **snake\_case**
  codes **first** — they fire before the provider is called. Match on
  `error.code` and handle both vocabularies.
</Warning>

The three error classes in full:

1. **Aurous pre-dispatch gates** — snake\_case codes, superset envelope (the shape above).
2. **Provider request-faults** — things only the provider can catch (`InvalidParameter`, `MissingParameter`, the `*SensitiveContentDetected` family). Returned with the **provider's exact status and body**. This is the raw contract.
3. **Account / infrastructure faults** — remapped to [`provider_unavailable`](/errors#provider_unavailable) (503, `Retry-After: 30`) or, for unrecognized upstream errors, [`provider_unknown_error`](/errors#provider_unknown_error) (502).

See [Create a task → errors](/api-reference/seedance/create-task#errors) for the per-code table and the [Errors](/errors) catalog for everything else.

## Reference-file budget

A multimodal-reference request accepts **up to 12 reference files**: **9 images** + **3 videos** (each 2–15 s, ≤15 s total) + up to **3 audio** clips (≤15 s total, never alone). Details in [Create a task → generation modes](/api-reference/seedance/create-task#generation-modes).
