# aisplice — full API contract Generated from the published documentation. Every value below is what the gateway actually returns; branch on these rather than on OpenAI defaults. Base URL: `https://api.your-domain.example` · Auth: `Authorization: Bearer sk-...` ## Error envelopes ### Gateway error The gateway itself rejected the call — auth, quota, routing, request shape. ``` { "error": { "message": "insufficient account balance, please top up to continue (request id: 20260910...)", "type": "xiaoxiang_error", "code": "insufficient_user_quota" } } ``` ### Upstream error, passed through The vendor answered with its own error. The body is forwarded unchanged, so type and code are the vendor’s. ``` { "error": { "message": "This model's maximum context length is 128000 tokens.", "type": "invalid_request_error", "code": "context_length_exceeded" } } ``` ### Guard rejection Idempotency and a few other pre-relay guards answer before the relay envelope exists. Flat shape, uppercase code. ``` { "code": "IDEMPOTENCY_IN_PROGRESS", "error": "a request with this Idempotency-Key is still processing" } ``` ### Async task rejection Every task endpoint — video, image task, music, 3D — reports failures in its own flat shape. There is no nested error object here, so a parser written against /v1/chat/completions reads null. ``` { "code": "task_not_exist", "message": "task does not exist", "data": null } ``` ### Status only, empty body Rate-limit rejections and oversized request bodies abort with a bare status. ``` HTTP/1.1 429 Too Many Requests Retry-After: 20 Content-Length: 0 ``` ## Response headers | Header | Sent when | Use | | --- | --- | --- | | `X-Oneapi-Request-Id` | Every response, success or error. | Log it beside your own trace id. Support answers tickets from this value, and the same id is appended to gateway error messages. | | `Retry-After` | On 429, and on 409 while an Idempotency-Key is still processing. | Wait at least this many seconds. It is the only backoff hint the gateway sends. | | `X-Idempotent-Replayed` | A stored response is replayed for a repeated Idempotency-Key. | true means no new work ran and nothing was charged again — treat the body as the original result. | | `X-Idempotency-Sealed` | The original response cannot be replayed safely (for example a stream or an oversized body). | Stop retrying that key. Reconcile from the task endpoint or your own records instead. | ## Gateway error codes | HTTP | error.code | Retryable | Meaning | Action | | --- | --- | --- | --- | --- | | 401 | (none) | no | Missing, malformed, deleted or disabled bearer token. This layer sets no code — match on the status. | Check the Authorization header and the key state in Console → Keys. | | 401 | `signature_required` | no | The key has request signing on and the request lacks X-Timestamp, X-Nonce or X-Signature. | Sign every request made with this key (Authentication & keys → Request signing), or turn signing off on the key. | | 401 | `signature_expired` | yes | X-Timestamp is not 10-digit unix seconds or is more than 5 minutes from server time. The response carries X-Server-Timestamp. | Correct the client clock by the X-Server-Timestamp difference and sign again with a new nonce. | | 401 | `signature_invalid` | no | X-Signature does not match the key’s signing secret, or X-Nonce is malformed. | Sign timestamp, nonce, method and path (no query string) joined by newlines with the current secret from Console → Keys. | | 401 | `signature_replayed` | yes | This X-Nonce was already used with this key in the last 10 minutes. | Generate a new random nonce for every request, including retries. | | 403 | `access_denied` | no | The caller IP is outside the key’s IP allowlist. | Add the egress address in Console → Keys, or drop the allowlist for keys with no fixed IP. | | 403 | (none) | no | Account suspended, or the key is not allowed to use this model or channel. | Check the key’s model whitelist first; if it is unrestricted, open a ticket — the account itself is blocked. | | 400 | `unsupported_endpoint` | no | The model exists but does not serve this path. The message lists the endpoints it does serve. | Move the call to a listed endpoint — image task models do not answer on /v1/chat/completions. | | 400 | `read_request_body_failed` | no | The request body could not be read — truncated upload, wrong Content-Type, or a stream that ended early. | Send a complete body with a matching Content-Type. Resending the same broken request fails the same way. | | 402 | `insufficient_user_quota` | no | Wallet balance cannot cover the amount pre-reserved for this call. | Top up in Console → Wallet, then retry. | | 429 | `insufficient_user_quota` | no | The account’s daily spend cap is reached. Same status as throttling, opposite meaning — backoff will not clear it. | Raise or clear the cap in account settings, or resume tomorrow. Branch on the code, not on 429 alone. | | 429 | (none) | yes | Per-key RPM/RPD throttle or a concurrency guard. The body is empty; Retry-After carries the wait. | Sleep for Retry-After seconds, then retry with exponential backoff and jitter. | | 413 | `read_request_body_failed` | no | Request body exceeds the accepted size. The transport-level limit aborts with an empty body. | Shrink the payload — upload files through /v1/media/upload-policy and send the URL instead of Base64. | | 422 | `invalid_request` | no | No channel supports this media intent for the model — for example image-to-video on a text-to-video-only route. | Check the model card for supported intents and switch model or intent. | | 503 | `model_not_found` | yes | Unknown model name, or no healthy channel currently serves it. Note the status: an unknown model is 503, not 404. | Verify the name against /v1/models, then retry — failover usually finds a healthy channel on the next attempt. | | 500 | `sensitive_words_detected` | no | The prompt hit the content filter before it left the gateway. | Change the prompt. Retrying the same text always fails. | | 500 | `count_token_failed` | yes | The gateway could not estimate tokens for pricing, usually from a malformed message array. | Validate the messages array; if it is well-formed, retry and open a ticket with the request id. | | 500 | `invalid_request` | no | The request could not be parsed into a shape this endpoint accepts. Note the status — a body the gateway cannot read is reported as 500, not 400. | Validate the JSON against the endpoint reference before retrying. | | 502 | `do_request_failed` | yes | The vendor was unreachable, or returned a failure the gateway could not recover through failover. | Retry with backoff — the next attempt usually lands on a different channel. | | 504 | `do_request_failed` | yes | The vendor did not answer within the gateway deadline. | Retry with backoff; long prompts and busy vendors time out more often. | | 499 | `do_request_failed` | no | Your client closed the connection before the answer arrived — usually your own HTTP timeout firing mid-stream. | Raise the client read timeout. Long generations need minutes, not the default of most HTTP libraries. | ## Idempotency Header `Idempotency-Key`, at most 255 characters. A completed response is replayed for 24h and carries `X-Idempotent-Replayed: true`. - **Opt-in, never automatic** — Without the header nothing changes. A retry with no key is a second billable request. - **Write methods only** — GET, HEAD and OPTIONS ignore the header — polling a task is already safe to repeat. - **Scoped to user + key + method + route** — The same value under a different API key, or on a different endpoint, is a different operation. - **The body is part of the identity** — Path, query and body are hashed together. Reusing a key with a changed payload returns 409, it does not overwrite. - **Not compatible with streaming** — A streamed response cannot be stored for replay, so stream:true plus a key is rejected with 400. - **Fails closed** — If the ledger is unavailable the request is refused with 503 rather than executed unprotected. | HTTP | code | Meaning | Action | | --- | --- | --- | --- | | 400 | `IDEMPOTENCY_STREAM_UNSUPPORTED` | Idempotency-Key was sent on a streaming request. | Drop the header for streaming calls, or set stream:false when you need replay protection. | | 400 | `IDEMPOTENCY_BODY_UNREADABLE` | The body could not be hashed for the idempotency ledger. | Send a normal JSON or multipart body — chunked bodies that cannot be buffered are rejected. | | 409 | `IDEMPOTENCY_PAYLOAD_CONFLICT` | This key was already used with a different body, path or query. | Generate a new key. A key identifies one exact request, not one retry loop. | | 409 | `IDEMPOTENCY_IN_PROGRESS` | The first request with this key is still running. | Wait for Retry-After (2s) and retry the same key — you will get the original response. | | 409 | `IDEMPOTENCY_RESULT_UNREPLAYABLE` | The original request finished but its response was sealed and cannot be replayed. | Do not resubmit blindly. Reconcile through the task endpoint or your own records first. | | 503 | `IDEMPOTENCY_UNAVAILABLE` | The idempotency ledger is unavailable, so the guard fails closed rather than risk a double charge. | Retry with backoff — the request was not executed. | ## Async task status One internal lifecycle, two public vocabularies. Match the column for the endpoint family you poll. | Internal | Image · music · 3D | Video · webhook | Terminal | Meaning | | --- | --- | --- | --- | --- | | `NOT_START` | `processing` | `queued` | no | Accepted by the gateway, not yet handed to a channel. | | `SUBMITTED` | `queued` | `queued` | no | Sent upstream and acknowledged by the vendor. | | `QUEUED` | `queued` | `queued` | no | Waiting in the vendor’s queue. | | `IN_PROGRESS` | `processing` | `in_progress` | no | Rendering. progress carries a percentage string such as "60%". | | `SUCCESS` | `succeeded` | `completed` | yes | Finished. Fetch the result from /content. | | `FAILURE` | `failed` | `failed` | yes | Failed for good. fail_reason explains why; the reserved quota is refunded. | | `UNKNOWN` | `unknown` | `unknown` | no | The vendor stopped reporting a state the gateway can map. Keep polling with a deadline, then reconcile — do not treat it as success or failure. | ## Webhooks Submit with `webhook_url` (and optional `webhook_secret`). Delivery times out after 15s and retries after 5s, 30s, 2m, then stops. - **Must be publicly routable** — localhost, 127.0.0.1, private ranges, link-local and cloud metadata addresses are rejected when the task is submitted — not at delivery time. Use a tunnel while developing. - **http or https, resolvable at submit time** — The hostname is resolved and every returned address is checked. A name that does not resolve fails the submit. - **Delivery is best-effort** — Three retries, then the gateway stops and only logs. The status endpoint stays the source of truth. | Header | Sent when | Use | | --- | --- | --- | | `X-Xiaoxiang-Signature` | Only when the task was submitted with webhook_secret. | sha256=. Verify before parsing, with a constant-time compare. | | `X-Xiaoxiang-Event` | Every delivery. | task.completed or task.failed — route on this instead of re-deriving intent from the payload. | | `X-Xiaoxiang-Event-Id` | Every delivery. | Stable across retries of the same event. Store it and drop repeats — this is your deduplication key. | | Field | Type | Present | Description | | --- | --- | --- | --- | | `event_id` | string | Always | Matches X-Xiaoxiang-Event-Id. Deduplicate on it. | | `event` | string | Always | "task.completed" or "task.failed". | | `task_id` | string | Always | The id returned by the submit call — your join key. | | `status` | string | Always | Terminal status in the video vocabulary — "completed" or "failed", even for image and music tasks. | | `model` | string | When known | Model that produced the task. Omitted when unknown. | | `url` | string | On success | Result URL when the task succeeded. Prefer /content for a stable platform link. | | `fail_reason` | string | On failure | Present on failure only. | | `quota` | integer | Always | Quota actually consumed, in internal units. Reconcile against the usage log, not this field. | | `created_at` | integer | Always | Unix seconds, task submission. | | `finished_at` | integer | Always | Unix seconds, terminal transition. | ## Endpoints ### Text #### `POST /v1/chat/completions` The workhorse endpoint: multi-turn chat, streaming, tool calling, vision and JSON mode for every chat-capable model in the catalogue. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | Model ID from /v1/models. The gateway resolves the upstream vendor from this value. | | `messages` | array | yes | Conversation turns: {role: system\|user\|assistant\|tool, content}. Content may be a string or a multimodal part array. | | `stream` | boolean | no | true streams SSE deltas, closed by data: [DONE]. | | `temperature` | number | no | 0 to 2. Leave empty for the model default. | | `max_tokens` | integer | no | Upper bound on generated tokens; also caps the pre-reserved quota estimate. | | `tools` | array | no | Function declarations the model may call — see the tool-calling guide. | | `tool_choice` | string \| object | no | "auto", "none", or pin a specific function. | | `response_format` | object | no | {"type":"json_object"} or a json_schema wrapper for structured output on supporting models. | | `top_p` | number | no | Nucleus sampling; use temperature or top_p, not both. | | `stop` | string \| array | no | Up to 4 stop sequences that cut generation. | Response: ```json { "id": "chatcmpl-…", "object": "chat.completion", "model": "gpt-5.6-luna", "choices": [{ "index": 0, "message": {"role": "assistant", "content": "Hello!"}, "finish_reason": "stop" }], "usage": {"prompt_tokens": 9, "completion_tokens": 3, "total_tokens": 12} } ``` #### `POST /v1/responses` OpenAI’s Responses API shape — single input field, structured output items, and reasoning summaries for models that expose them. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | Model ID from /v1/models. The gateway resolves the upstream vendor from this value. | | `input` | string \| array | yes | The prompt: a plain string or an array of input items. | | `instructions` | string | no | System-level guidance, equivalent to a system message. | | `stream` | boolean | no | Streams response.* SSE events instead of one JSON body. | | `max_output_tokens` | integer | no | Upper bound on generated tokens; also caps the pre-reserved quota estimate. | | `tools` | array | no | Function declarations the model may call — see the tool-calling guide. | | `previous_response_id` | string | no | Chain a follow-up turn onto a prior response without resending history. | Response: ```json { "id": "resp_…", "object": "response", "model": "gpt-5.6-luna", "output": [{ "type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "Hello!"}] }], "usage": {"input_tokens": 9, "output_tokens": 3, "total_tokens": 12} } ``` #### `POST /v1/messages` Anthropic’s native wire format, served with the same bearer key. Claude Code and the official anthropic SDKs work by only changing the base URL — no adapter, no shape translation. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | A Claude model ID from the models page, e.g. claude-opus-5-5. | | `max_tokens` | integer | yes | Required by the Anthropic shape — the hard output cap. | | `messages` | array | yes | Anthropic-style turns: {role: user\|assistant, content}. | | `system` | string | no | System prompt — a top-level field here, not a message role. | | `stream` | boolean | no | Streams Anthropic-native SSE events (message_start, content_block_delta, …). | | `temperature` | number | no | 0 to 1 in the Anthropic scale. | | `tools` | array | no | Anthropic tool definitions; tool_use blocks come back in content. | Response: ```json { "id": "msg_…", "type": "message", "role": "assistant", "model": "claude-opus-5-5", "content": [{"type": "text", "text": "Hello!"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 5} } ``` #### `POST /v1/responses/compact` Condense a long conversation on models that support compaction — no streaming form. ### Vector #### `POST /v1/embeddings` Vector embeddings on the OpenAI shape — text-embedding-3, jina-embeddings, voyage and vendor equivalents. Billing is by input tokens. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | An embedding model ID, e.g. text-embedding-3-small. | | `input` | string \| array | yes | One string or an array — the response returns vectors in the same order. | | `dimensions` | integer | no | Truncate the vector on models that support Matryoshka dimensions. | | `encoding_format` | string | no | "float" (default) or "base64" for a compact payload. | Response: ```json { "object": "list", "model": "text-embedding-3-small", "data": [ {"object": "embedding", "index": 0, "embedding": [0.0023, -0.0091, …]}, {"object": "embedding", "index": 1, "embedding": [0.0154, 0.0007, …]} ], "usage": {"prompt_tokens": 8, "total_tokens": 8} } ``` #### `POST /v1/rerank` Score a list of candidate documents against one query. Results are returned in descending relevance order and retain each document’s original index. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | Exact rerank model identifier from the live model catalogue. | | `query` | string | yes | The search query or question used to score candidates. | | `documents` | string[] | yes | Candidate passages to rank. Keep their original order in your application. | | `top_n` | integer | no | Maximum number of ranked results to return. | | `return_documents` | boolean | no | When supported, include document text in each result. | Response: ```json { "results": [ { "index": 0, "relevance_score": 0.9821 }, { "index": 1, "relevance_score": 0.1034 } ] } ``` #### `POST /v1/moderations` Content moderation on the same wire format. ### Audio #### `POST /v1/audio/transcriptions` Speech both ways: /v1/audio/speech synthesizes text into audio bytes; /v1/audio/transcriptions turns audio into text (Whisper-compatible); /v1/audio/translations transcribes straight into English. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | tts-1 class for speech; whisper-1 class for transcription. | | `input` | string | yes | speech only: the text to speak. | | `voice` | string | yes | speech only: voice name (alloy, nova, …); available voices per model card. | | `file` | file (multipart) | yes | transcriptions only: the audio file (mp3, wav, m4a, webm …). | | `language` | string | no | transcriptions only: ISO-639-1 hint that improves accuracy. | | `response_format` | string | no | speech: mp3 \| wav \| opus …; transcriptions: json \| text \| srt \| verbose_json. | | `speed` | number | no | speech only: 0.25–4.0 playback speed. | Response: ```json // /v1/audio/speech → binary audio body (Content-Type: audio/mpeg) // /v1/audio/transcriptions {"text": "…transcript…"} ``` #### `POST /v1/audio/translations` Speech both ways: /v1/audio/speech synthesizes text into audio bytes; /v1/audio/transcriptions turns audio into text (Whisper-compatible); /v1/audio/translations transcribes straight into English. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | tts-1 class for speech; whisper-1 class for transcription. | | `input` | string | yes | speech only: the text to speak. | | `voice` | string | yes | speech only: voice name (alloy, nova, …); available voices per model card. | | `file` | file (multipart) | yes | transcriptions only: the audio file (mp3, wav, m4a, webm …). | | `language` | string | no | transcriptions only: ISO-639-1 hint that improves accuracy. | | `response_format` | string | no | speech: mp3 \| wav \| opus …; transcriptions: json \| text \| srt \| verbose_json. | | `speed` | number | no | speech only: 0.25–4.0 playback speed. | Response: ```json // /v1/audio/speech → binary audio body (Content-Type: audio/mpeg) // /v1/audio/transcriptions {"text": "…transcript…"} ``` #### `POST /v1/audio/speech` Speech both ways: /v1/audio/speech synthesizes text into audio bytes; /v1/audio/transcriptions turns audio into text (Whisper-compatible); /v1/audio/translations transcribes straight into English. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | tts-1 class for speech; whisper-1 class for transcription. | | `input` | string | yes | speech only: the text to speak. | | `voice` | string | yes | speech only: voice name (alloy, nova, …); available voices per model card. | | `file` | file (multipart) | yes | transcriptions only: the audio file (mp3, wav, m4a, webm …). | | `language` | string | no | transcriptions only: ISO-639-1 hint that improves accuracy. | | `response_format` | string | no | speech: mp3 \| wav \| opus …; transcriptions: json \| text \| srt \| verbose_json. | | `speed` | number | no | speech only: 0.25–4.0 playback speed. | Response: ```json // /v1/audio/speech → binary audio body (Content-Type: audio/mpeg) // /v1/audio/transcriptions {"text": "…transcript…"} ``` ### Music #### `POST /v1/music/generations` Async music generation for song and instrumental workflows. Submit returns a task_id immediately; poll the task or accept a webhook callback, then read the finished audio from /content. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | A music model ID from the models page, e.g. suno. | | `prompt` | string | yes | Describe the song, mood, instrumentation, or lyric direction. | | `title` | string | no | Optional track title forwarded to model families that support it. | | `tags` | string \| array | no | Optional genre or style hints such as cinematic, pop, or ambient. | | `version` | string | no | Model-family version switch when the provider exposes multiple generations. | | `mv` | string | no | Provider-specific music mode selector; keep it server-side unless users need explicit control. | | `instrumental` | boolean | no | Generate instrumental output without vocals when the model supports it. | | `make_instrumental` | boolean | no | Alternative vendor flag for instrumental output; some providers use this instead of instrumental. | | `continue_clip_id` | string | no | Continue or extend a previous clip instead of starting a fresh track. | | `continue_at` | number | no | Resume point in seconds for continuation flows. | | `webhook_url` | string | no | HTTPS URL called once the task reaches a terminal state. | | `webhook_secret` | string | no | If set, deliveries carry X-Xiaoxiang-Signature: sha256=hex(HMAC-SHA256(body, secret)). | Response: ```json // POST — submit accepted {"task_id": "task_xxx", "status": "queued"} // GET /v1/music/tasks/{task_id} { "task_id": "task_xxx", "status": "in_progress", "progress": "42%" } // GET /v1/music/tasks/{task_id}/content { "task_id": "task_xxx", "status": "completed", "metadata": { "audio_url": "https://…", "lyric": "…" } } ``` #### `GET /v1/music/tasks/:task_id` Poll async music task status and progress. #### `GET /v1/music/tasks/:task_id/content` Fetch the finished audio through the gateway. ### Image #### `POST /v1/images/generations` OpenAI’s image API, as the OpenAI SDK calls it. Every image model answers it: the call waits until the image is ready (usually 30–60 s) and returns it the way OpenAI does. To edit images, call /v1/images/edits the same way with the images uploaded as files or given as URLs. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | Any image model, e.g. gpt-image-2. | | `prompt` | string | yes | What to draw. | | `n` | integer | no | Number of images (billing multiplies per image). | | `size` | string | no | Aspect-ratio preset such as "16:9" — what most image models take; the model card lists the exact values. Some older models also accept WIDTHxHEIGHT. | | `resolution` | string | no | Output resolution tier such as "1K", "2K" or "4K", where the model card lists it — also a pricing-tier dimension. | | `quality` | string | no | Vendor quality level (e.g. standard / hd) — also a pricing-tier dimension. | | `response_format` | string | no | "b64_json" (default): the image bytes; "url": a link to the image instead. | Response: ```json { "created": 1720000000, "data": [{"b64_json": "iVBORw0KGgo…", "url": "https://…"}] } ``` #### `POST /v1/images/tasks` Async image generation — the route every image model in the catalogue uses. Submit returns a task_id; poll the status endpoint, then read persistent result URLs from /content — or register a webhook and skip polling. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | An async image model ID, e.g. gpt-image-2. Sync-only models reject this endpoint. | | `prompt` | string | yes | What to draw. | | `size` | string | no | Aspect-ratio preset such as "16:9" — what most image models take; the model card lists the exact values. Some older models also accept WIDTHxHEIGHT. | | `resolution` | string | no | Output resolution tier such as "1K", "2K" or "4K", where the model card lists it — also a pricing-tier dimension. | | `quality` | string | no | Vendor quality level (e.g. standard / hd) — also a pricing-tier dimension. | | `n` | integer | no | Number of images (billing multiplies per image). | | `images` | array | no | Reference images to edit or follow, as URLs or data URIs. The model card lists how many each model takes. | | `mask` | string | no | Repaint one area of the first reference image — see "Edit part of an image" below. Only models whose model card lists mask take it; mask_url is accepted as the same field. | | `webhook_url` | string | no | HTTPS URL called once the task reaches a terminal state — see the video guide for the callback contract. | | `webhook_secret` | string | no | If set, deliveries carry X-Xiaoxiang-Signature: sha256=hex(HMAC-SHA256(body, secret)). | Response: ```json // POST /v1/images/tasks {"task_id": "task_xxx", "status": "queued"} // GET /v1/images/tasks/{task_id} {"task_id": "task_xxx", "status": "in_progress", "progress": "60%"} // GET /v1/images/tasks/{task_id}/content — after completion {"created": 1720000000, "data": [{"url": "https://…"}]} ``` #### `GET /v1/images/tasks/:task_id` Poll async image task status and progress. #### `GET /v1/images/tasks/:task_id/content` Fetch final image URLs — persistent links, safe to store. #### `POST /v1/images/edits` OpenAI’s image edit API — upload the images or give their URLs; every image model. ### Video #### `POST /v1/videos` Async video generation (Seedance, Kling, Veo, Wan, Grok Imagine …). /v1/videos accepts text-to-video and image-to-video inputs in one OpenAI-shaped request. It returns a task_id you poll — or register a webhook at submit time. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | A video model ID from the models page, e.g. doubao-seedance-2.5. | | `prompt` | string | yes | Scene description; for image2video it steers the motion. | | `image` | string | no | image2video only: first-frame image as URL or base64. | | `images` | array | no | Reference frames for vendors that accept multiple images in one task. | | `duration` | number | no | Clip length in seconds — a pricing-tier dimension; supported values per model card. | | `seconds` | number | no | Alias used by some model families for clip length; treat duration as the preferred field. | | `resolution` | string | no | Output resolution tier such as 720p or 1080p when the model exposes it. | | `size` | string | no | Aspect-ratio or size selector for model families that use width x height style sizing. | | `webhook_url` | string | no | HTTPS URL called once the task reaches a terminal state — see the video guide for the callback contract. | | `webhook_secret` | string | no | If set, deliveries carry X-Xiaoxiang-Signature: sha256=hex(HMAC-SHA256(body, secret)). | Response: ```json // POST — submit accepted {"task_id": "task_xxx", "status": "queued"} // GET /v1/videos/{task_id} { "task_id": "task_xxx", "status": "in_progress", // queued | in_progress | completed | failed "progress": "45%" } // terminal state { "task_id": "task_xxx", "status": "completed", "metadata": {"url": "https://…"} } ``` #### `GET /v1/videos/:task_id` Poll async task status; content lives at /content. #### `GET /v1/videos/:task_id/content` Download the finished video through the gateway. #### `DELETE /v1/videos/:task_id` Cancel a task that has not reached a terminal state yet. #### `POST /v1/videos/:video_id/remix` Re-render an existing video with a new prompt, on models that support it. ### 3D #### `POST /v1/model3d/tasks` Async 3D generation and post-processing. One field, operation, chooses what the task does: make a model from text, an image or several views, then texture, segment, retopologise or rig it. Submit returns a task_id; poll it until the model URL arrives in the task body. | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | yes | A 3D model ID from the catalogue, e.g. tripo-3d. | | `operation` | string | yes | text_to_model · image_to_model · multiview_to_model · texture · smart_segment · retopology · rig · animate. | | `prompt` | string | no | What to make. Used by text_to_model. | | `input` | string | no | A public image URL for image_to_model, or the task_id of a finished 3D model for texture, smart_segment, retopology, rig and animate. | | `inputs` | object[] | no | multiview_to_model: [{"front": url}, {"left": url}, …] — a front view plus at least one more. | | `texture` | boolean | no | Generate textures with the model. | | `pbr` | boolean | no | Generate PBR materials. | | `texture_prompt` | object | no | texture: {"text": "weathered bronze"} describes the new surface. | | `face_limit` | integer | no | retopology: the most faces the result may have. | | `quad` | boolean | no | retopology: quad mesh instead of triangles. | | `animation` | string | no | animate: the animation to apply to a rigged model. | Response: ```json // POST /v1/model3d/tasks {"task_id": "task_xxx", "status": "queued"} // GET /v1/model3d/tasks/{task_id} — after completion {"task_id": "task_xxx", "status": "completed", "url": "https://…/model.glb", "output": {"model_url": "https://…/model.glb"}} ``` #### `GET /v1/model3d/tasks/:task_id` Poll the 3D task; the result URL arrives in the task body, there is no /content route. ### Realtime #### `WS /v1/realtime` OpenAI Realtime API — audio + text over one socket. ### Media #### `POST /v1/media/upload-policy` Create a short-lived file upload policy for an image, video, audio, or generic file up to its configured limit. ### App subscriptions #### `POST /v1/google-play/verify` Checks a Google Play purchase token with the service account set on this API key (console → API keys → Edit → Google Play subscription check). The answer comes straight from Google; the gateway bills nothing and your app decides what to unlock. Answers are cached for one minute per token. | Field | Type | Required | Description | | --- | --- | --- | --- | | `purchase_token` | string | yes | The purchase token from Play Billing. In Flutter in_app_purchase it is purchaseDetails.verificationData.serverVerificationData. | Response: ```json { "package_name": "com.example.app", "subscribed": true, "active": true, "in_trial": false, "state": "SUBSCRIPTION_STATE_ACTIVE", "product_id": "vip_monthly", "expiry_time": 1793000000, "auto_renewing": true, "acknowledged": true, "test_purchase": false, "order_id": "GPA.3300-1111-2222-44444..2", "checked_at": 1790400000 } ``` #### `POST /v1/xsolla/verify` Checks whether an Xsolla user, or one subscription, is subscribed, with the merchant ID, project ID and Xsolla API key set on this API key (console → API keys → Edit → Xsolla subscription check). The answer comes straight from Xsolla; the gateway bills nothing and your app decides what to unlock. Found subscriptions are cached for one minute. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | no | The user.id your app put into the Xsolla payment token. Required unless subscription_id is sent. | | `subscription_id` | integer | no | An Xsolla subscription ID, if the app has one. Sent together with user_id, the subscription must belong to that user. | Response: ```json { "project_id": 273204, "subscribed": true, "active": true, "in_trial": false, "state": "active", "subscription_id": 249579, "user_id": "user-8f2c", "plan_id": 601, "plan_external_id": "weekly", "expiry_time": 1793000000, "auto_renewing": true, "checked_at": 1790400000 } ``` ### Account #### `GET /v1/balance` Returns the account balance and what the calling API key can still spend, in USD. Free and read-only: no model call, nothing billed. Keys that are used up or expired can still call it; only disabled keys are refused. | Field | Type | Required | Description | | --- | --- | --- | --- | | `Authorization` | header | yes | Bearer sk-YOUR_KEY. There is no body and no query parameter. | Response: ```json { "object": "balance", "currency": "USD", "balance": 12.5, "available": 2, "key": { "name": "app", "unlimited": false, "remaining": 2, "used": 1, "expires_at": 0 } } ``` ### Metadata #### `GET /v1/models` List every model this key is allowed to hit. #### `GET /v1/models/:model` Retrieve one model entry — the shape Anthropic and OpenAI clients probe on startup. ## Compatibility and legacy routes ### Gemini native (passthrough) Point the Google GenAI SDK at this gateway without a code change. The generateContent path shape and request body are Gemini’s own. | Method | Path | Notes | | --- | --- | --- | | GET | `/v1beta/models` | List models in the Gemini catalogue shape. | | POST | `/v1beta/models/*` | Gemini path form: /v1beta/models/{model}:{action}, e.g. :generateContent or :streamGenerateContent. | | POST | `/v1/models/*` | Same Gemini request shape under /v1, for clients that cannot change the API version prefix. | | POST | `/v1/engines/:model/embeddings` | Gemini embeddings through the legacy engines path. | ### Kling native (passthrough) Accepts Kling’s official request body (model_name + prompt and the vendor’s own fields) and returns its task shape. | Method | Path | Notes | | --- | --- | --- | | POST | `/kling/v1/videos/text2video` | Submit a Kling text-to-video task in the vendor’s own body shape. | | POST | `/kling/v1/videos/image2video` | Submit a Kling image-to-video task. | | GET | `/kling/v1/videos/text2video/:task_id` | Poll a Kling text-to-video task. | | GET | `/kling/v1/videos/image2video/:task_id` | Poll a Kling image-to-video task. | ### Jimeng native (passthrough) Jimeng’s official Action-query API. Submit and result share one path and are selected by the Action parameter. | Method | Path | Notes | | --- | --- | --- | | POST | `/jimeng/?Action=CVSync2AsyncSubmitTask&Version=2022-08-31` | Submit a task with req_key and prompt in the vendor body. The trailing slash is required — /jimeng redirects, and a 301 drops the body in most HTTP clients. | | POST | `/jimeng/?Action=CVSync2AsyncGetResult&Version=2022-08-31` | Fetch the result of a submitted task. | ### Suno native (passthrough) Suno-shaped music submission and fetch. Prefer /v1/music/generations for new work — it returns the platform task shape used everywhere else. | Method | Path | Notes | | --- | --- | --- | | POST | `/suno/submit/:action` | action is music or lyrics. | | POST | `/suno/fetch` | Batch fetch by id list. | | GET | `/suno/fetch/:id` | Fetch one task. | ### Legacy aliases (legacy) Still routed, still billed, but frozen: no new parameters and no compatibility promises. Migrate when you touch the calling code. | Method | Path | Notes | | --- | --- | --- | | POST | `/v1/completions` | OpenAI legacy prompt completion. Use /v1/chat/completions. | | POST | `/v1/edits` | Routes to the image edit format despite the name. Use /v1/images/edits. | | POST | `/v1/video/generations` | Pre-OpenAI-shape video submission. Use POST /v1/videos. | | GET | `/v1/video/generations/:task_id` | Pre-OpenAI-shape video poll. Use GET /v1/videos/{task_id}. | ### Midjourney archive (legacy) Read-only. Paid Midjourney submission is retired; only inspection of historical tasks and their archived images remains. | Method | Path | Notes | | --- | --- | --- | | GET | `/mj/task/:id/fetch` | Inspect an archived task. | | GET | `/mj/task/:id/image-seed` | Read the seed of an archived image. | | GET | `/mj/image/:id` | Serve an archived image. |