# AiuniVid Open API reference

Version: v1. This document is the compact, machine-readable companion to the interactive documentation. The OpenAPI contract is available at /openapi.yaml.

## Base URLs

- Hosted environment: `https://aiunivid.com/api/open`
- Local test environment (only when you run the server yourself): `http://localhost:3003/api/open`

Set `AIUNIVID_BASE_URL` to the environment you are integrating with. Do not use localhost URLs as media URLs or callback URLs: those URLs must be public HTTPS addresses.

## Upstream models

Requests run on BytePlus ModelArk directly, not through a reseller or a proxy of the
Dreamina web app. Each public model ID maps to a published ModelArk model, so the
capability limits below can be checked against BytePlus documentation:

| Public model ID | BytePlus ModelArk model |
| --- | --- |
| `seedance-2.5-{text,image,reference}-to-video` | `dreamina-seedance-2-5-260628` |
| `seedance-2.0-{text,image,reference}-to-video` | `dreamina-seedance-2-0-260128` |
| `seedance-2.0-fast-*` | `dreamina-seedance-2-0-fast-260128` |
| `seedance-2.0-mini-*` | `dreamina-seedance-2-0-mini-260615` |

## Authentication

Send `Authorization: Bearer $AIUNIVID_API_KEY` on every endpoint. Create an API key in Developer Center. The API key must stay on your server; never embed it in browser code.

## Scopes

Each API key is granted one or more scopes. Requests fail with `403 insufficient_scope` if the key lacks the required scope for an endpoint.

| Scope | Grants |
| --- | --- |
| `video:create` | Create and quote generation tasks. |
| `video:read` | Read tasks, models, and credit balance. |
| `files:write` | Request presigned upload URLs for reference media. |

## Rate limits

| Class | Endpoints | Limit |
| --- | --- | --- |
| Create endpoints | POST /v1/videos/generations, /quote | 10 requests / minute / key |
| Read endpoints | GET /v1/models, /v1/credits/balance, /v1/videos/generations, /v1/videos/generations/{id} | 60 requests / minute / key |

Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (Unix seconds) headers. Exceeding the limit returns `429 rate_limit_exceeded`.

## Workflow

1. Optionally call `POST /v1/videos/generations/quote` with the same body as the create request.
2. Call `POST /v1/videos/generations` with an `Idempotency-Key` header. The response is `202` and contains a task `id`.
3. Poll `GET /v1/videos/generations/{id}` until `status` is `succeeded` or `failed`. Alternatively pass `callback_url` when creating the task.
4. On `succeeded`, use `result.download_url`.

## List models

Endpoint: `GET /v1/models`

Returns public model IDs plus allowed `duration`, `quality`, `aspect_ratio`, and capability flags. Fast and Mini omit 1080p.

## Create a video task

Endpoint: `POST /v1/videos/generations`

Required headers:

- `Authorization: Bearer $AIUNIVID_API_KEY`
- `Idempotency-Key: <unique key>`
- `Content-Type: application/json`

Minimal text-to-video request:

```sh
export AIUNIVID_BASE_URL=https://aiunivid.com/api/open

curl --request POST \
  --url $AIUNIVID_BASE_URL/v1/videos/generations \
  --header "Authorization: Bearer $AIUNIVID_API_KEY" \
  --header "Idempotency-Key: $(uuidgen)" \
  --header "Content-Type: application/json" \
  --data '{
    "model": "seedance-2.5-text-to-video",
    "prompt": "A product floating in soft morning light, cinematic camera move",
    "duration": 5,
    "quality": "720p",
    "aspect_ratio": "16:9",
    "generate_audio": true
  }'
```

The response is `202 Accepted`:

```json
{
  "id": "vid_01JQ9X2B6XK9K4VQY2QZ4H6W3R",
  "object": "video.generation.task",
  "model": "seedance-2.5-text-to-video",
  "status": "queued",
  "progress": 0,
  "created": 1761313744,
  "task_info": { "can_cancel": false, "estimated_time": 165 },
  "usage": { "billing_rule": "per_second", "credits_reserved": 50 },
  "result": null,
  "error": null
}
```

## Create request fields

| Field | Type | Required | Rules |
| --- | --- | --- | --- |
| `model` | string | yes | One of the public model IDs below. |
| `prompt` | string | yes | 1 to 10,000 characters. In reference mode, use `@image1`, `@video1`, and `@audio1` to refer to array items by position. |
| `image_urls` | string[] | mode-dependent | Public HTTPS URLs. Text mode: forbidden. Image mode: exactly 1 or 2 items. Reference mode: up to 9 items for Seedance 2.0 (30 for Seedance 2.5); at least one of `image_urls` or `video_urls` is required unless using Seedance 2.5 audio-only. |
| `video_urls` | string[] | optional | Public HTTPS URLs. Reference mode only; up to 3 items for Seedance 2.0, 10 for Seedance 2.5. |
| `audio_urls` | string[] | optional | Public HTTPS URLs. Reference mode only; up to 3 items for Seedance 2.0, 10 for Seedance 2.5. Seedance 2.0 cannot use audio as the only reference input; Seedance 2.5 allows audio-only. |
| `duration` | integer | optional | Seedance 2.5: 4–30 seconds. Seedance 2.0 series: 4–15 seconds. Use `GET /v1/models` for the allowed values of the selected model. |
| `quality` | string | optional | Use the selected model capabilities returned by `GET /v1/models`. Seedance 2.5 supports up to 1080p (no 4K); Fast and Mini are limited to 720p. |
| `aspect_ratio` | string | optional | `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `21:9`, or `adaptive`; selected model capability still applies. |
| `generate_audio` | boolean | optional | Defaults to `true`. |
| `callback_url` | string | optional | Public HTTPS URL. Receives terminal task events. |
| `metadata` | object | optional | String values only; each value is at most 500 characters. Returned with the task object. |

## Public model IDs

| ID | Mode | Tier |
| --- | --- | --- |
| `seedance-2.5-text-to-video` | text-to-video | standard |
| `seedance-2.5-image-to-video` | image-to-video | standard |
| `seedance-2.5-reference-to-video` | reference-to-video | standard |
| `seedance-2.0-text-to-video` | text-to-video | standard |
| `seedance-2.0-image-to-video` | image-to-video | standard |
| `seedance-2.0-reference-to-video` | reference-to-video | standard |
| `seedance-2.0-fast-text-to-video` | text-to-video | fast |
| `seedance-2.0-fast-image-to-video` | image-to-video | fast |
| `seedance-2.0-fast-reference-to-video` | reference-to-video | fast |
| `seedance-2.0-mini-text-to-video` | text-to-video | mini |
| `seedance-2.0-mini-image-to-video` | image-to-video | mini |
| `seedance-2.0-mini-reference-to-video` | reference-to-video | mini |

## Task states and polling

Endpoint: `GET /v1/videos/generations/{id}`

Possible states: `queued`, `processing`, `succeeded`, and `failed`. Only `succeeded` and `failed` are terminal for new tasks. The task object contains `progress` (0–100), `usage`, `result`, `error`, `metadata`, and `request_id`. `task_info.can_cancel` is always `false`.

```sh
curl --request GET \
  --url "$AIUNIVID_BASE_URL/v1/videos/generations/$TASK_ID" \
  --header "Authorization: Bearer $AIUNIVID_API_KEY"
```

## List tasks

Endpoint: `GET /v1/videos/generations`

Query parameters: `limit` (1–100, default 20), `cursor` (opaque pagination token from the previous response), `status` (filter by `queued`, `processing`, `succeeded`, or `failed`).

```sh
curl --request GET \
  --url "$AIUNIVID_BASE_URL/v1/videos/generations?limit=20&status=succeeded" \
  --header "Authorization: Bearer $AIUNIVID_API_KEY"
```

Response: `{ "success": true, "data": { "tasks": [...], "next_cursor": "..." } }`. Pass `next_cursor` back as `cursor` to fetch the next page; `next_cursor` is `null` on the last page.

## Cancel a task

Endpoint: `POST /v1/videos/generations/{id}/cancel`

Cancellation is not supported. Submitted tasks run until they succeed or fail; failed tasks refund reserved credits. This endpoint returns `403 task_cancel_not_allowed`. `task_info.can_cancel` is always `false`.

## Credit balance

Endpoint: `GET /v1/credits/balance`

```json
{
  "success": true,
  "request_id": "req_xxx",
  "data": {
    "available_credits": 1250,
    "frozen_credits": 50
  }
}
```

`available_credits` can be spent immediately. `frozen_credits` is reserved by in-flight tasks (via quote/create holds) and will be released or settled as those tasks complete.

## Full endpoint index

| Method | Path | Purpose |
| --- | --- | --- |
| GET | `/v1/models` | List the current model capabilities. |
| GET | `/v1/credits/balance` | Get available and frozen credits. |
| POST | `/v1/videos/generations/quote` | Validate a generation request and quote credits without creating a task. |
| POST | `/v1/videos/generations` | Create an asynchronous video-generation task. |
| GET | `/v1/videos/generations/{id}` | Get task state, result, usage, and error. |
| GET | `/v1/videos/generations` | List your tasks, optionally filtered by status, with cursor pagination. |
| POST | `/v1/uploads/presign` | Get a temporary URL for reference-media upload. |

## Upload a reference file

1. Call `POST /v1/uploads/presign` with `filename`, `content_type`, and `purpose: "reference"`.
2. Upload bytes to the returned `data.upload_url` using the returned `data.headers`.
3. Put the returned `data.public_url` into `image_urls`, `video_urls`, or `audio_urls`.

## Webhooks

Pass `callback_url` when creating a task. AiuniVid POSTs the same task object as `GET /v1/videos/generations/{id}` when the task reaches a terminal state.

Headers:

- `X-AiuniVid-Event`: `video.completed` | `video.failed` | `video.cancelled`
- `X-AiuniVid-Signature`: `t=<unix_seconds>,v1=<hex>`

Signature:

```text
signed_payload = "{t}." + raw_body
v1 = hex(HMAC_SHA256(webhook_secret, signed_payload))
```

Reject events whose timestamp `t` is older than 5 minutes. Use `WEBHOOK_SIGNING_SECRET` (or `AI_CALLBACK_SECRET`) as the shared secret. Always read the raw request body before JSON parsing.

Failed deliveries are retried with exponential backoff (1s, 2s, 4s, 8s, …) for up to 24 hours. Return 2xx to acknowledge.

## Advanced path

`POST /v1/contents/generations/tasks` accepts BytePlus-style `content[]` and internal model IDs (`seedance-2.5`, `seedance-2.0`, `fast`, `mini`). New integrations should use `/v1/videos/generations`.

## Errors and retry behavior

| HTTP | error.code | Integrator action |
| --- | --- | --- |
| 400 | `invalid_request` | Correct the request body. |
| 400 | `unsupported_model` | Use one of the public model IDs listed above. |
| 401 | `invalid_api_key` | Use a valid, active API key. |
| 402 | `insufficient_credits` | Add credits before retrying. |
| 403 | `insufficient_scope` | Use a key with the required scope. |
| 403 | `task_cancel_not_allowed` | Cancellation is not supported; wait for the task to succeed or fail. |
| 404 | `task_not_found` | Task does not exist or does not belong to this API key. |
| 409 | `idempotency_conflict` | Reuse an idempotency key only with the identical request body. |
| 422 | `unsupported_capability` | Select fields supported by the model (duration, quality, media counts). |
| 422 | `unsafe_input` | Media or callback URL must be a public HTTPS address, not localhost or a private network. |
| 429 | `rate_limit_exceeded` | Wait for the rate-limit reset headers before retrying. |
| 429 | `too_many_concurrent_jobs` | Wait for an in-flight generation to finish, then retry. |
| 500 | `internal_error` | Retry with exponential backoff and the same idempotency key. |

## Sources of truth

- OpenAPI 3.1: /openapi.yaml
- Human documentation: /en/docs and /zh/docs
- Machine-readable Markdown: /api-reference.md
