Private protocol compatibility layer (legacy migration)
The /server/* endpoints kept for legacy clients; fields and error messages are byte-compatible. New integrations should use the standard protocols.
Call
POST/server/api/call
| Field | Type | Description | |
|---|---|---|---|
| apiKey | string | required | Legacy API key (starts with ak-). |
| model | string | required | Model code (same as the legacy system). |
| clientRequestId | string | optional | Idempotency key; the same key returns the first result within 24 hours. |
| content / messages / prompt … | any | optional | Remaining fields are passed through upstream according to the model type. |
| stream | boolean | optional | Real SSE under the standard_v1 profile; buffered under legacy_v1. |
curl https://www.link42.ai/server/api/call \
-H "Content-Type: application/json" \
-d '{
"apiKey": "ak-…",
"model": "seedance-2.0",
"clientRequestId": "9d7258b0da1c42dfad49d18bfd5e4adb",
"content": [{"type": "text", "text": "Sunrise over the sea, slow push-in"}],
"duration": 5, "resolution": "720p", "ratio": "16:9", "generate_audio": true, "watermark": false
}'{"id": "cgt-2026…"}- Errors use the legacy wording, for example {"code":-1,"message":"user remainingAmount is not enough"}.
Get the result
POST/server/api/getResult
| Field | Type | Description | |
|---|---|---|---|
| id | string | optional | Task ID (either this or clientRequestId). |
| clientRequestId | string | optional | The idempotency key used at submission. |
{
"id": "cgt-2026…", "model": "seedance-2.0", "status": "succeeded",
"content": {"video_url": "https://…/video.mp4"},
"usage": {"completion_tokens": 246840, "total_tokens": 246840},
"provider_task_id": "cgt-2026…",
"client_request_id": "9d7258b0da1c42dfad49d18bfd5e4adb",
"billing_time": 1765683800,
"billing_status": "succeeded",
"actual_cost": 1.819440,
"currency_or_credit_type": "USD"
}Get the balance
POST/server/user/getAmountByApiKey
{"code": 0, "data": {"username": "…", "totalAmount": "120.00", "walletAmount": "87.42", "useAmount": "32.58"}}- This endpoint has its own rate limit.
Asset endpoints
POST/server/asset/create
The legacy system's five asset endpoints are kept as they were. The body's apiKey authenticates the call and is removed before forwarding; everything else is passed to the caller's group in the upstream asset library, and the upstream body comes back verbatim, unwrapped.
| Endpoint | Body | On success |
|---|---|---|
| POST /server/asset/list | {apiKey, Filter{Statuses, Name}, PageNumber, PageSize, SortBy, SortOrder} | The upstream page, verbatim; when the upstream is unreachable, {"code":0,"message":"success"} |
| POST /server/asset/create | {apiKey, Name, URL, AssetType} | The upstream body, verbatim, carrying Result.Id |
| POST /server/asset/get | {apiKey, Id} | The upstream body, verbatim, carrying Result.Id and Result.Status |
| POST /server/asset/update | {apiKey, Id, Name} | The upstream body, verbatim |
| POST /server/asset/delete | {apiKey, Id} | The upstream body, verbatim |
curl https://www.link42.ai/server/asset/create \
-H "Content-Type: application/json" \
-d '{
"apiKey": "ak-…",
"Name": "opening-shot",
"URL": "https://example.com/clip.mp4",
"AssetType": "Video"
}'{"code": -1, "message": "add asset error"}- Every failure answers HTTP 200 with {"code": -1, "message": "…"}, and the wording is byte-compatible with the legacy system: apiKey is null, apiKey not exist, apiKey disabled, add asset error, get asset error, update asset error, delete asset error, call api error.
- The apiKey is read from the body; when the body has none, an Authorization: Bearer or x-api-key header is accepted instead.
- list only returns the caller's own asset groups. get, update and delete only accept an Id the caller owns; anything else answers the matching … error text. When create carries no Name the platform generates asset-{yyyyMMddHHmmss}-{8 random}. AssetType accepts Video, Audio or Image and is otherwise inferred from the URL extension.
getResult shapes per profile
The same getResult request answers differently under the two compatibility profiles. Migrated ak- keys carry legacy_v1; newly created sk- keys carry standard_v1.
| Case | legacy_v1 | standard_v1 |
|---|---|---|
| Task still running, local queue included | An empty body | {"id": "…", "status": "queued"} |
| No such task, or it belongs to another key | An empty body | {"code": -1, "message": "task not found"} |
| Type of actual_cost | A JSON number, for example 1.819440 | A JSON string, for example "1.819440" |
| clientRequestId replay of a video task | {"id": "…"}, whatever state the task is in | {"id": "…"}, whatever state the task is in |
| A text call with stream=true | The whole SSE exchange buffered into one body | True SSE, relayed frame by frame |
- local_queued never appears on the private protocol: legacy clients never saw that value, so standard_v1 reports queued and legacy_v1 reports an empty body.
- When both id and clientRequestId are empty the answer is {"code": -1, "message": "id and clientRequestId can not be null at the same time"}.
- When concurrency or the queue is exhausted the private protocol maps too_many_running_tasks onto the legacy wording get lock failed.
Request shapes per model
/server/api/call carries five different body shapes on one endpoint, and the model decides which upstream shape the request is forwarded to. apiKey and clientRequestId are always removed before forwarding; everything else is passed through untouched, and only model is rewritten to the upstream model name.
| Shape | Models | Body | Response |
|---|---|---|---|
| content[] video task | The 10 Seedance models | content[] (text / image_url / video_url / audio_url); 2.x adds resolution, duration, ratio, generate_audio, watermark | Returns only {"id": "cgt-…"}; fetch the result with /server/api/getResult |
| prompt image | The 4 Seedream models | prompt, size, response_format, sequential_image_generation, watermark; 5.0 Pro adds image[] | Returns {model, created, data[], usage} synchronously |
| messages[] chat | Seed 2.0 pro / lite / mini, Seed 2.1 turbo, DeepSeek V4 Pro / Flash (-260425), GLM-5.2 (-260617) | messages[] in the OpenAI chat shape | Returns the OpenAI chat.completion shape with choices[] synchronously |
| input[] Responses | Seed-1.6, DeepSeek-3.2 | input[]; DeepSeek-3.2 may add tools | Returns the Responses shape synchronously, object set to response, with output[] |
| contents[] Gemini | Gemini3-pro | contents[], generationConfig, safetySettings | Returns the Gemini shape synchronously, with candidates[] and usageMetadata |
| prompt single turn | Configured Alibaba Bailian single-turn models such as glm-5.2; do not treat domestic Ark DeepSeek-V4-Pro / Flash as this endpoint shape | One top-level prompt string and nothing else | Returns the top-level shape {content, model, usage} synchronously, with no choices |
- A shared name with a different suffix is a different model: deepseek-v4-pro / deepseek-v4-flash are bound to domestic Ark (Pro is not currently activated), while the historical -260425 rows use BytePlus. glm-5.2 and glm-5-2-260617 are separate upstreams too. Copy an id from the live catalogue instead of guessing.
- Each model's differing fields, billing basis and limits are in the model reference table on its protocol page.