# SuperSeedance API

Updated: 2026-10-20. This document describes the implemented application API. Check `GET /api/studio/config` before submitting work: generation, analysis and paid checkout have separate availability. Seedance 2.5 has a fixed studio price for the supported parameters below; this does not mean every parameter combination has been individually generated in a live test. Unavailable services return an error; the generation API never substitutes a reference clip for a result.

## Authentication and request rules

- Browser users register and log in with the account forms. The server sets an HttpOnly session cookie. Browser mutations require the exact site `Origin` and same-origin requests.
- Server integrations use `Authorization: Bearer YOUR_STUDIO_API_KEY`. Create a key in the verified account's settings. Send it from your backend, with **no `Origin`, `Cookie` or `Sec-Fetch-Site` header**. Browser bearer requests are rejected. Never embed a key in frontend JavaScript or a public URL.
- Account and key management require a browser session. API keys authorize only their owner’s permitted resources; they do not bypass email verification or credit checks.
- JSON mutations use `Content-Type: application/json`; uploads use `multipart/form-data` with a `file` field. Production requires an HTTPS site URL and correctly configured reverse-proxy trust.
- Normal responses are JSON. Errors have `{ "error": "Human-readable message", "message": "Human-readable message", "code": "machine_code" }`. Codes are case-sensitive and may be upper- or lowercase. File endpoints return media bytes.

## Public service status

### GET /api/studio/config

Returns `generation`, `analysis`, `payments`, `storage` and `email`. `generation` includes `configured`, `pricingConfirmed`, `workerReady`, `message`, and `models` with configured settings, `available` and `reason`.

`generation.fullSpecModels` lists explicitly enabled models whose supported parameter combinations are open. For these models, the `profiles` array is a set of example presets, not an exhaustive whitelist. Other models still use the exact profiles whitelist when configured. Neither field bypasses model availability, verified-account, credit-balance or worker checks. Invalid server configuration fails closed.

`generation.configured` is true only when the video credentials, confirmed rate card and a healthy worker are present. It reports configuration readiness; it does not prove provider account entitlement, output quality or successful live generation. Read both the top-level readiness and the selected model's `available` value.

`analysis` reports its own `configured`, `credits`, `message` and browser sampling limits. Analysis needs an explicitly configured vision model and a confirmed rate card; it does not depend on the generation worker. `payments` reports public credit packs and whether they are illustrative or in test mode.

Current storage is private local storage on the application server, not browser-only storage and not an object-storage service. Files require authenticated owner access.

### POST /api/quote

Public; no account or API key required. It validates the settings and returns the site credit calculation without reserving credits.

Default-settings request:

```json
{
  "model": "seedance-2.5",
  "mode": "text",
  "duration": 4,
  "resolution": "480p",
  "aspectRatio": "16:9",
  "nativeAudio": false,
  "draft": false
}
```

When the Seedance 2.5 rate card and service are enabled, the response is:

```json
{"credits":67,"usdEstimate":0.67,"rateCard":"seedance-2.5-v1","mode":"estimate","confirmed":true,"generationAvailable":true,"reason":""}
```

`usdEstimate` converts studio credits at US$0.01 per credit, before applicable taxes. It is neither a supplier invoice nor a promise that checkout is currently open. The actual payment amount is the configured pack's price. `confirmed` concerns the studio selling price; `generationAvailable` concerns the selected parameters and runtime readiness. A valid quote does not bypass authentication or balance checks.

Seedance 2.5 selling prices use `ceil(83 × duration/5 × outputPixels/(854×480) × resolutionRate/70)`, where resolutionRate is 70 for 480p/720p and 77 for 1080p. Pixel dimensions follow the official resolution/aspect table. Adaptive aspect uses the largest listed canvas in its resolution tier to set a fixed price before submission; it is not an exact provider-usage prediction. Audio on/off and 480p drafts use the same studio price. The original 5s/480p/16:9 silent non-draft text profile remains 83 credits and retains its `launch` rate-card label. Other supported 2.5 combinations use `seedance-2.5-v1`. Other models remain `illustrative` until separately priced and enabled.

| 16:9 duration | 480p credits | 720p credits | 1080p credits |
|---|---:|---:|---:|
| 5 seconds | 83 | 187 | 462 |
| 10 seconds | 166 | 374 | 924 |
| 30 seconds | 498 | 1120 | 2772 |

Supported Seedance 2.5 combinations follow the [official creation API](https://docs.volcengine.com/docs/ark/create-video-generation-task-api?lang=zh), checked on 2026-10-20: durations 4–30 integer seconds; 480p/720p/1080p; audio on/off; drafts at 480p only. `mode: "image"` requires `aspectRatio: "adaptive"` to match the starting image. Text and single-image reference modes accept 16:9, 9:16, 1:1, 4:3, 3:4, 21:9 or adaptive. Unsupported combinations are rejected before credits are reserved. This application does not expose automatic duration, frame counts, 4K for 2.5, video/audio reference uploads, multi-image references, first/last-frame pairs or draft-upgrade tasks. 1080p output may use HEVC; download it to a compatible player if browser playback is unavailable.

## Private assets

| Method and endpoint | Result |
|---|---|
| `GET /api/assets` | `{assets: [{id, name, type, size, createdAt, url}]}` |
| `POST /api/assets` | Multipart `file`; returns `201 {asset}` |
| `GET /api/assets/{id}` | Private image bytes; owner authentication required |
| `HEAD /api/assets/{id}` | File headers |
| `DELETE /api/assets/{id}` | `{deleted:true}`; active tasks using the asset block deletion |

Upload JPG, PNG or WebP up to 10 MB. Width and height must each be 300–6000 pixels and width/height must be 0.4–2.5. The server decodes and normalizes the image; returned metadata describes the stored asset. The account limit is 100 images and 100 MB total. Supply only content you may process.

Server integration example, using an environment-held key:

```sh
curl "$STUDIO_BASE_URL/api/assets" \
  -H "Authorization: Bearer $STUDIO_API_KEY" \
  -F 'file=@product.jpg'
```

Keep the returned `asset.id`. Image/reference generation uses this server-owned asset, not a browser data URL, local path, remote URL or metadata-only placeholder.

## Generation jobs

### POST /api/generate

Requires a verified account, sufficient credits, an available provider configuration and a running worker. Returns `202 {job}` after the local task and credit reservation have been committed. It does not return a generated video immediately.

Default-settings text request:

```json
{
  "model": "seedance-2.5",
  "mode": "text",
  "duration": 4,
  "resolution": "480p",
  "aspectRatio": "16:9",
  "nativeAudio": false,
  "draft": false,
  "locale": "en",
  "prompt": "A ceramic cup in morning light, slow camera push-in.",
  "idempotencyKey": "6b87b020-5126-4cd4-a39a-42036f58c244"
}
```

The key above is an example: generate a fresh UUID for each intentional new task. Keep the same key and identical request body when retrying an uncertain network response. Keys accept 16–128 letters, digits, underscores or hyphens. Reusing one with a different request returns `409 idempotency_conflict`; repeating the same task does not reserve credits twice.

For image-to-video set `"mode":"image"` and, with Seedance 2.5, `"aspectRatio":"adaptive"`; for a single reference image set `"mode":"reference"`. Add `"assetId":"THE_UPLOADED_ASSET_ID"`. The server reads the actual stored metadata and verifies ownership. The UI's reference mode does not claim support for arbitrary multi-image, audio or video references.

The prompt must contain 5–2,000 characters. `locale` accepts `en`, `zh`, `es`, `pt-BR`, `fr`, `de`, `it`, `nl`, `pl`, `ru`, `tr`, `ar`, `hi`, `bn`, `id`, `vi`, `th`, `ms`, `ja` or `ko`. This records the interface language; it does not translate the prompt or guarantee output quality in that language. Supported configured options come from the model catalogue; the connected provider remains the final authority on model entitlement and parameter support. At most three active tasks per account are accepted, including tasks held for review.

A job exposes:

```text
id, prompt, settings, status, credits, assetId, createdAt, updatedAt,
errorCode, errorMessage, deleted, videoUrl
```

`settings` contains the validated request settings. `videoUrl` is present only after a successful output has been saved. No sample URL is returned.

### Inspect and manage jobs

| Method and endpoint | Request / response |
|---|---|
| `GET /api/jobs` | `{jobs:[...]}`; newest 100 non-deleted jobs for the current owner |
| `GET /api/jobs/{id}` | `{job}` |
| `POST /api/jobs/{id}/cancel` | `{}`; returns `{job}`; only `queued` jobs can be cancelled |
| `POST /api/jobs/{id}/retry` | `{idempotencyKey:"NEW_UUID"}`; returns `202 {job}` for a new task; only failed/cancelled tasks |
| `DELETE /api/jobs/{id}` | `{deleted:true}`; only succeeded/failed/cancelled tasks |
| `GET /api/jobs/{id}/video` | Private MP4 bytes; owner authentication required |
| `GET /api/jobs/{id}/video?download=1` | MP4 with download disposition |
| `HEAD /api/jobs/{id}/video` | File headers |

Poll the job endpoint about every 5 seconds while active, with backoff after network errors. The separate worker submits and polls the provider; reading a job does not itself run the provider task.

States:

- `queued` → `submitting` → `running` → `saving` → `succeeded` is the normal path.
- `failed` records a confirmed failure and returns the reserved credits once.
- `cancelled` records cancellation in the local queue and returns the reservation once. Submitted provider tasks cannot be cancelled through this endpoint.
- `attention` means the submission or result is uncertain and requires operator reconciliation. Do not automatically resubmit, refund or claim success from this state.

Retry creates a new chargeable task. An unwanted creative result from a successfully completed task is not a technical failure. Delete removes library content and the stored result while preserving the accounting/idempotency record needed to prevent duplicate charging.

## Video-to-prompt analysis

Video analysis currently accepts only `en` and `zh`; it has not been localized into the other interface languages. The `/en/video-to-prompt` and `/zh/video-to-prompt` pages support a real sampled-frame analysis API as well as a manual worksheet. The API is unavailable until the operator configures a compatible vision model; the worksheet and local frame preview remain usable without claiming an AI result. No live model accuracy or quality benchmark has been measured for this site.

### POST /api/analyze

Requires a verified account and the authentication/origin rules above. This endpoint accepts **six JPEG frames**, not an original video upload. The browser tool accepts a decodable local video up to 50 MiB and 60 seconds, samples at 1/12, 3/12, 5/12, 7/12, 9/12 and 11/12 of its duration, and scales frames to fit within 1280 × 720. Original video and audio stay on the user's device; the six frames are sent to the analysis provider after submission.

Request shape below uses placeholders. Replace every `<FRAME_N_BASE64>` with an actual encoded JPEG; there must be exactly six strings:

```json
{
  "frames": [
    "data:image/jpeg;base64,<FRAME_1_BASE64>",
    "data:image/jpeg;base64,<FRAME_2_BASE64>",
    "data:image/jpeg;base64,<FRAME_3_BASE64>",
    "data:image/jpeg;base64,<FRAME_4_BASE64>",
    "data:image/jpeg;base64,<FRAME_5_BASE64>",
    "data:image/jpeg;base64,<FRAME_6_BASE64>"
  ],
  "locale": "en",
  "idempotencyKey": "269ed4fb-8473-41ad-b546-f4a6e1b2a983"
}
```

Generate a fresh key for an intentional new analysis; keep the same key for recovery of that request. Keys use the same 16–128 letter/digit/underscore/hyphen format as generation. The server rejects remote image URLs, non-JPEG content, frames above 1 MiB, width above 1280 or height above 720, and JSON requests above 8 MiB. It decodes, normalizes and removes metadata from each frame. Model ID, credit price, owner and output status are controlled by the server.

The endpoint makes one synchronous provider call, with a 90-second provider timeout and a route duration allowance of 120 seconds. It returns `{analysis}` with HTTP 202 if a duplicate request finds an analysis still running, otherwise HTTP 200 for its stored outcome. **HTTP 200 does not mean the analysis succeeded**: inspect `analysis.status`.

The analysis record contains:

```text
id, status, credits, locale, createdAt, updatedAt,
result, errorCode, errorMessage
```

On `succeeded`, `result` has this structure:

```ts
{
  summary: string;
  prompt: string;
  limitations: string;
  shots: Array<{
    frame: number;       // Exactly six items, numbered 1 through 6 in order.
    description: string;
    camera: string;
    prompt: string;
  }>;
}
```

These are notes about sampled frames, not detected edit boundaries. Six still images cannot recover the exact original prompt, full motion, dialogue or audio. The provider is instructed to identify motion inferences and uncertainty. The server validates lengths, structure, frame count and order; the UI presents results as plain text.

### Recover or list analyses

| Method and endpoint | Response |
|---|---|
| `GET /api/analyze` | `{analyses:[...]}`; the current owner's newest 20 records |
| `GET /api/analyze?id=ANALYSIS_ID` | `{analysis}` for that owner |
| `GET /api/analyze?idempotencyKey=REQUEST_KEY` | `{analysis}`; recover after an interrupted response without resubmitting frames |

All reads also require a verified account. After a network interruption, query by the existing key before considering a new submission. If no record exists, the user may explicitly retry the same frames with the same key. Do not silently create a new key or resubmit an `attention` outcome.

### Analysis credits and outcomes

`ANALYSIS_CREDITS` configures the reservation per analysis (default 5, integer range 1–10000). The operator must supply `ARK_API_KEY`, a verified compatible `ARK_VISION_MODEL` and `RATE_CARD_CONFIRMED=true`. No vision model ID is guessed. Provider configuration readiness does not establish model entitlement or result quality. This is the site's fee, not the provider's price.

- Same owner, same key and normalized frame content: one record, one provider submission and one reservation. Different content under the same key returns `409 idempotency_conflict`.
- `running` means the provider call is in progress. A queried record still running after 150 seconds moves to `attention`; a late response cannot overwrite that reconciliation state.
- `succeeded` contains validated analysis text.
- `failed` records a known rejection or invalid completed output and returns reserved credits once.
- `attention` records an ambiguous timeout, transport failure or provider result. It keeps the reservation for operator reconciliation and is not automatically refunded or submitted again.

At most two `running`/`attention` analyses per account are permitted. The ledger uses `generation` and `generation_refund` entries with the analysis UUID as the reference. The database stores request digests, outcome text, credits and state, not the sampled frames or original video. There is no public reconciliation/refund override endpoint.

Missing configuration returns `503 analysis_not_configured`; common other errors include 401 unauthenticated, 403 unverified email, 402 insufficient credits, 400 invalid frames, 409 idempotency conflict, 413 oversized JSON and 429 rate/concurrency limits. Mock-provider tests do not substitute for a paid end-to-end test with an enabled vision model.

## Credits and checkout

### GET /api/billing

Authenticated owner response: `{balance, ledger, plans, payments, configured}`. The ledger shows credit changes, references and dates. Payment plan `amount` is in the currency's smallest unit; for USD, 1000 means $10.00. The current example plans remain disabled unless the payment integration is configured.

### POST /api/billing/checkout

Requires a verified account and configured Stripe plans:

```json
{"planId":"starter","idempotencyKey":"bfc86df4-682f-42be-8851-f511de798cef","locale":"en"}
```

Returns `{url}` for Stripe Checkout. Keep the key stable across network retries. A browser returning to `?checkout=success` is **not** proof that credits were paid. Credits are added only after the server accepts the signed Stripe webhook. The worker does not grant payment credits, and the client cannot set a balance.

`POST /api/billing/webhook` is reserved for signed Stripe events and is not a client checkout or manual credit-grant endpoint. Unconfigured payments fail explicitly. No live Stripe settlement has been demonstrated by the presence of this code alone.

## Browser account management

These endpoints use the same-origin browser session and JSON bodies. Do not use an API key for account changes.

| Endpoint | Body / behavior |
|---|---|
| `GET /api/auth/me` | `{user}` or `{user:null}` |
| `POST /api/auth/register` | `{email,password,name,locale}`; creates an account and session; returns `{user,verification}` |
| `POST /api/auth/login` | `{email,password}`; establishes the session and returns `{user}` |
| `POST /api/auth/logout` | `{}`; clears the session |
| `POST /api/auth/forgot-password` | `{email,locale}`; requests a reset email with an account-neutral response |
| `POST /api/auth/reset-password` | `{token,password}`; resets the password and revokes existing sessions/API keys |
| `POST /api/auth/verify-email` | `{token}`; validates the email link |
| `POST /api/auth/resend-verification` | `{locale}`; authenticated session required |

Passwords use 8–128 characters; names use 1–80 characters; emails use at most 254 characters. Registration may succeed while email delivery is unavailable: check `verification.sent` and the returned status. This does not mark the email as verified. Email links use an account URL fragment (`#reset=...` or `#verify=...`); the UI reads and removes it before posting the token.

### API key management

`GET /api/keys` lists metadata. `POST /api/keys` accepts `{name}` for a verified browser account and returns `{key,token}`; the full token is shown once. Keys expire after 90 days; a maximum of 10 active keys is allowed. `DELETE /api/keys?id=KEY_ID` revokes an owned key immediately. These endpoints reject bearer authentication.

## Operational and error boundaries

Common statuses include 400 invalid settings, 401 login required, 402 insufficient credits, 403 unverified email/origin rejection, 404 missing resource, 409 state/idempotency conflict, 413 upload too large, 429 rate limit, and 503 unavailable provider/worker/payment/email configuration. Use the returned `code` and message; never interpret an HTTP error as a sample generation success.

Private assets, job output and billing data must remain authenticated. The current single-server deployment needs persistent disk, backups and a separate task worker. Production publication also requires live provider, email and payment acceptance tests, a confirmed credit rate card and accurate operator/contact details. See the website's integration status and deployment documentation for the current state.
