# TokenLab - Full Documentation > Unified AI API Gateway - Access hundreds of AI models through a single API Generated: 2026-10-10 Source: https://tokenlab.sh/docs --- # Documentation ## Getting Started ### Introduction > One API for chat, images, video, audio, and more URL: https://tokenlab.sh/docs/en/introduction ## Build with TokenLab TokenLab connects your application or coding tool to AI models with one API key and a shared balance. Choose a model and its supported API format; requests are charged at the published price. ## What you can build Use the current [Models catalog](https://tokenlab.sh/en/models) for model IDs, availability, supported formats, and prices. | Capability | Description | |---|---| | Chat and reasoning | Text generation, planning, and tools | | Vision and multimodal | Image understanding and multimodal chat | | Image generation and editing | Text-to-image and image editing | | Video generation | Text-to-video, image-to-video, reference video, and motion control | | Audio and realtime | TTS, transcription, audio translation, and realtime WebSocket sessions | | Embeddings and rerank | Text vectorization and reranking | | Translation | Text translation and WebSocket speech translation workflows | | Music & 3D | Music and 3D asset generation | | Worlds | World generation, status polling, and media asset workflows | ## Keep going #### [Quickstart](/quickstart) Send your first request in a couple of minutes. #### [API Reference](/api-reference/introduction) Find request fields, response shapes, and examples. #### [Integrations](/integrations/openai-sdk) Set up an SDK, coding tool, or app. #### [Models](https://tokenlab.sh/en/models) Browse current model availability and pricing. --- ### Quickstart > Make your first TokenLab API request with cURL, Python, JavaScript, Go, or PHP. Set up your API key, configure the endpoint, and verify a Chat Completions response. URL: https://tokenlab.sh/docs/en/quickstart ## Get an API key #### Create an account Sign up at [tokenlab.sh](https://tokenlab.sh) using your email, Gmail, or GitHub account. #### Create and save an API key Open [API Keys](https://tokenlab.sh/dashboard/api?tab=keys) in your workspace. Create a key and save the full value immediately; it is shown only once. If you already saved a full key, use that value. The prefix shown or copied from the list cannot authenticate requests. #### Check the workspace balance Check your available balance in [Billing](https://tokenlab.sh/dashboard/billing). This request is billed at the selected model’s current price; add funds if needed. > Warning: Keep the API key secret. Do not put it in client-side code or a public repository. ## Set the key for this terminal The commands below keep the key out of the command itself. They set it only for the current terminal and its child processes. Use a secret manager for deployed applications. #### macOS / Linux / WSL ```bash macOS / Linux / WSL printf 'TokenLab API key: ' read -r -s TOKENLAB_API_KEY printf '\n' export TOKENLAB_API_KEY ``` #### Windows PowerShell ```powershell Windows PowerShell $secret = Read-Host 'TokenLab API key' -AsSecureString $env:TOKENLAB_API_KEY = [System.Net.NetworkCredential]::new('', $secret).Password Remove-Variable secret ``` ## Install a client #### Python ```bash Python pip install openai ``` #### JavaScript ```bash JavaScript npm install openai ``` #### Go ```bash Go go mod init tokenlab-quickstart go get github.com/openai/openai-go/v3 ``` #### PHP ```bash PHP composer require openai-php/client guzzlehttp/guzzle ``` ## Send your first request For most new integrations, start with **Chat Completions** on `POST /v1/chat/completions`. #### cURL — macOS / Linux / WSL ```bash cURL — macOS / Linux / WSL curl https://api.tokenlab.sh/v1/chat/completions \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-5.6-terra","messages":[{"role":"user","content":"Reply only with OK."}]}' ``` #### Windows PowerShell ```powershell Windows PowerShell $body = @{ model = 'gpt-5.6-terra' messages = @(@{ role = 'user'; content = 'Reply only with OK.' }) } | ConvertTo-Json -Depth 6 $result = Invoke-RestMethod -Method Post ` -Uri 'https://api.tokenlab.sh/v1/chat/completions' ` -Headers @{ Authorization = "Bearer $env:TOKENLAB_API_KEY" } ` -ContentType 'application/json' -Body $body -TimeoutSec 30 $result.choices[0].message.content ``` #### Python — quickstart.py ```python Python — quickstart.py import os from openai import OpenAI with OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", timeout=30.0, max_retries=0, ) as client: response = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Reply only with OK."}], ) print(response.choices[0].message.content) ``` #### JavaScript — quickstart.mjs ```javascript JavaScript — quickstart.mjs import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.TOKENLAB_API_KEY, baseURL: 'https://api.tokenlab.sh/v1', timeout: 30_000, maxRetries: 0, }); const response = await client.chat.completions.create({ model: 'gpt-5.6-terra', messages: [{ role: 'user', content: 'Reply only with OK.' }], }); console.log(response.choices[0].message.content); ``` #### Go — main.go ```go Go — main.go package main import ( "context" "fmt" "os" "time" "github.com/openai/openai-go/v3" "github.com/openai/openai-go/v3/option" ) func main() { client := openai.NewClient( option.WithAPIKey(os.Getenv("TOKENLAB_API_KEY")), option.WithBaseURL("https://api.tokenlab.sh/v1"), option.WithMaxRetries(0), ) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() response, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{ Model: "gpt-5.6-terra", Messages: []openai.ChatCompletionMessageParamUnion{ openai.UserMessage("Reply only with OK."), }, }) if err != nil { panic(err) } fmt.Println(response.Choices[0].Message.Content) } ``` #### PHP — quickstart.php ```php PHP — quickstart.php withApiKey(getenv('TOKENLAB_API_KEY')) ->withBaseUri('https://api.tokenlab.sh/v1') ->withHttpClient(new \GuzzleHttp\Client(['timeout' => 30])) ->make(); $response = $client->chat()->create([ 'model' => 'gpt-5.6-terra', 'messages' => [['role' => 'user', 'content' => 'Reply only with OK.']], ]); echo $response->choices[0]->message->content . PHP_EOL; ``` Save one SDK example as `quickstart.py`, `quickstart.mjs`, `main.go`, or `quickstart.php`, then run `python quickstart.py`, `node quickstart.mjs`, `go run .`, or `php quickstart.php`. Python, JavaScript, and Go use official OpenAI SDKs; PHP uses the community-maintained [OpenAI PHP client](https://github.com/openai-php/client) (PHP 8.2+). > Note: Use `POST /v1/responses` only when you need Responses-specific behavior. Support for Responses-only fields depends on the selected model. ## Confirm the result A successful request returns a non-empty answer. Open [Requests](https://tokenlab.sh/dashboard/runs) in the same workspace to check its model, status, and usage. For errors, follow [troubleshooting](/guides/observability-troubleshooting). Changing the model or adding streaming is a separate next step in the [SDK guide](/integrations/openai-sdk). ## Keep building #### [Authentication](/authentication) Store, limit, and rotate API keys. #### [OpenAI SDK](/integrations/openai-sdk) Use your existing OpenAI SDK with TokenLab. #### [API Reference](/api-reference/introduction) Find request fields, response shapes, and examples. #### [Models](https://tokenlab.sh/en/models) Browse current model availability and pricing. --- ### Authentication > Authenticate TokenLab API requests with an API key URL: https://tokenlab.sh/docs/en/authentication ## API Keys Model calls and private resources require an API key. Public discovery endpoints such as `GET /v1/models` and `GET /v1/models/{model}` work without one. A successful discovery response does not verify that your key works; follow the Quickstart to send a model request. Use the complete saved key; the prefix in the key list cannot authenticate. For OpenAI-compatible endpoints, send it as: ```bash Authorization: Bearer sk-your-api-key ``` For Anthropic-compatible `/v1/messages` requests, you can also use: ```bash x-api-key: sk-your-api-key ``` ## Management Tokens Management API endpoints use a separate token type: ```bash Authorization: Bearer mt-your-management-token ``` Use management tokens only with `/v1/management/*` endpoints. Open [API → Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) for the selected workspace to create or replace a token. Save the full token when it is shown; it is displayed only once after creation or replacement. > Note: Management tokens cannot be used for model inference, and standard `sk-...` API keys cannot be used for the Management API. ## Get an API key 1. Sign in to [TokenLab Console](https://tokenlab.sh/dashboard) 2. Open **API Keys** 3. Create a new key 4. Give it a descriptive name 5. Copy it immediately because it is shown only once > Warning: - Never expose API keys in client-side code > - Never commit API keys to version control > - Use environment variables or a secret manager > - Rotate keys periodically > - Delete unused keys ## Use an API key See the [Quickstart](/quickstart) for environment variables and a complete first request. Use the SDK matching your chosen [API format](/guides/api-formats); keep keys on the server. ## Set a usage limit You can set a usage limit on each API key: | Setting | Description | |---------|-------------| | **No Limit** | Key uses your account balance without restrictions | | **Fixed Limit** | Key stops working after reaching the specified amount | ## Key prefix All TokenLab API keys start with `sk-`. ## Error Responses | Status Code | Type | Code | Description | |-------------|------|------|-------------| | 401 | `invalid_api_key` | `invalid_api_key` | The API key is missing, invalid, inactive, or revoked | | 401 | `expired_api_key` | `expired_api_key` | The key has expired | | 402 | `insufficient_balance` | `insufficient_balance` | Account balance is insufficient | | 402 | `quota_exceeded` | `quota_exceeded` | API key usage limit reached | Example: ```json { "error": { "message": "Invalid API key provided", "type": "invalid_api_key", "code": "invalid_api_key" } } ``` --- ## Core Guides ### Errors agents can act on > Use error codes, retry timing, and model suggestions without parsing prose URL: https://tokenlab.sh/docs/en/guides/agent-first-api This page describes machine-readable public API errors for applications and coding agents. It does not grant access to workspace request investigations or support. For a request in your workspace, start with [request troubleshooting](/guides/observability-troubleshooting). OpenAI-compatible TokenLab errors can include structured hints for an agent or application. Use these fields when present; do not parse the human-readable `message` to decide what to do. Anthropic Messages and Gemini APIs keep their native error formats, so the extensions on this page apply only to OpenAI-compatible Chat Completions and Responses errors. ## Optional error fields All fields below appear inside the `error` object and may be absent. | Field | Type | Use | | --- | --- | --- | | `did_you_mean` | `string` | Replacement for a retired model, or the closest available model ID | | `suggestions` | `array` | Models that may fit the request | | `hint` | `string` | A short explanation or suggested action | | `retryable` | `boolean` | Whether the same request may succeed later | | `retry_after` | `number` | Seconds to wait before trying again | | `balance_usd` | `number` | Current balance in USD | | `estimated_cost_usd` | `number` | Estimated cost of the rejected request | Your client should still handle every error by its HTTP status and `code`. Treat these extra fields as useful context, not required fields. ## Unknown model A misspelled or unavailable model returns `400 model_not_found`. If `did_you_mean` is present, show it to the user or retry only when your product already has permission to change the selected model. ```json { "error": { "message": "Model not found: please check the model name", "type": "invalid_request_error", "param": "model", "code": "model_not_found", "did_you_mean": "gpt-5.6-terra", "suggestions": [ {"id": "gpt-5.6-terra"}, {"id": "gpt-5.6-luna"} ], "hint": "Did you mean 'gpt-5.6-terra'? Use GET https://api.tokenlab.sh/v1/models to list all available models." } } ``` ## Insufficient balance `402 insufficient_balance` can include the current balance and the estimated amount required. Your application can offer a top-up link, a less expensive model, or a smaller request. ```json { "error": { "message": "Insufficient balance: need ~$0.3500 for claude-sonnet-4-6, but balance is $0.1200.", "type": "insufficient_balance", "code": "insufficient_balance", "balance_usd": 0.12, "estimated_cost_usd": 0.35, "suggestions": [ {"id": "gpt-5.6-luna"}, {"id": "deepseek-v3-2"} ], "hint": "Try a cheaper model, or top up at https://tokenlab.sh/dashboard/billing." } } ``` ## Model unavailable A `503 all_channels_failed` or `503 delivery_tier_unavailable` does not always mean a temporary outage. If the requested operation has no supply in the selected Delivery tier, `retryable` is `false` and `retry_after` is omitted. Do not repeat the same request. Check the operation and Delivery availability with `GET /v1/models` before selecting another model. Similar names do not prove availability; unverified alternatives are omitted. ```json { "error": { "message": "This model is unavailable for the requested operation and Delivery tier.", "type": "all_channels_failed", "code": "all_channels_failed", "retryable": false, "hint": "Check the model's operation and Delivery availability with GET /v1/models. Repeating the same request will not resolve this." } } ``` ## Rate limit For `429 rate_limit_exceeded`, wait for `retry_after` seconds or use the standard `Retry-After` response header. ```json { "error": { "message": "Rate limit: 1000 rpm exceeded", "type": "rate_limit_exceeded", "code": "rate_limit_exceeded", "retryable": true, "retry_after": 8, "hint": "Retry after 8s." } } ``` ## Context too long `400 context_length_exceeded` is not fixed by sending the same request again. Shorten the input or let the user choose a model with a larger context window. ```json { "error": { "message": "This model's maximum context length is 128000 tokens...", "type": "invalid_request_error", "code": "context_length_exceeded", "retryable": false, "suggestions": [ {"id": "gemini-2.5-pro"}, {"id": "claude-sonnet-5"} ], "hint": "Reduce your input or switch to a model with a larger context window." } } ``` ## Find the right API format Read `tokenlab.accepted_request_formats` from `GET /v1/models/{model}` before using a model-specific API. | Value | Endpoint | | --- | --- | | `openai_chat_completions` | `/v1/chat/completions` | | `openai_responses` | `/v1/responses` | | `anthropic_messages` | `/v1/messages` | | `gemini_generate_content` | `/v1beta/models/{model}:generateContent` | An accepted format confirms the endpoint. Individual tools and fields can still vary by model; check the model page before depending on them. ## Find a model by task The Models API can return a current shortlist for non-chat tasks: ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=image" ``` Valid `recommended_for` values are `image`, `video`, `music`, `3d`, `tts`, `stt`, `embedding`, `rerank`, and `translation`. Send the chosen model ID explicitly in the create request. TokenLab does not silently replace it with a different model. ## Machine-readable overview Agents can read a compact API overview at: ```text GET https://api.tokenlab.sh/llms.txt ``` It includes a first request, common endpoints, model filters, and error-handling guidance. ## Handle an error without replaying the request This example makes one request, preserves the selected model, and reports structured recovery information. SDK retries are disabled so your application can decide whether a retry is safe. Present model suggestions for an explicit choice; do not replay an accepted or timed-out generation automatically. ```python import os from openai import OpenAI, APIStatusError with OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", timeout=30.0, max_retries=0, ) as client: try: response = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Reply only with OK."}], ) print(response.choices[0].message.content) except APIStatusError as exc: body = exc.body if isinstance(exc.body, dict) else {} error = body.get("error", body) if not isinstance(error, dict): error = {} print({ "status": exc.status_code, "request_id": exc.request_id, "code": error.get("code"), "hint": error.get("hint"), "suggested_model": error.get("did_you_mean"), "retry_after": exc.response.headers.get("Retry-After") or error.get("retry_after"), }) raise ``` --- ### API formats > Choose Chat Completions, Responses, Messages, or Gemini URL: https://tokenlab.sh/docs/en/guides/api-formats One TokenLab API key works with four common API formats. Keep the format your application already uses unless you need a model-specific feature. | Format | Endpoint | Choose it when | | --- | --- | --- | | Chat Completions | `/v1/chat/completions` | You use an OpenAI-compatible chat client or want broad model compatibility | | Responses | `/v1/responses` | Your application uses Responses events, background responses, or Responses tools | | Anthropic Messages | `/v1/messages` | You need Claude Messages fields such as thinking or Claude tool use | | Gemini | `/v1beta/models/:model:generateContent` | You already use Gemini `contents`, parts, files, cache, or Gemini tools | Check `tokenlab.accepted_request_formats` on the model page or in `GET /v1/models/{model}`. A model does not necessarily accept all four formats. ## Chat Completions Chat Completions is the easiest choice for existing OpenAI-compatible clients and portable chat features. ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) response = client.chat.completions.create( model="gpt-5.6-luna", messages=[{"role": "user", "content": "Hello!"}], ) print(response.choices[0].message.content) ``` Use this format for: - existing OpenAI-compatible chat integrations - standard message history and streaming - function calling supported by the selected model Fields unique to another API format are not guaranteed to work here. ## Responses Use Responses only when the selected model lists `openai_responses` in `accepted_request_formats`. ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) response = client.responses.create( model="gpt-5.6-terra", input="Explain why the sky is blue in two sentences.", ) print(response.output_text) ``` The Responses API supports create, retrieve, compact, delete, streaming, WebSocket creation and continuation, and background responses where the selected model supports them. Delete removes a stored response; it does not cancel an active response. ## Anthropic Messages Use the Anthropic SDK with the TokenLab host, without adding `/v1` to the base URL. ```python import os from anthropic import Anthropic client = Anthropic( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh", ) message = client.messages.create( model="claude-sonnet-5", max_tokens=512, messages=[{"role": "user", "content": "Hello!"}], ) print(message.content[0].text) ``` Choose Messages for Claude-specific request and response fields. Keep Claude tool calls, thinking blocks, and prompt-cache fields in this format. ## Gemini Gemini requests use the TokenLab host and the Gemini REST path. ```bash curl "https://api.tokenlab.sh/v1beta/models/gemini-3.5-flash:generateContent" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [{"parts": [{"text": "Hello!"}]}] }' ``` Choose Gemini when your application already uses Gemini content parts, function declarations, files, cached content, or other Gemini fields. Both lowerCamelCase ProtoJSON names and original snake_case proto names are accepted; avoid sending both spellings of the same field in one request. ## Do not mix formats in one conversation Messages, Responses, Chat Completions, and Gemini represent conversation state and tool calls differently. Keep one format for a conversation. If you must migrate stored history, convert and validate it in your application before sending the next request. ## Unknown fields TokenLab may pass through fields it does not recognize, but that does not mean every model accepts them. A feature is safe to depend on only when the selected model documents it and your application handles an unsupported-field error. ## Related references - [Create Chat Completion](/api-reference/chat/create-completion) - [Create Response](/api-reference/responses/create-response) - [Create Message](/api-reference/messages/create-message) - [Generate Gemini Content](/api-reference/gemini/generate-content) - [SDKs and libraries](/guides/sdks) --- ### TokenLab API skill for coding agents > Give a coding agent current TokenLab models, API examples, and error-handling guidance URL: https://tokenlab.sh/docs/en/integrations/coding-agent-skill ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and add the TokenLab Skill to my current agent: https://tokenlab.sh/docs/en/integrations/coding-agent-skill Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` The `tokenlab-api-integration` skill helps coding agents add TokenLab to an application without relying on an old model list. It covers chat, images, video, audio, translation, embeddings, rerank, music, 3D, and Worlds. The public skill is available in [tokenlab-skills](https://github.com/hedging8563/tokenlab-skills). ## Install ```bash npx skills add https://github.com/hedging8563/tokenlab-skills \ --skill tokenlab-api-integration \ -y ``` Run the same command again to update an existing installation. If your coding tool does not support the installer, copy `skills/tokenlab-api-integration/` from the repository into its shared skills or rules directory. To confirm installation, ask the agent to list its available skills and look for `tokenlab-api-integration`. ## What the skill can help with - Create a runnable example for a TokenLab API. - Read the current model list and model-specific fields. - Choose Chat Completions, Responses, Anthropic Messages, or Gemini format. - Handle model-name suggestions, rate limits, and retry timing. - Keep API keys out of prompts and source code. The skill does not replace the API reference. For exact fields and response objects, use the linked TokenLab documentation. ## Create an API key Open [Console → API keys](https://tokenlab.sh/dashboard/api?tab=keys&createKey=1&docsTopic=codingAgents), create a key, and store it in an environment variable. A TokenLab API key starts with `sk-`. ```bash export TOKENLAB_API_KEY="sk-your-api-key" ``` Never paste a key into an agent prompt, frontend code, screenshot, or public repository. Ask the agent to read it from the environment instead. ## First request ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) response = client.chat.completions.create( model="gpt-5.6-luna", messages=[{"role": "user", "content": "Hello!"}], ) print(response.choices[0].message.content) ``` Install the OpenAI SDK and run the file: ```bash pip install openai python app.py ``` ## Ask the agent to use current model data Model IDs, fields, and prices change. The skill can read: ```bash # Compact API overview curl https://api.tokenlab.sh/llms.txt # Current models curl "https://api.tokenlab.sh/v1/models" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" # Models for a non-chat task curl "https://api.tokenlab.sh/v1/models?recommended_for=image" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" # Fields and formats for one model curl "https://api.tokenlab.sh/v1/models/gpt-image-2" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` `recommended_for` supports `image`, `video`, `music`, `3d`, `tts`, `stt`, `embedding`, `rerank`, and `translation`. ## Keep the API format consistent Read `tokenlab.accepted_request_formats` from the selected model before generating code. | Format | Endpoint | | --- | --- | | Chat Completions | `/v1/chat/completions` | | Responses | `/v1/responses` | | Anthropic Messages | `/v1/messages` | | Gemini | `/v1beta/models/{model}:generateContent` | A conversation or tool call should stay in one format. Model-specific fields are not automatically converted between formats. ## Let errors guide safe changes OpenAI-compatible errors may include `did_you_mean`, `suggestions`, `hint`, `retryable`, and `retry_after`. The agent should still use the HTTP status and error `code` as the primary signal. - A wrong model ID can be corrected with `did_you_mean` after user approval when changing models matters. - `429` can be retried after `Retry-After`. - Validation, authentication, balance, and permission errors require a change; repeating the same request will not help. - A create-request timeout must be checked for an existing task before the agent sends another create request. ## Example requests for the agent ```text Use the TokenLab API skill to add image generation to this Node.js app. Read the current model details before choosing fields. Keep the API key in TOKENLAB_API_KEY and add one runnable test. ``` ```text Use TokenLab Chat Completions in this Python service. Keep the existing OpenAI SDK and change only the Base URL, API key source, and model. ``` ## Help For API or account questions, use [in-app Support](https://tokenlab.sh/dashboard/support); review the preview and submit explicitly. For a particular request, start with [Requests](https://tokenlab.sh/dashboard/runs) and follow the [investigation guide](/guides/observability-troubleshooting). Report defects in the Skill itself through [GitHub Issues](https://github.com/hedging8563/tokenlab-skills/issues). --- ### Streaming > Show model output as it arrives URL: https://tokenlab.sh/docs/en/guides/streaming ## Overview Streaming delivers model output in small events instead of waiting for the full answer. Use Responses streaming for a model that lists `openai_responses` in `accepted_request_formats`; keep Chat Completions streaming when that is what your SDK or application already expects. ## Responses streaming #### cURL ```bash cURL curl https://api.tokenlab.sh/v1/responses \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "input": "Write a short poem.", "stream": true }' ``` #### Python ```python Python import os from openai import OpenAI with OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", timeout=30.0, max_retries=0, ) as client: completed = False with client.responses.create( model="gpt-5.6-terra", input="Write a short poem.", stream=True, ) as stream: for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) elif event.type == "response.completed": completed = True elif event.type in {"response.failed", "response.incomplete", "error"}: raise RuntimeError(f"Stream ended with {event.type}") if not completed: raise RuntimeError("Stream closed before response.completed") ``` #### JavaScript ```javascript JavaScript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.TOKENLAB_API_KEY, baseURL: 'https://api.tokenlab.sh/v1', timeout: 30_000, maxRetries: 0, }); const stream = await client.responses.create({ model: 'gpt-5.6-terra', input: 'Write a short poem.', stream: true, }); let completed = false; try { for await (const event of stream) { if (event.type === 'response.output_text.delta') { process.stdout.write(event.delta); } else if (event.type === 'response.completed') { completed = true; } else if (['response.failed', 'response.incomplete', 'error'].includes(event.type)) { throw new Error(`Stream ended with ${event.type}`); } } if (!completed) throw new Error('Stream closed before response.completed'); } finally { stream.controller.abort(); } ``` Responses SSE keeps the documented event names, order, and fields. After the first event arrives, an interrupted stream is incomplete and is not restarted automatically. ## Responses WebSocket Connect to `wss://api.tokenlab.sh/v1/responses` and send `response.create` events. WebSocket responses are always streamed and do not support `background` or `response.cancel`. A `response.create` with `generate: false` returns an ID that can be continued without generating model output or creating a model charge. Each connection handles one active response at a time and lasts up to 60 minutes. Events include a response-scoped `sequence_number`. Stream events named `error` use a flat event object; connection and protocol failures use a nested `error` object. ## Chat Completions Streaming If your framework still expects SSE chunks from `/v1/chat/completions`, that also works: ```python import os from openai import OpenAI with OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", timeout=30.0, max_retries=0, ) as client: finish_reason = None with client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Write a short poem."}], stream=True, stream_options={"include_usage": True}, ) as stream: for chunk in stream: if not chunk.choices: continue choice = chunk.choices[0] if choice.delta.content: print(choice.delta.content, end="", flush=True) if choice.finish_reason: finish_reason = choice.finish_reason if finish_reason != "stop": raise RuntimeError(f"Stream ended without a complete text answer: {finish_reason}") ``` ## Gemini Streaming `POST /v1beta/models/{model}:streamGenerateContent?alt=sse` returns Gemini chunks. An event can contain only metadata, intermediate events may omit `finishReason`, and the stream can end naturally without a Chat Completions `[DONE]` marker. ## Stream End Conditions Typical completion conditions: - `response.completed` for Responses API streams - `finish_reason: "stop"` for Chat Completions streams - `finish_reason: "length"` when a token limit is hit - tool/function call events when the model wants to use tools ## Web App Pattern Run the SDK stream consumer on your server. The browser should call your own authenticated backend; keep the TokenLab key in the server environment. Forward text deltas to the browser and cancel the upstream stream when the user stops or disconnects. Use the SDK to parse SSE events: one network chunk is not necessarily one complete event. ## Handle streams well #### Match the API your app already understands Use Responses streaming when the selected model and your client support it. Existing Chat Completions streaming code can stay on `/v1/chat/completions`. #### Flush output incrementally Append delta chunks to the UI or terminal as they arrive rather than waiting for the full response. #### Treat disconnects as incomplete responses Preserve the partial output if it is useful, but do not mark it as complete. Repeating a request may produce different output and another charge. --- ### Handle API errors > Read error codes, retry only when useful, and keep the Request ID URL: https://tokenlab.sh/docs/en/guides/error-handling Handle errors by HTTP status and `code`. The `message` is written for people and can change without notice. Chat Completions and Responses use an OpenAI-style `error` object. Anthropic Messages and Gemini keep their own error formats, so do not use one parser for every TokenLab API. ```json { "error": { "message": "Human-readable description", "type": "error_type", "code": "error_code", "param": "parameter_name", "retryable": true, "retry_after": 30 } } ``` Only `message` and `type` are always present in OpenAI-compatible errors created by TokenLab. Other fields appear when they are relevant. ## Status codes | Status | Meaning | Typical action | | --- | --- | --- | | `400` | A field, model ID, or input is invalid | Fix the request; do not repeat it unchanged | | `401` | API key is missing, invalid, expired, or revoked | Replace the key | | `402` | Balance or API-key limit is too low | Top up, raise the limit, or reduce the request | | `403` | This key cannot use the resource or model | Change the key's permissions or model | | `404` | Resource does not exist or is no longer available | Check the ID and the API key that created it | | `413` | Request or uploaded file is too large | Reduce the input to the documented model or endpoint limit | | `429` | Request limit reached | Wait for `Retry-After` | | `500`–`504` | Service unavailable or network failure | Retry only when `retryable` is `true`; respect `retry_after` and limit attempts | ## Common error codes | Code | What it means | What to change | | --- | --- | --- | | `invalid_api_key` | The API key is missing, invalid, inactive, or revoked | Check the Authorization header and key value | | `expired_api_key` | The key has expired | Create or select an active key | | `insufficient_balance` | The account balance cannot cover the request | Add funds, reduce the request, or choose a lower-priced model | | `quota_exceeded` | The API key reached its own limit | Increase that key's limit or use a different authorized key | | `model_not_allowed` | The key cannot use the requested model | Update the key's model list or choose an allowed model | | `model_not_found` | The model ID is unknown or unavailable | Read `/v1/models` and use a current model ID | | `context_length_exceeded` | Input is longer than the model accepts | Remove history or choose a model with a larger context window | | `missing_required_parameter` | A required field is missing. `param` names it, for example `model` on audio and image requests, which have no default model | Send the field that `param` names | | `invalid_value` | A field has a value that the model does not accept, for example a `reasoning_effort` value. The message lists the accepted values | Use one of the listed values | | `invalid_image_url` | TokenLab cannot use an image URL: it is not a valid HTTP(S) URL or image data URI, or the image host did not return the image. `param` names the field | Send a public HTTPS image URL or a supported data URI | | `unsupported_model_capability` | No route of the selected Delivery tier supports a request parameter, such as the operation, `resolution`, `aspect_ratio`, `duration`, an input media field, `web_search_options`, or `max_tokens`. The message names each parameter that did not match and, when the model publishes them, the supported values | Remove or change the parameter, or choose another Delivery tier or model; do not repeat the request unchanged | | `unpriceable_selection` | The video request leaves out a selector that its price depends on, so TokenLab cannot price it. `param` names the selector | Set the field that `param` names | | `model_pricing_unavailable` | HTTP 503. The request is valid, but TokenLab cannot price this combination of model and operation on the routes the request can use. The request was not charged, and `retryable` is `false` | Do not repeat the request unchanged; choose another model or operation, or contact support with the Request ID | | `rate_limit_exceeded` | Too many requests were sent in the current window | Wait for `Retry-After` | | `payload_too_large` | The request body or file exceeds the endpoint limit | Reduce or compress the input | | `all_channels_failed` | The selected model cannot serve this request | Retry only when `retryable` is `true`; respect `retry_after` and limit attempts | | `timeout_error` | The request did not finish in time | Retry only when the operation is safe to repeat | A `503 all_channels_failed` or `503 delivery_tier_unavailable` does not always mean a temporary outage. If the requested operation has no supply in the selected Delivery tier, `retryable` is `false` and `retry_after` is omitted. Do not repeat the same request. Check the operation and Delivery availability with `GET /v1/models` before selecting another model. Similar names do not prove availability; unverified alternatives are omitted. Some OpenAI-compatible errors include optional `did_you_mean`, `suggestions`, `alternatives`, `hint`, `retryable`, or `retry_after` fields. See [Errors agents can act on](/guides/agent-first-api). When a request ran on an Official route and the upstream service rejected the request itself, such as an input it does not accept or a content policy decision, the error also carries `upstream`: the upstream's `message` as reported, plus `code` and `source` (the upstream service name) when known. Anthropic Messages and Gemini errors carry the same object inside their own `error`. Keep branching on `code` and `type`; `upstream.code` values are defined by the upstream service and may change. ## Retry decisions | Error | Repeat the same request? | | --- | --- | | `400`, `401`, `402`, `403`, `404`, `413` | No. Change the request, credentials, balance, permissions, or input. | | `429` | Yes, after the server-provided delay. | | `500`–`504` | Retry only when `retryable` is `true`; respect `retry_after` and limit attempts | | Connection closed before any response | Sometimes. For create operations, check whether a task or side effect already exists. | | Stream interrupted after output arrived | Do not call it a complete response. Repeating may generate different output or a second charge. | For image, video, music, 3D, and Worlds creation, save the task ID as soon as it is returned. If a create request times out, check the task record before sending another create request. ## Keep the Request ID Response headers include a Request ID for tracing. Save it with the endpoint, model, time, and your own user or job ID. For async work, also save `task_id` and `billing_transaction_id` when present. When contacting support, include those IDs and a redacted example. Never send API keys, management tokens, private media, signed URLs, or complete private prompts. [From a request to an investigation and support](/guides/observability-troubleshooting) --- ### Rate limits > Find your request limit and recover from a 429 response URL: https://tokenlab.sh/docs/en/guides/rate-limits Your account tier determines how many API requests you can send each minute. When you reach the limit, the API returns `429 rate_limit_exceeded` and a `Retry-After` header. The limits below are the standard tier values and may change with the active configuration. For authenticated requests, these limits are enforced per API key. Public discovery endpoints such as `GET /v1/models` have separate limits. On a 429, use the returned `X-RateLimit-Limit` and `Retry-After` values instead of assuming a copied table is the active limit. ## Account limits | Tier | Requests per minute | | --- | ---: | | **User** | 1,000 | | **Partner** | 10,000 | | **VIP** | 10,000 | Need a different limit? Email support@tokenlab.sh with your account email, expected request volume, and use case. ## 429 response ```json { "error": { "message": "Rate limit exceeded. Please retry later.", "type": "rate_limit_exceeded", "code": "rate_limit_exceeded", "retryable": true, "retry_after": 8 } } ``` The standard response header gives the same wait time in seconds: ```http Retry-After: 8 ``` ## Retry after the server delay Use `Retry-After` when it is present. If it is missing, use exponential backoff with random jitter. Always set a retry limit so a busy queue cannot grow forever. ```python import random import time from openai import RateLimitError def chat_with_rate_limit_retry(client, model, messages, attempts=4): for attempt in range(attempts): try: return client.with_options(max_retries=0).chat.completions.create( model=model, messages=messages, ) except RateLimitError as exc: if attempt == attempts - 1: raise header = exc.response.headers.get("Retry-After") wait = float(header) if header else min(30, 2 ** attempt + random.random()) time.sleep(wait) ``` ## Keep traffic below the limit - Queue requests when your application can generate large bursts. - Limit concurrency as well as the average requests per minute. - Cache only results that are safe and useful to reuse. - Do not retry validation, authentication, balance, or permission errors. - Track repeated `429` responses by API key so one client cannot crowd out the rest of your application. A faster model does not increase your account's request limit. Model speed, token limits, and account rate limits are separate constraints. --- ### Billing and pricing > How charges, estimates, balances, and limits work URL: https://tokenlab.sh/docs/en/guides/billing One TokenLab balance works across models. There is no subscription or minimum spend; charges are based on the model and usage recorded for each request. ## Delivery options The same model and API are available through two routes: Verified costs less, and Official is more reliable. The delivery option decides which route runs a request and which price you pay. | Option | Route | Price | If the route is unavailable | |---|---|---|---| | `Verified` | Verified only: routes tested by TokenLab | Verified price, lower than the Official price on most models | The request fails and is not charged | | `Official` | Official only: the model maker or a major platform | Official price, the model maker's published price | The request fails and is not charged | | `Auto` (default) | Verified first. Official runs the request once if Verified has no supply or fails before any output | The price of the route that completed the request | Official runs the request | The Models page shows both prices for each model. - Each completed request is charged once. - Auto does not switch after output starts or after a task is accepted. - Before a run, Auto holds the higher of the two possible amounts. After the run, you pay the actual charge and the rest is released. - The `X-TokenLab-Delivery-Policy` request header overrides the API key setting, and the API key setting overrides the Workspace default. See the [API introduction](/api-reference/introduction). For paid generation, Console shows the maximum estimate before confirmation. After completion, Runs and Usage show the final charge and the option that completed the request. ## Pricing Models Model prices can change. Check Console, the Models page, `GET /v1/models/:model/pricing`, or the [Pricing API](/api-reference/pricing/get-pricing) for the current price. On the Models page, a dash in the Verified price or discount column means no Verified offer is currently available; it does not mean free. Models with Official supply remain available through Official or Auto. In the API, the Verified price fields of such a model are `null` or omitted, and the `official_*` price fields show what you pay. Delivery labels describe supported options, while availability is checked when each request runs. Series prices summarize models with supply in the relevant delivery option and keep different billing units separate. ### Token-Based Pricing Most chat, reasoning, embedding, rerank, and some image models are priced by input, output, cache, or image-output tokens. | Pricing family | Examples | Verify current price | |---|---|---| | Chat / Responses | `gpt-5.4`, `claude-sonnet-4-6`, `gemini-3.5-flash` | Models page or Pricing API | | Embeddings / rerank | `text-embedding-3-small`, `qwen3-vl-rerank` | Model pricing detail | | Token-priced images | `gpt-image-2` | `GET /v1/models/gpt-image-2/pricing` | > Note: Do not hard-code a copied price table. Read the current price when your application needs to display or compare costs. ### Tool fees Some built-in tools cost extra on top of tokens. TokenLab charges a tool fee only when the model uses the tool and the response reports it. A request that offers a tool the model does not use has no tool fee. | Tool | Models | Charge | |---|---|---| | Web search | OpenAI, Claude and Grok models, and Gemini 3 models with Google Search grounding | Each search the model runs. For Gemini 3, each unique search query | | Search grounding | Gemini 2.5 models with Google Search grounding | Once for each request that the search grounded | - Each model has its own tool fee for Verified and Official, the same as its token prices. The model's pricing details show it as **Web search** or **Search grounding**. - Search results that the model reads are billed as input tokens at the model's price. - A tool without a price on a model is not charged. - In Console, open a request in Runs: **Tool fees** shows the number of calls and the price of each call. The API key usage records show the same values in `charged_price_basis`: the count in `measure.dimensions.webSearchCalls` or `groundedPrompts`, and the price of each call in `effectiveUnitPrices.webSearch` or `effectiveUnitPrices.groundedPrompt`. ### Prompt caching A model can read the repeated start of a prompt from a cache. Tokens read from the cache cost less than input tokens. - GPT, Gemini and some other models cache long repeated prompts automatically. You do not set anything. - Claude models cache only when you ask. Send `"anthropic_options": {"prompt_caching": true}` in a [Chat Completions](/api-reference/chat/create-completion) request. TokenLab marks the end of the system prompt and the latest user message as cache points. With the [Messages API](/api-reference/messages/create-message), add `cache_control` to the blocks yourself. | Charge | Claude price (typical) | |---|---| | Cache write, 5 minutes (default) | 1.25 × input price | | Cache write, 1 hour (`"prompt_caching_ttl": "1h"`) | 2 × input price | | Cache read | 0.1 × input price | - Cache prices differ by model and delivery option. Check the model on the Models page before you rely on a ratio. - One cache read within the cache lifetime pays back the write. A prompt that never repeats costs a little more with caching on. - A prompt shorter than the model's minimum (512 to 4,096 tokens for Claude, depending on the model) is not cached and has no cache write charge. - If the serving route does not support a one-hour cache, the five-minute cache is used. - The response shows the result: `usage.prompt_tokens_details.cached_tokens` is the number of tokens read from the cache, and `usage.cache_creation_input_tokens` is the number written to it. ### Service tier TokenLab runs and bills requests at the standard service tier. `service_tier` values `auto` and `default` are accepted. Other values, such as `flex`, `priority`, `fast`, and `ultrafast`, are ignored: the request runs at the standard tier and is charged the standard price. ### Request and Task Pricing Image, video, music, 3D, audio, and world models may be priced per request, image, second, minute, or task. The model page shows the billing unit. | Family | Examples | |---|---| | Image | `flux-pro-1.1` | | Video | `veo3.1`, `seedance-2.0` | | Music | `suno-music` | | 3D | `tripo-h3.1` | | Audio | `grok-tts`, `whisper-large-v3` | ### Async task billing > Note: Some tasks reserve the estimated cost when they are created. A completed task is charged once; a failed task releases or refunds the pending amount. Video, music, 3D, and task-based image generation return a task ID and `poll_url`. The final amount is recorded after the task becomes `completed` or `failed`. A completed task includes `billing_transaction_id` after billing; a failed task is not charged. If Usage still does not show the final charge or released amount after the task finishes, contact support@tokenlab.sh with the Request ID and task ID. ### Billing transaction IDs Non-streaming OpenAI-compatible responses include `billing_transaction_id` when billing finishes before the response is sent. The same value may appear in `X-Billing-Transaction-ID`; Gemini and other native formats may expose it only in this header. Async tasks include `billing_transaction_id` after completion. Streaming may finish before billing does, so use Usage when the header is absent. ## Token counts Text models usually report input, output, and total tokens in `usage`. Tokenization differs by model, language, and media input, so character-count formulas are not reliable billing estimates. Use the model's current price and the final Usage record. ## Find your usage Open [Usage](https://tokenlab.sh/dashboard/usage) to find: - Real-time balance - Usage history by model - Cost breakdown - API key usage ### API response Token-based responses usually include usage information: ```json { "usage": { "prompt_tokens": 50, "completion_tokens": 100, "total_tokens": 150 } } ``` ## Spend less #### Compare current model prices Test a few models on the same task and compare the final cost in Usage. The lowest price per token is not always the lowest cost per completed task. #### Use prompt caching Keep the long, stable part of the prompt (instructions, documents, tool definitions) at the start, and turn on [prompt caching](#prompt-caching) for Claude models. Cache responses in your application for requests that repeat exactly. #### Optimize prompts Keep prompts concise while maintaining clarity. #### Set max_tokens Limit response length when full responses aren't needed. #### Use streaming for long responses Streaming shows content earlier but does not reduce the number of billed tokens. ## Low-balance alerts Set a threshold in **Console → Settings**. TokenLab emails you when the balance falls below it. ## Add balance Open **Console → Billing**, choose an amount, and use one of the payment methods shown at checkout. The balance updates after the payment provider confirms the payment. ## API Key Limits You can set a spending limit when you create or edit an API key in **Console → API Keys**. When the limit is reached, requests with that key will return `402 Payment Required`. ## Billing help Contact support@tokenlab.sh for billing inquiries. --- ### Async jobs and polling > Create media tasks and wait for the finished result URL: https://tokenlab.sh/docs/en/guides/async-jobs-polling Many media endpoints return a task instead of a finished file. Save the returned task ID and use `poll_url` until the status is `completed` or `failed`. Async terminal task snapshots have a lifecycle separate from the 30-day request/response capture window. Generated image, video, and audio result URLs are TokenLab media-result URLs that stay valid for 30 days. The response's `media_retention` fields report each item's saving status and `expires_at`; they can be absent when the saving status is not known in time, and the URL still works. Failed and in-progress tasks return empty media URL fields. See [Data retention](/guides/data-retention). ## Fields to save Create responses can include: | Field | Meaning | What to do | |-------|---------|------------| | `id` | TokenLab task ID | Store it with your own job record | | `task_id` | Another name for the same task ID | Treat it as equivalent to `id` | | `status` | Current task status | Keep checking until it finishes or fails | | `poll_url` | Preferred status URL | Use this first when present | | `model` | Model used for the task | Store it with the task record | Use `poll_url` when it is returned. If your client needs a fixed URL, use `/v1/tasks/{id}`. ## Save the task and check its status Save `id` or `task_id` as soon as the create request succeeds. Store `poll_url`, model, endpoint, and your own user or job ID with it. For long media jobs, checking every 5–10 seconds is usually enough. Stop when the status becomes `completed` or `failed`. A completed task contains the media result; a failed task contains the error you can show or log. Retrying a failed generation creates a new task and may create a new charge. ```json { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "model": "veo3.1" } ``` ## Polling Example ```bash curl "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` Statuses are `pending`, `processing`, `completed`, and `failed`. A cancelled task uses `failed` together with `cancelled: true` and `cancellation_status: "cancelled"`. A successful status read returns HTTP 200 even when the task has failed. Use the task's `status` to decide whether generation succeeded. The `error` field retains its string or object shape. Failed tasks may also include `error_details` with `status` (the business error status), `type`, `code`, `message`, `param`, and `retryable`. For example, `error_details.status: 400` with `param: "size"` means the request needs correction; it does not mean the status request itself failed. `param` is omitted when no specific field is known. When `error_details.projection_version` is `2`, `message` is the final reason for the failure, usually the upstream service's own explanation, such as a parameter limit or a content policy decision. For a task that ran on an Official route, `error_details.upstream` also carries the upstream error as reported: `message`, plus `code`, `param`, and `source` (the upstream service name) when known. Keep branching on `code` and `type`; `upstream.code` values are defined by the upstream service and may change. Errors that reject the create request before a task is accepted use the normal HTTP error status and structured `error` object. Repeating the same idempotent create request preserves that rejection. A failed status read does not change the task's terminal state. ## Avoid duplicate tasks Most duplicate generations come from retrying a create request after a timeout. | Where timeout happens | Safer behavior | |-----------------------|----------------| | Before your server receives a create response | Check the `request_id`; retry only if no task was created | | After a create response was stored | Resume polling the stored `task_id` | | During polling | Retry the status request with backoff | | After terminal status | Do not poll again unless the user explicitly refreshes the record | Do not send a second create request just because the browser refreshed or a status poll failed. ## Billing records An async task may reserve its estimated cost when it is accepted. The final amount is recorded after the task finishes or fails. Status responses may include `billing_transaction_id` and the `X-Billing-Transaction-ID` header. Keep these identifiers together: - `request_id` from the create request. - `task_id` / `id` from the task. - `billing_transaction_id` when present. - Your own user ID, project ID, or job ID. ## Cancellation `DELETE /v1/tasks/{id}` can cancel supported Seedance video tasks while they are still queued, including `seedance-1.5-pro`, `seedance-2.0`, and `seedance-2.0-fast`. Unsupported tasks return `400 unsupported_task_cancel`. A task that is already running or finished returns `409 task_not_cancellable`. Cancellation is a request, not a guarantee that work already in progress will stop. ## Troubleshooting | Symptom | Likely cause | What to check | |---------|--------------|---------------| | `404 async_task_not_found` | The task has expired or is no longer available | Check the saved `task_id` and `poll_url` | | `403 task_not_owned` | Task ownership cannot be confirmed for the current workspace | Confirm the workspace or organization associated with the API key | | Task never appears to finish | Client keeps polling the wrong URL or stopped before terminal status | Use `poll_url` or `/v1/tasks/{id}` and inspect the latest status | | Final media URL missing | Task is not completed, or it finished without a usable file | Keep polling until it finishes, then treat a missing result as failed | | User sees duplicates | Retry path created a new task after timeout or refresh | Deduplicate by your own job ID and stored `task_id` | | Billing mismatch | Final billing is not recorded yet, or the wrong IDs are being compared | Compare `request_id`, `task_id`, and `billing_transaction_id` | ## What to send support Include `request_id`, `task_id`, `billing_transaction_id` when present, endpoint, model, time, and the names of fields you sent. Never send API keys, private media, signed URLs, or full prompts unless support specifically asks for a redacted example. ## API Reference | Topic | Reference | |-------|-----------| | Get Task Status | [Get Task Status](/api-reference/tasks/get-task-status) | | Cancel Task | [Cancel Task](/api-reference/tasks/cancel-task) | | Image Generation | [Image Generation](/guides/image-generation) | | Video Generation | [Video Generation](/guides/video-generation) | | Music Generation | [Music Generation](/guides/music-generation) | | 3D Generation | [3D Generation](/guides/3d-generation) | | Billing & Pricing | [Billing & Pricing](/guides/billing) | Configure task notifications with the [Webhook management API](/guides/webhooks), authenticated with an `mt-…` Management Token. MCP `full` uses `TOKENLAB_MANAGEMENT_TOKEN`. Stop polling terminal tasks and on 401/403/404 or non-retryable errors. --- ### Webhook management API > Configure task notifications through Dashboard, API or MCP, verify signatures, and troubleshoot delivery. URL: https://tokenlab.sh/docs/en/guides/webhooks Webhooks notify your app when an asynchronous task completes, fails or times out. Configure them in [Dashboard → API → Webhooks](https://tokenlab.sh/dashboard/api?tab=webhooks), through the Management API, or through TokenLab MCP's `full` profile. They share the same workspace endpoints and delivery history. ## Which credential to use | Credential | Purpose | Where it is used | | --- | --- | --- | | Management Token `mt-…` | Create, list, update, delete, test and rotate webhooks | `Authorization: Bearer mt-…` on `/v1/management/webhooks*` | | API key `sk-…` | Submit model requests and query task status | Model APIs and `/v1/tasks/{id}` | | Signing secret `whsec_…` | Verify that a received notification is authentic | Your receiver; never use it as an API Bearer token | Create a Management Token at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens). Tokens are workspace scoped: choose the same workspace as the API key submitting tasks. A token cannot read or modify another workspace's endpoints. API keys and signing secrets are rejected by the Management API. Dashboard configuration uses your signed-in workspace admin session. Store `mt-…` and `whsec_…` on your backend. Do not embed them in browser code, prompts, repository files or URLs. Management Tokens also authorize other workspace management operations; they are not webhook-only credentials. ## Configure an endpoint ```bash export TOKENLAB_MANAGEMENT_TOKEN="mt-your-management-token" curl https://api.tokenlab.sh/v1/management/webhooks \ -H "Authorization: Bearer $TOKENLAB_MANAGEMENT_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"url":"https://your-app.example/webhooks/tokenlab","events":["task.completed","task.failed","task.timeout"],"description":"Production task results"}' ``` The `201` response includes `id` and a one-time `secret` beginning with `whsec_`. Store the secret before leaving this step. List/get/update responses never expose it. Up to 10 endpoints can be configured per workspace. URLs must use public HTTPS and cannot contain credentials, query strings or fragments. Redirects are not followed. | Operation | Endpoint | | --- | --- | | [List](/api-reference/management/list-webhooks) / [Create](/api-reference/management/create-webhook) | `GET` / `POST /v1/management/webhooks` | | [Get](/api-reference/management/get-webhook) / [Update](/api-reference/management/update-webhook) / [Delete](/api-reference/management/delete-webhook) | `GET` / `PATCH` / `DELETE /v1/management/webhooks/{webhookId}` | | [Rotate signing secret](/api-reference/management/rotate-webhook-secret) | `POST /v1/management/webhooks/{webhookId}/rotate-secret` | | [Send test](/api-reference/management/test-webhook) | `POST /v1/management/webhooks/{webhookId}/test` | | [Delivery history](/api-reference/management/list-webhook-deliveries) | `GET /v1/management/webhooks/{webhookId}/deliveries?page=1&limit=50` | Pause with `PATCH {"is_active":false}`; resume with `PATCH {"is_active":true}`. Resuming resets the consecutive failure count. Rotation returns a new secret once; configure it on your receiver immediately. Requests already in flight may still carry the previous signature, so briefly accept both secrets during rotation. Rotation is not reversible. ## Events and payloads Subscriptions cover future terminal async task events; synchronous results and historical tasks are not replayed. Receive all workspace tasks for the selected event types and match `data.taskId` to the ID returned when you created the task. Callback fields use camelCase; management response fields use snake_case. ```json { "id": "event-unique-id", "type": "task.completed", "created": 1790000000, "data": { "taskType": "video", "taskId": "ldtask_0123456789abcdef0123456789abcdef", "model": "your-selected-model", "durationMs": 42000, "resultUrls": ["https://api.tokenlab.sh/v1/media-results/7c9e6679-7425-40de-944b-e07fc1f90ae7.mp4?token=…"], "settledCost": 0.12 } } ``` | Event | Data | | --- | --- | | `task.completed` | `taskType`, `taskId`, optional `model`, `durationMs`, `resultUrls`, `settledCost` | | `task.failed` | `taskType`, `taskId`, `error`, `errorCode`, `retryable`, `refundOutcome` | | `task.timeout` | `taskType`, `taskId`, `expectedTimeoutMs`, `actualWaitMs`, `refundIssued`, `resolutionSource`, `refundOutcome` | | `webhook.test` | Test message and configured subscriptions; sent only by the test operation | Each `resultUrls` entry is a TokenLab media-result URL that stays valid for 30 days. See [Data retention](/guides/data-retention). For `task.timeout`, `expectedTimeoutMs` is the configured timeout threshold and `actualWaitMs` is the elapsed wait, both in milliseconds. `refundIssued: true` means the refund succeeded or had already been issued; `false` does not rule out a later refund. `refundOutcome` describes the refund processing result. Timeout events do not contain `durationMs`. Fields depend on the task and can be absent. Use `GET /v1/tasks/{id}` with the original workspace's `sk-…` key for the authoritative result and billing status. An event's `retryable` describes the generation failure; it is not an instruction to poll a terminal task indefinitely or automatically submit a new billable task. In `task.failed`, `error`, `errorCode`, and `retryable` are the task's public `error_details.message`, `code`, and `retryable`, the same values a status read returns. ## Verify before processing Each POST includes `X-Webhook-ID`, `X-Webhook-Timestamp` (Unix seconds) and `X-Webhook-Signature` (`sha256=`). Compute HMAC-SHA256 over the exact timestamp string, a period, and the **raw request body bytes**, using the complete `whsec_…` secret. Do not parse and reserialize JSON before verification. ```js import { createHmac, timingSafeEqual } from 'node:crypto'; export function verifyWebhook(rawBody, headers, secrets) { const timestamp = headers['x-webhook-timestamp']; const signature = headers['x-webhook-signature']; if (typeof timestamp !== 'string' || !/^\d+$/.test(timestamp)) return false; if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; if (typeof signature !== 'string' || !/^sha256=[a-f0-9]{64}$/.test(signature)) return false; const received = Buffer.from(signature.slice(7), 'hex'); return secrets.some(secret => { const expected = createHmac('sha256', secret).update(timestamp + '.').update(rawBody).digest(); return timingSafeEqual(expected, received); }); } ``` After verification, check the body `id` matches `X-Webhook-ID`, persist the event ID with your work item atomically, and return `2xx` quickly. Process heavy work from your own queue. Deduplicate by event ID within the receiving endpoint: deliveries can repeat and ordering is not guaranteed. A five-minute timestamp tolerance limits replay age; ID deduplication prevents repeated processing within that window. ## Retries and recovery Each delivery cycle makes up to three attempts, waiting 1 second then 4 seconds. Each HTTP attempt has a 10-second timeout and a fresh timestamp/signature. Network failures, `429`, and `5xx` are retryable. Other `4xx`, redirects and invalid network targets stop that cycle without retrying. Transient failures can trigger later event retries with the same delivery ID. Ten consecutive failed cycles automatically pause the endpoint. Use the test operation and delivery history to see `outcome`, `http_status`, `attempts` and `delivered_at`. A successful test API response (`200`) only means the attempt was recorded: check `outcome == "delivered"`. Fix the receiver, resume the endpoint, then send another test. History contains delivery metadata, not payloads; manual replay of old events is not available. Keep task IDs and reconcile with task status if your receiver was paused or unavailable. ## MCP configuration Webhook management tools are available in the `full` profile. The management credential is separate from the inference key; catalog and webhook management can work without an inference key. ```json { "mcpServers": { "tokenlab": { "command": "npx", "args": ["-y", "@tokenlabai/mcp-server@latest"], "env": { "TOKENLAB_MCP_TOOL_PROFILE": "full", "TOKENLAB_MANAGEMENT_TOKEN": "mt-your-management-token", "TOKENLAB_API_KEY": "sk-your-inference-key" } } } } ``` Tools: `list_webhooks`, `create_webhook`, `get_webhook`, `update_webhook`, `delete_webhook`, `rotate_webhook_secret`, `test_webhook`, `list_webhook_deliveries`. Never paste credentials into a tool argument. Grant management access only to trusted MCP clients. ## Polling fallback Webhooks do not remove status-query access or impose a new polling limit. If you poll, use the creation response's `poll_url`, back off while pending, and stop on terminal states. Stop on `401`, `403`, `404`, or `error.retryable == false`; retry transient `503 async_task_owner_unavailable` with backoff. A missing or expired task returns `404 async_task_not_found`. Repeatedly querying a missing task cannot recreate it. The [Seedance compatibility API's `callback_url`](/api-reference/video/create-volc-compatible-seedance-task) is a separate per-request callback contract. It can be set when creating that task; it does not use these workspace event payloads or HMAC headers. --- ### Logs and troubleshooting > Keep the IDs needed to trace requests, tasks, and charges URL: https://tokenlab.sh/docs/en/guides/observability-troubleshooting Keep TokenLab request, task, and billing IDs in your logs. They connect a user action to its result and charge without storing the prompt or private media. In the Console, captured request and response details are available for 30 days from capture, with access ending at the exact expiry even if physical cleanup is still queued. Request identity and billing/usage history have independent lifetimes. See [Data retention](/guides/data-retention). ## From a request to an investigation and support 1. Open [Requests](https://tokenlab.sh/dashboard/runs) in the workspace that owns the request. Find its Request ID and select the row. 2. Choose **Open full details** for a dedicated page. **Back to requests** restores the list filters. A link never grants access to another workspace; use an account authorized for that workspace. 3. For pending or unknown status, refresh progress first. For a failure or interrupted response, choose **Investigate the issue** when available. This starts an investigation for that request in the in-app Agent; it does not replay the original model request. Successful requests do not need an investigation by default. 4. If you still need help, choose the human-support action from the request or investigation. It opens a preview with the linked request and conversation when available. Review the summary and remove private content, then explicitly select **Submit**. An investigation or a preview alone does not send a support request. 5. After submission, keep the confirmation and continue the same conversation in [Support](https://tokenlab.sh/dashboard/support). Reuse it for replies and extra information instead of submitting duplicates. ## IDs to keep | Identifier | Where it appears | Use it for | |------------|------------------|------------| | `request_id` | Error bodies, dashboard logs, usage records | Request-level support and reconciliation | | `id` / `task_id` | Async create and status responses | Polling image, video, music, and 3D jobs | | `poll_url` | Async create responses | Preferred status URL | | `billing_transaction_id` | Non-streaming responses when settled, async task status responses, usage records, `X-Billing-Transaction-ID` | Cost reconciliation | | `X-Task-ID` | Async task response headers | Header-level task correlation | | Your own job/user ID | Your application | Finding the TokenLab request for a user action | Customer-facing records should use TokenLab IDs. Never expose private service URLs, cache keys, or diagnostic metadata. ## What To Log Log enough to diagnose the request without leaking secrets: - Endpoint, HTTP method, model, status code, timestamp, and latency. - Public identifiers: `request_id`, `task_id`, `poll_url`, and `billing_transaction_id` when present. - Field names sent in the request, not the full prompt or private media content. - Final async status responses and safe error fields. - Client retry count and whether the retry created a new task or resumed an existing one. Always redact `Authorization`, API keys, management tokens, signed URLs, private media URLs, full prompts, and user personal data unless you have explicit permission to retain them. ## Troubleshooting Matrix | Symptom | First check | Useful page | |---------|-------------|-------------| | `401` or `403` | API key, management token, organization access, key scope | [Authentication](/authentication) | | `402` | Balance, API key spend limit, model price availability | [Billing & Pricing](/guides/billing) | | `429` | Account tier, endpoint rate limit, retry behavior | [Rate Limits](/guides/rate-limits) | | `400 invalid_request_error` | Unsupported field, wrong endpoint, missing required field, or model request mismatch | [Error Handling](/guides/error-handling) | | Async task cannot be found | Wrong API key, mistyped ID, or an expired task | [Async Jobs & Polling](/guides/async-jobs-polling) | | Cost does not match UI | Settlement timing or comparing the wrong identifier | [Billing & Pricing](/guides/billing) | ## Usage Reconciliation Use the Management API for server-side reconciliation: ```bash curl "https://api.tokenlab.sh/v1/management/api-keys/key_abc123def456/usage?page=1&limit=20&scene=video" \ -H "Authorization: Bearer mt-your-management-token" ``` `GET /v1/management/api-keys/{keyId}/usage` can filter by `scene`, `model`, `modelVendor`, `startDate`, and `endDate`. Use these records instead of scraping Console pages or calculating charges from response token counts alone. Streaming responses may settle after the stream is sent, so a billing header can be absent even when usage is later recorded. Async media tasks may settle after terminal polling. ## What to send support When contacting support, include: - `request_id`. - `task_id` and `poll_url` for async work. - `billing_transaction_id` when present. - Endpoint, method, model, timestamp, and status code. - Request field names and the safe error body. - Your expected result and what the user actually saw. Do not include API keys, management tokens, private media, full prompts, private service URLs, or diagnostic identifiers. If support needs a request example, send a redacted copy. ## Checks worth automating - Alert on repeated `401`, `402`, `429`, and `5xx` responses separately; each needs a different response. - Track async jobs that remain non-terminal longer than your product SLA. - Track duplicate create attempts for the same user job ID. - Sample completed jobs and verify the user-visible asset, usage record, and stored task record agree. ## API Reference | Topic | Reference | |-------|-----------| | Error Handling | [Error Handling](/guides/error-handling) | | Rate Limits | [Rate Limits](/guides/rate-limits) | | Billing & Pricing | [Billing & Pricing](/guides/billing) | | Get API Key Usage | [Get API Key Usage](/api-reference/management/get-api-key-usage) | | Get Task Status | [Get Task Status](/api-reference/tasks/get-task-status) | --- ### Data retention > How long you can access request details and media, and how to keep important results URL: https://tokenlab.sh/docs/en/guides/data-retention Request and response details, and automatically saved media results, are available for 30 days. Conversation history, billing records, saved Media Assets, and files you download are handled separately. See [TokenLab data retention](https://tokenlab.sh/data-retention) for the full explanation. ## Request and response details Saved request and response content is available in Console, Request Debugger, and TokenLab Agent (Assistant) for 30 days from when it is recorded. After expiry, these tools can no longer retrieve that original content. Request summaries, usage, and billing records may still be available. Download any details you need before they expire. An expired capture is different from content that was never saved or could not be loaded. Check the displayed status when troubleshooting. ## Generated media TokenLab automatically saves copies of supported image, video, audio, and downloadable 3D results (including Worlds files) returned as HTTP(S) links. Every media URL you receive for these results is a TokenLab URL: in API responses, task status reads, the `task.completed` webhook `resultUrls`, and the Seedance-compatible callback `content.video_url` and `content.last_frame_url`. ```text https://api.tokenlab.sh/v1/media-results/{id}.{ext}?token=… ``` The extension matches the media type, for example `png`, `mp4`, `mp3`, or `glb`. The URL stays valid for 30 days from when the result is first recorded. Viewing it again, retrying, or checking task status does not restart that period. `stream_audio_url` is a provider link for listening while the track is made. It is not a TokenLab media result and stops working when the track is finished. The URL works as soon as you receive it. While TokenLab is still saving its copy, the URL streams the file from the source through TokenLab. When the copy is ready, the URL serves the copy. If saving fails, the URL streams from the source for up to 24 hours after the result was created, then returns HTTP 410. For longer access, download the file or save it to Media Assets while it is available. A copy saved to Media Assets does not expire with the automatic 30-day copy. Automatic saving does not cover interactive Worlds pages, externally referenced model dependencies, inline Base64 or binary media, input files, or links within generated text. Save these results yourself if you need them later. Task records have a separate lifetime; a completed task does not guarantee its media is still available. ### Checking media availability through the API AI-protocol JSON responses include `media_retention` for supported results: `days` and `items`, with each item reporting `url`, `kind`, `status`, and `expires_at`. If the saving status is not known within the response time limit, the response omits `media_retention`; the media URL still works. You can also add `metadata=true` to a media-result URL to check its current status. A `saving` or failed status does not mean the file has been saved. Failed and in-progress tasks return empty media URL fields. | Media-result URL response | Meaning | What to do | | --- | --- | --- | | `404` `media_not_found` | The token or file extension does not match the result | Use the exact URL you received | | `410` `media_expired` | The 30-day period has ended, or saving failed more than 24 hours ago | The file is no longer available through TokenLab | | `502` or `503` `media_unavailable` | The result is still being recorded, or TokenLab is busy | Retry after the `Retry-After` delay | A download URL obtained from an available result lasts at most five minutes. If it expires while the result is still within its 30-day window, request a fresh URL. This does not extend the result's expiry. ## TokenLab Agent (Assistant) conversation history These are your conversations with the built-in TokenLab assistant. They are stored separately from API request logs and are not automatically removed when those logs reach 30 days. Text already saved in a conversation may remain there, but expired request details and media cannot be loaded again from that conversation. You can remove a conversation from Agent history. This removes it from the history list; it does not erase all associated data or remove request, billing, support, or Console history. The Assistant can investigate original request and response content only while it is within the 30-day window; older billing and usage records may still be available. ## Other records and files - Account information, billing and usage history, and task records are kept separately to provide account services, billing, and support. Their retention is not limited to the 30-day request-detail window. - Organization call-record export files are available to download for 90 days. Download them before they expire; expiry of an export does not delete the underlying records. - Temporary files uploaded in Console or TokenLab Agent (Assistant) can be used in new requests for 24 hours. After that, upload the file again if you need to use it in a new request. - Files you download or export, browser storage, and copies saved by your own application are under your control. TokenLab's retention limits do not delete those copies. The periods above describe availability through TokenLab. Deletion from storage may finish later than access expiry. AI service providers that process your requests have their own data policies and retention periods. For request troubleshooting, see [Logs and troubleshooting](/guides/observability-troubleshooting). For media task polling, see [Async jobs and polling](/guides/async-jobs-polling). --- ### Structured Outputs & Tool Calling > Return JSON or let a model call functions in your application URL: https://tokenlab.sh/docs/en/guides/structured-outputs-tool-calling Structured output asks a model for JSON. Tool calling lets the model request a function that your application executes. The available fields depend on the API format and model, so keep one format throughout the conversation and validate every model-produced value in your application. ## Choose an API format | Need | API | Fields | |------|-------|----------------| | Portable JSON object responses | `/v1/chat/completions` | `response_format: {"type": "json_object"}` | | OpenAI-compatible function calling | `/v1/chat/completions` | `tools: [{ "type": "function", "function": ... }]` | | OpenAI Responses tools | `/v1/responses` | Responses `tools`, `tool_choice`, and `text` fields | | Claude-native tool use or thinking | `/v1/messages` | Anthropic Messages tool schema | | Gemini function declarations or built-in tools | `/v1beta/models/:model:generateContent` | Gemini-native `tools` and content parts | Use built-in tools only with the API format that documents them; TokenLab does not convert those tools between formats. ## JSON Mode For a JSON object that works across many chat models, use Chat Completions JSON mode: ```json { "model": "gpt-5.6-terra", "messages": [ { "role": "user", "content": "Return a JSON object with city and weather." } ], "response_format": { "type": "json_object" } } ``` Chat Completions accepts `text` and `json_object`. `json_schema` and `strict` are model- and format-specific; use them only when the selected model documents support. Always parse and validate returned JSON on your server. JSON mode does not replace application-level schema validation. ## Complete a tool call TokenLab returns the function name and arguments; your application executes it: 1. Send messages plus tool definitions. 2. Read the model response for `tool_calls`, `function_call`, Anthropic `tool_use`, or Gemini function call parts. 3. Execute the tool in your own backend. 4. Append the tool result in the same API format. 5. Continue the conversation until the model returns a final answer. Keep the same API format until the tool call and final answer are complete. The four formats represent tool state differently. ## Chat Completions Example ```bash curl https://api.tokenlab.sh/v1/chat/completions \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "messages": [ { "role": "user", "content": "Extract name and email as JSON. If needed, look up the customer." } ], "response_format": { "type": "json_object" }, "tools": [ { "type": "function", "function": { "name": "lookup_customer", "description": "Look up a customer by email", "parameters": { "type": "object", "properties": { "email": { "type": "string" } }, "required": ["email"] } } } ] }' ``` ## Write a useful schema - Keep schemas small and explicit. Large nested schemas add tokens and reduce reliability. - Prefer required fields for values your product cannot continue without. - Use enums for closed sets that your UI or backend depends on. - Include examples in the prompt when the model struggles with a shape. - If a field is not supported, remove it or use an API format that documents it. ## Before executing a tool - Record the API format, model, tool names, and schema field names. - Validate tool arguments before executing any side effect. - Apply your own permission checks before tool execution. - Make tool execution idempotent when a client retry can repeat the same tool call. - Do not log secrets returned by tools into model-visible messages. ## API Reference | Topic | Reference | |-------|-----------| | Multi-Format API | [Multi-Format API](/guides/api-formats) | | Create Chat Completion | [Create Chat Completion](/api-reference/chat/create-completion) | | Create Response | [Create Response](/api-reference/responses/create-response) | | Create Message | [Create Message](/api-reference/messages/create-message) | | Generate Gemini Content | [Generate Gemini Content](/api-reference/gemini/generate-content) | --- ### Migration Guides > Move existing OpenAI, Anthropic, Gemini, and media clients to TokenLab URL: https://tokenlab.sh/docs/en/guides/migration-guides Keep the request format your application already uses. OpenAI-compatible clients, Anthropic Messages, Gemini REST, and media endpoints each have a matching TokenLab API address. ## API mapping | Existing API | TokenLab base URL | Endpoint | Keep in mind | |-------------------|-------------------|------------------|----------------| | OpenAI Chat Completions | `https://api.tokenlab.sh/v1` | `/chat/completions` | Smallest change for OpenAI-compatible chat and function calling | | OpenAI Responses | `https://api.tokenlab.sh/v1` | `/responses` | Use when your app depends on Responses-specific input, tools, or output handling | | Anthropic SDK | `https://api.tokenlab.sh` | `/v1/messages` | Do not append `/v1` to the SDK base URL | | Gemini REST | `https://api.tokenlab.sh` | `/v1beta/models/:model:generateContent` | Keep Gemini-native fields on the Gemini route | | Media generation | `https://api.tokenlab.sh/v1` | `/images`, `/videos`, `/music`, `/3d` | Discover models with `recommended_for` and expect async polling where documented | | Management and billing | `https://api.tokenlab.sh/v1` | `/management/...` | Server-side calls use a management token, not a model API key | ## Quick Migration Recipes ### OpenAI to TokenLab Change the SDK `base_url` / `baseURL` to `https://api.tokenlab.sh/v1`, replace the API key, and choose a model ID from `GET /v1/models`. ### OpenRouter to TokenLab Replace the OpenRouter base URL with `https://api.tokenlab.sh/v1`. TokenLab model IDs do not include an OpenRouter provider prefix. Use Anthropic Messages or Gemini only when your application needs fields from those formats. ### LiteLLM to TokenLab Use LiteLLM's `custom_openai/` configuration with `api_base: https://api.tokenlab.sh/v1`. Keep your LiteLLM alias separate from the TokenLab model ID so either can change without editing application prompts. ### Claude Messages via TokenLab Point Anthropic SDK clients at `https://api.tokenlab.sh` and call `messages.create`. Do not append `/v1` to the SDK base URL; the SDK owns the `/v1/messages` path. ### Gemini Native via TokenLab Keep Gemini payloads on `https://api.tokenlab.sh/v1beta/models/{model}:generateContent`. Gemini-native `contents`, `parts`, files, cached contents, function declarations, and built-in tools should stay on this route when your app depends on Gemini behavior. ## OpenAI-Compatible Migration ```python from openai import OpenAI client = OpenAI( api_key="sk-your-tokenlab-key", base_url="https://api.tokenlab.sh/v1", ) response = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Hello from TokenLab"}], ) ``` Your existing retry, timeout, and streaming code can usually stay. Confirm the model ID with `GET /v1/models`. Image requests must include a model explicitly; see the image guide for model-specific inputs. ## Anthropic Migration ```python from anthropic import Anthropic client = Anthropic( api_key="sk-your-tokenlab-key", base_url="https://api.tokenlab.sh", ) message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Reply with: Connected to TokenLab."}], ) ``` Use `/v1/messages` for Claude tool calls, thinking blocks, and Anthropic message fields. Chat Completions does not guarantee those fields. ## Gemini Migration ```bash curl "https://api.tokenlab.sh/v1beta/models/gemini-3.5-flash:generateContent" \ -H "Authorization: Bearer sk-your-tokenlab-key" \ -H "Content-Type: application/json" \ -d '{"contents":[{"parts":[{"text":"Hello"}]}]}' ``` Keep Gemini built-in tools, File API references, cached contents, function declarations, and native content parts on `/v1beta` when your app depends on Gemini-native behavior. ## Media Migration 1. Query `GET /v1/models?recommended_for=image|video|music|3d`. 2. Read `GET /v1/models` in list responses and the full `GET /v1/models/{model}` where available. 3. Send an explicit `model`, especially for image endpoints. 4. Store `task_id`, `poll_url`, endpoint, model, and your own job ID for async jobs. 5. Reconcile costs through usage records and `billing_transaction_id`, not provider task IDs. Media generation can finish asynchronously, so save `task_id` and `poll_url` before replacing an existing integration. A create-request timeout must not produce a second user job. ## Migration Pitfalls - Do not put every model behind one OpenAI Chat Completions path if your app needs native Anthropic, Gemini, or Responses behavior. - Do not assume old image defaults. Send `model` explicitly. - Do not retry async create requests without checking whether a task was already created. - Use TokenLab task IDs and usage records in your product. Third-party task IDs are not billing identifiers. ## API Reference | Topic | Reference | |-------|-----------| | Multi-Format API | [Multi-Format API](/guides/api-formats) | | OpenAI SDK | [OpenAI SDK](/integrations/openai-sdk) | | Anthropic SDK | [Anthropic SDK](/integrations/anthropic-sdk) | | Gemini Native | [Gemini Native API](/api-reference/gemini/generate-content) | | Image Generation | [Image Generation](/guides/image-generation) | | Async Jobs & Polling | [Async Jobs & Polling](/guides/async-jobs-polling) | --- ### Build a reliable TokenLab integration > Keep cost, retries, API keys, and request records under control URL: https://tokenlab.sh/docs/en/guides/best-practices A good integration makes price and model choices visible, survives ordinary network failures, and never exposes an API key in the browser. The practices below apply across chat, image, video, audio, and other TokenLab APIs. ## Choose models with current data Model names, capabilities, and prices change. Read the [Models API](/api-reference/models/list-models) or model pages instead of keeping a permanent list in code or documentation. For a new use case: 1. Filter models by the capability your product needs. 2. Compare their current TokenLab prices. 3. Test a small set with representative user requests. 4. Keep the model ID explicit in each request. Do not switch models silently. A replacement can change output quality, price, context length, available tools, or media support. ## Put an upper bound on cost For text models, limit output to the length your product can actually use. The parameter name depends on the API and model, so check the model page before sending `max_tokens` or `max_output_tokens`. Shorter instructions are not automatically better, but duplicated context and repeated boilerplate always add input tokens. Keep stable instructions concise and send only the history needed for the next answer. For image, video, music, 3D, and Worlds requests, cost often depends on output count, duration, resolution, or another media option. Show the current estimate before a user confirms an expensive generation whenever the API can provide one. ## Stream long text responses Streaming shows the answer as it arrives and avoids making the user wait for the complete response. It does not reduce token cost. For a complete example with client setup and completion checks, see [Streaming](/guides/streaming). Handle a disconnect as an incomplete answer. Do not present partial output as successfully finished, and do not repeat a request automatically when doing so could create a second side effect or charge. ## Retry only when it is safe Retry `429` responses after `Retry-After` or `retry_after`. A short exponential backoff is appropriate for transient `5xx` responses. Authentication, validation, balance, and context-length errors need a change from the application or user; sending the same request again will not fix them. See [Rate limits](/guides/rate-limits) for a bounded retry example that respects `Retry-After` and disables duplicate SDK retries. Set a retry limit and a total time limit. For media creation, save the returned task ID and check it after a timeout before creating another task. Use an idempotency key when the endpoint supports one. ## Keep API keys on the server Never embed a TokenLab API key in browser JavaScript, a mobile application bundle, a public repository, or a client-visible log. ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", timeout=30.0, max_retries=0, ) ``` Use separate API keys for development, production, and independent applications. Give each key only the models and spending limit it needs. Revoke a key immediately if it may have been exposed. ## Keep useful request records Store the identifiers that let you connect a user action to usage and support records: - `request_id` for the API request - `task_id` for asynchronous generation - `billing_transaction_id` when returned - model ID and endpoint - your own user, project, or job ID Do not log API keys, management tokens, full private prompts, private media, or signed URLs. Usage records in Console and the Management API are the source for billed amounts; model response token counts alone may not describe the final charge. ## Design for asynchronous work Image, video, music, 3D, and Worlds APIs may return before the result is ready. Save the task ID, use the returned `poll_url`, and stop only at `completed` or `failed`. A failed status lookup does not mean the generation itself failed. See [Async jobs and polling](/guides/async-jobs-polling) for cancellation, timeout, and duplicate-request handling. ## Before launch - Run representative requests with the exact model and API format you will ship. - Check both success and common error responses. - Confirm the price shown in your product matches the current TokenLab price. - Make refreshes, network timeouts, and repeated clicks safe. - Make it possible to trace a user-visible result back to its Request ID. --- ## Media Guides ### Image generation > Generate or edit images and handle finished files or async tasks URL: https://tokenlab.sh/docs/en/guides/image-generation TokenLab supports text-to-image, image-to-image, and image editing. Parameters differ by model, so check the selected model's request fields before sending the request. ## Choose an endpoint | What you need | Endpoint | Use when | Not for | |---------------|----------|----------|------------| | Text-to-image | `POST /v1/images/generations` | The user starts with text only | Editing an existing GPT Image file | | Image-to-image | `POST /v1/images/generations` | The model accepts `operation: "image-to-image"` and image URLs | Models that require multipart edit input | | Image edit | `POST /v1/images/edits` | A supported edit model changes an existing image | Nano Banana-style reference generation | | Variation | `POST /v1/images/variations` | An existing integration already uses the variations API | A new reference-image feature | | Status | `GET /v1/tasks/{id}` | A create response returns `task_id`, `status: "pending"`, or `poll_url` | The create response already contains final `data[]` | Always send `model`; image APIs do not choose a default model for you. ## Choose a model Find current image models with: ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=image" \ -H "Authorization: Bearer sk-your-api-key" ``` Open the selected model's details to confirm: - The supported operation, such as `text-to-image`, `image-to-image`, or `image-edit`. - The request endpoint expected by the model. - How to send references: `image_url`, `image_urls`, `reference_image_urls`, multipart `image`, or JSON `images[]`. - Whether the model accepts `size`, `aspect_ratio`, `resolution`, `quality`, `background`, `output_format`, or `response_format`. ## Model-specific fields - `gpt-image-2` style requests use OpenAI-like `size`, `quality`, and edit fields. For generation and edits, `background` accepts `auto` or `opaque`; `transparent` is not supported. Omit optional fields to use automatic defaults. - Gemini and Nano Banana image families usually use `aspect_ratio`; only send `resolution` when the model details expose it. On models with resolution tiers, a `size` of `1K`, `2K`, or `4K` is sent as `resolution`; a tier that is different from `resolution` is refused. - Nano Banana image-to-image belongs on `/v1/images/generations` with `operation: "image-to-image"` and reference image URLs. - `/v1/images/generations` does not accept top-level `images[]` or `file_id`; those are edit-flow shapes. - Remote image references must be public `http` or `https` URLs. Do not send private network URLs, embedded credentials, URL fragments, or signed URLs that may expire before processing starts. ## Text-to-image example ```bash curl https://api.tokenlab.sh/v1/images/generations \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2", "prompt": "A clean product photo of a ceramic coffee cup on a walnut desk", "size": "1024x1024", "response_format": "url" }' ``` ## Reference-image example ```bash curl https://api.tokenlab.sh/v1/images/generations \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "nano-banana-2", "operation": "image-to-image", "prompt": "Keep the product shape, change the background to a bright studio setup", "image_urls": ["https://example.com/input/product.png"], "aspect_ratio": "1:1" }' ``` ## Get the image Image responses can be synchronous or asynchronous: - Synchronous responses return final `data[]` with `url` or `b64_json`. - Async responses return `id`, `task_id`, `status`, and usually `poll_url`. - Use `poll_url` when it is present. If you need a fixed URL, call `GET /v1/tasks/{id}`. - Use a synchronous request when you need `b64_json`; async image results use URLs. Image result URLs are TokenLab media-result URLs that stay valid for 30 days. `media_retention.items` reports each item's saving status and `expires_at`; the response can omit `media_retention` when the saving status is not known in time, and the URL still works. Inline Base64 results are outside this URL-copy retention. Manually saving an image to the Media Library gives it a separate lifecycle. See [Data retention](/guides/data-retention). ```bash curl "https://api.tokenlab.sh/v1/tasks/$TASK_ID" \ -H "Authorization: Bearer sk-your-api-key" ``` Persist the returned image URL, task ID, model, and your own user/job ID. Do not keep polling after a terminal status. ## Before launch - Validate user inputs before calling TokenLab: prompt length, image count, URL reachability, and file type. - Set HTTP timeouts high enough for synchronous high-resolution requests. Use async mode where available for long work. - Store `request_id`, `task_id`, `poll_url`, model, endpoint, and the field names sent. - On client timeout, check whether a task was created before retrying the create request. - Use Usage and `billing_transaction_id` for the final charge. ## Common Errors | Symptom | Likely cause | Fix | |---------|--------------|-----| | `400` with `param: "model"` | Missing explicit model | Query `/v1/models?recommended_for=image` and send `model` | | Unsupported field | Field is not documented for that model | Remove the field or choose a model/endpoint that documents it | | No `b64_json` on async result | Async image tasks return URL-oriented results | Use synchronous mode for base64 output | | Reference image rejected | Wrong endpoint or private/expired URL | Match the model's documented reference shape and use reachable URLs | ## API Reference | Topic | Reference | |-------|-----------| | Create Image | [Create Image](/api-reference/images/create-image) | | Edit Image | [Edit Image](/api-reference/images/edit-image) | | Create Image Variation | [Create Image Variation](/api-reference/images/create-variation) | | Get Image Status | [Get Image Status](/api-reference/images/get-image-status) | | Get Task Status | [Get Task Status](/api-reference/tasks/get-task-status) | --- ### Audio and realtime > Create speech, transcribe audio, translate speech, or open a realtime session URL: https://tokenlab.sh/docs/en/guides/audio-realtime Use the audio endpoints when the result is a file or text. Use the realtime WebSocket endpoint for a live, two-way audio experience. ## Choose an endpoint | Need | Endpoint | Use it when | |----------|----------|-------------| | Text to speech | `POST /v1/audio/speech` | You need an audio file from text. | | Transcription | `POST /v1/audio/transcriptions` | You need text from an audio file. | | Audio translation | `POST /v1/audio/translations` | You need translated text from an audio file. | | Realtime WebSocket | `WS /v1/realtime?model={model}` | You need bidirectional streaming audio or realtime multimodal events over WebSocket. Plain `GET /v1/realtime` only returns metadata. | ## Choose a model Read the current model list instead of hard-coding it. Check the selected model's details for realtime support before opening a socket. ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=tts" \ -H "Authorization: Bearer sk-your-api-key" curl "https://api.tokenlab.sh/v1/models?recommended_for=stt" \ -H "Authorization: Bearer sk-your-api-key" ``` ## Audio requests Speech returns audio in the HTTP response. Transcription and translation can return final text or an accepted task. When the body contains a task ID and status instead of final text, save the ID and follow `poll_url` until completion or failure. Allow enough time for large files and keep the Request ID. Audio result URLs are TokenLab media-result URLs that stay valid for 30 days. `media_retention.items` reports each item's saving status and `expires_at`; the response can omit `media_retention` when the saving status is not known in time, and the URL still works. Inline or binary audio responses are outside this URL-copy retention. See [Data retention](/guides/data-retention). ```bash curl -X POST "https://api.tokenlab.sh/v1/audio/speech" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "grok-tts", "voice": "eve", "input": "Welcome to TokenLab." }' \ --output speech.mp3 ``` ## Realtime Sessions Open a WebSocket with the model in the query string and the API key in the Authorization header. Keep the event format documented for the selected realtime model, and close the socket when the session is complete. This guide covers the WebSocket subset only. TokenLab does not currently provide OpenAI Realtime REST client secret, translation client secret, Calls, or legacy beta session management endpoints. For the server-side example below, install `ws` and set `TOKENLAB_REALTIME_MODEL` to a current model whose detail declares `/v1/realtime`. Provide `TOKENLAB_API_KEY` in the server environment; the name alone does not establish realtime support. ```bash npm install ws ``` ```javascript import WebSocket from 'ws'; const model = process.env.TOKENLAB_REALTIME_MODEL; const apiKey = process.env.TOKENLAB_API_KEY; if (!model || !apiKey) throw new Error('Set TOKENLAB_REALTIME_MODEL and TOKENLAB_API_KEY'); const url = new URL('wss://api.tokenlab.sh/v1/realtime'); url.searchParams.set('model', model); const socket = new WebSocket(url, { headers: { Authorization: `Bearer ${apiKey}` }, }); socket.on('message', (event) => console.log(event.toString())); socket.on('error', (error) => console.error(error.message)); process.once('SIGINT', () => socket.close(1000, 'Client stopped')); ``` ## Show progress safely - Save generated audio files instead of replaying the same request on refresh. - For transcription and translation, show upload and processing states even when the API call is synchronous. - For realtime, handle close events and reconnect only after the user starts a new session. - Do not put API keys, private URLs, or account secrets in audio text input. ## API Reference | Topic | Reference | |-------|-----------| | Create Speech | [Create Speech](/api-reference/audio/create-speech) | | Create Transcription | [Create Transcription](/api-reference/audio/create-transcription) | | Create Translation | [Create Translation](/api-reference/audio/create-translation) | | Realtime WebSocket | [Realtime WebSocket](/api-reference/realtime/connect) | | List Models | [List Models](/api-reference/models/list-models) | | Billing & Pricing | [Billing & Pricing](/guides/billing) | --- ### Video Generation > Create videos from text, images, audio, or existing clips URL: https://tokenlab.sh/docs/en/guides/video-generation Video generation is asynchronous. `POST /v1/videos/generations` returns a task ID and usually a `poll_url`. Use that URL until the finished video is ready. Send public HTTP(S) URLs or supported data URLs in the image fields accepted by your selected model. These inputs follow the normal media path and do not automatically create reusable material IDs. When an explicit material is still preparing, `POST /v1/videos/generations` returns `409 seedance_material_preparing` with `inactive_asset_ids`. Poll those assets until `ACTIVE`, then retry with the same material IDs. If an asset is `FAILED`, inspect `error_message` and correct or reimport it before retrying. ## Choose an API Use `/v1/videos/generations` for a new video integration. Existing Seedance 2.0 clients that already send Volcengine `content[]` or Action requests can keep that format through `/api/v3`. Both APIs use a TokenLab Bearer key, but their fields and status values are different. ## Choose what to create Set `operation` explicitly so the API can validate the right fields for the job. | Operation | Required or typical input | Use case | |-----------|---------------------------|----------| | `text-to-video` | `prompt` | Generate from text only | | `image-to-video` | `image_url` or compatible `image` | Animate a starting image | | `reference-to-video` | `reference_images` and optional `video_urls` / `audio_urls` on supported models | Keep identity, style, or asset references | | `start-end-to-video` | `start_image`, `end_image` | Control first and last frames | | `video-to-video` | `video_url` or model-specific `task_id` | Transform or upscale an existing clip | | `motion-control` | `image_url` plus `video_url` | Apply motion reference to a subject | | `audio-to-video` | `audio_url` | Audio-conditioned video flows | | `video-extension` | `task_id`, `extend_at`, or model-specific extension fields | Continue a generated video | ## Choose a model ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=video" \ -H "Authorization: Bearer sk-your-api-key" ``` Use a model ID returned by TokenLab, then choose the generation type with `operation` and the corresponding media inputs. Do not use an operation name as the model ID. Read the selected model detail before relying on specialized fields such as `reference_images`, `kling_elements`, `output_audio`, `duration`, `resolution`, or `aspect_ratio`. ## Create a video ```bash curl https://api.tokenlab.sh/v1/videos/generations \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "veo3.1", "operation": "text-to-video", "prompt": "A calm cinematic shot of a cat walking through a sunlit garden", "duration": 4, "aspect_ratio": "16:9" }' ``` Use `https` URLs for media that can be fetched from the internet. Temporary URLs must stay valid until TokenLab creates the task. ## Model-specific fields Audio behavior depends on the selected model **and operation**. A video may contain sound even when no audio selector is exposed. Omitting a selector is different from sending `false`. - `veo3.1` and `veo3.1-fast` always generate audio under the Gemini API contract. For `wan-2.6` and `wan-2.7` video generation, audio cannot be disabled. Omit `output_audio` or use `true` where the model detail permits it. - `hailuo-h3` and Grok video models generate native audio. Do not add an audio switch that the selected model detail does not list. - Seedance 1.5/2.x and `viduq3-pro` / `viduq3-turbo` default to audio on and support silent output. PixVerse C1/V5.6/V6 default to audio off. Use `output_audio` only for operations that list it; Vidu also accepts its declared boolean `audio` field. - `audio_url` / `audio_urls` provide input or reference audio. They are not audio-output switches. Video editing, motion transfer, and style transfer may retain the input soundtrack; preserving source audio does not mean muting it. Check the model detail for allowed values and audio-dependent prices. Supported aliases `outputAudio`, `generate_audio`, and boolean `audio` must agree with `output_audio` when combined. Do not assume every version or operation in a model family shares the same audio controls. - For the Seedance 2.0 family, read the [Seedance 2.0 video models guide](/guides/seedance-2-video) before using 4K output, Fast/Mini resolution caps, or multimodal reference inputs. - For `grok-imagine-video` video-to-video, send `prompt` and a public HTTPS `.mp4` `video_url`. A TokenLab `.mp4` result URL from an earlier task works. This operation does not use `duration`, `resolution`, or `aspect_ratio` selectors. ## PixVerse and HappyHorse | Model | Operations | Inputs | Resolution | Duration | Audio selector | |---|---|---|---|---|---| | `pixverse-c1`, `pixverse-v6` | `text-to-video`, `image-to-video`, `start-end-to-video`, `reference-to-video` | `prompt`; `image_url`; `start_image` + `end_image`; `reference_images` | 360p, 540p, 720p, 1080p | Any integer from 1 to 15 seconds | `output_audio`, default `false` | | `pixverse-v5.6` | `text-to-video`, `image-to-video`, `start-end-to-video`, `reference-to-video` | Same fields as C1 and V6 | 360p, 540p, 720p, 1080p | 5, 8, or 10 seconds; 1080p supports 5 or 8 seconds | `output_audio`, default `false` | | `happyhorse-1.0` | `text-to-video`, `image-to-video`, `reference-to-video`, `video-to-video` | `prompt`; `image_url`; `reference_images`; `video_url` + `reference_images` | 720p, 1080p | 3 to 15 seconds for generation operations; video-to-video output is capped at 15 seconds | Do not send `output_audio` | On TokenLab, the PixVerse models above do not accept `operation=video-extension`. ```bash curl https://api.tokenlab.sh/v1/videos/generations \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "pixverse-v6", "operation": "image-to-video", "prompt": "A slow camera move through a neon-lit street", "image_url": "https://example.com/start.jpg", "resolution": "1080p", "duration": 5, "output_audio": true }' ``` ## Get the finished video Use the returned `poll_url`. If you need a fixed URL, call `GET /v1/tasks/{id}` with the `id` or `task_id` from the create response. Completed video tasks may return `video_url`, `video`, or `videos` depending on the model and output count. Treat `billing_transaction_id` as a billing identifier, not as a task identifier. Video result URLs are TokenLab media-result URLs that end in the file type, such as `.mp4`, and stay valid for 30 days. `media_retention.items` reports each item's saving status and `expires_at`; the response can omit `media_retention` when the saving status is not known in time, and the URL still works. Failed and in-progress tasks return empty video URL fields. Async task snapshots have a separate lifecycle. See [Data retention](/guides/data-retention). ## Common Pitfalls - Do not hard-code old video status paths; prefer `poll_url`. - Do not combine first-frame fields with dedicated reference-image flows unless the model details allows it. - Do not assume `duration` describes input reference video length; it usually controls generated output length. - Do not retry create requests after a timeout without checking whether a task was already created. ## API Reference | Topic | Reference | |-------|-----------| | Create Video | [Create Video](/api-reference/video/create-video) | | Get Video Status | [Get Video Status](/api-reference/video/get-video-status) | | Get Task Status | [Get Task Status](/api-reference/tasks/get-task-status) | | Cancel Task | [Cancel Task](/api-reference/tasks/cancel-task) | | Billing & Pricing | [Billing & Pricing](/guides/billing) | > Note: Hailuo H3-Max generates 5–15-second videos at 480p or 768p from text, a first frame, or first and last frames. Its speed-focused workflow is useful for quickly turning a shot idea into a short clip. ```json { "model": "hailuo-h3-max", "operation": "text-to-video", "prompt": "A slow camera move through a quiet garden", "resolution": "768p", "duration": 5, "aspect_ratio": "16:9" } ``` --- ### Seedance 2.0 Video Models > Choose a Seedance 2.0 model and start generating video URL: https://tokenlab.sh/docs/en/guides/seedance-2-video This page helps you choose a Seedance 2.0 model and API. Separate guides cover Volcengine migration, reusable materials, and real-person verification. ## Choose an API | What you are building | Use | Guide | |------|-----|---------------| | Build a new TokenLab video integration | Unified `POST /v1/videos/generations` API | [Video Generation](/guides/video-generation) | | Move an existing Volcengine Seedance client | Volc-compatible REST or Action endpoints | [Migrate Seedance from Volcengine](/guides/seedance-volcengine-migration) | | Reuse virtual-avatar, product, style, or real-person references | TokenLab material library | [Seedance Materials and Real-Person Verification](/guides/seedance-materials) | Every option uses `Authorization: Bearer `. Volc-compatible requests keep the Volcengine fields but use a TokenLab key instead of AK/SK signing. ## Model Choice | Model | Supported resolutions | | --- | --- | | `seedance-2.0` | `480p`, `720p`, `1080p`, `4k` | | `seedance-2.0-fast` | `480p`, `720p` | | `seedance-2.0-mini` | `480p`, `720p` | `seedance-2.0-fast` and `seedance-2.0-mini` reject `1080p` and `4k` requests. Query the [Models API](/api-reference/models/list-models) when model availability must be decided at runtime. ## Supported Operations | Operation | Typical inputs | |-----------|----------------| | `text-to-video` | `prompt` | | `image-to-video` | `prompt`, `image_url` or a compatible image input | | `start-end-to-video` | `prompt`, `start_image`, `end_image` | | `reference-to-video` | `prompt`, `reference_images`, optional `video_urls`, optional `audio_urls` | | `video-to-video` | `prompt`, `video_url` or a compatible video input | | `video-extension` | `task_id` or model-specific continuation input | Set `operation` explicitly so the API can validate the fields for that generation type. `seedance-2.0` and `seedance-2.0-fast` accept up to 9 reference images, 3 reference videos, and 3 reference audio files where reference generation is supported. Audio by itself, or text plus audio without an image or video reference, is not supported. ## Results And Pricing Video generation is asynchronous. Store the returned task ID and follow its exact `poll_url` until the task reaches a terminal state. See [Async Jobs and Polling](/guides/async-jobs-polling) for retry and timeout guidance. Pricing depends on the model, output resolution, and input type. Read it from the [Pricing API](/api-reference/pricing/get-pricing) instead of hard-coding it in your product. ## API Reference | Topic | Reference | |-------|-----------| | Create a unified video task | [Create Video](/api-reference/video/create-video) | | Poll a unified task | [Get Task Status](/api-reference/tasks/get-task-status) | | Volc-compatible task operations | [Create Task (Volc Compatible)](/api-reference/video/create-volc-compatible-seedance-task) | | Volc-compatible material operations | [Material Actions (Volc Compatible)](/api-reference/video/volc-compatible-material-actions) | --- ### Migrate Seedance from Volcengine > Keep your Volcengine request format and switch Seedance to TokenLab URL: https://tokenlab.sh/docs/en/guides/seedance-volcengine-migration If your application already uses the Volcengine Seedance API, you can keep its REST paths, `content[]` body, response fields, callbacks, and PascalCase material Actions. Change the API host and replace AK/SK signing with a TokenLab API key. ## What Changes | Change | Existing Volcengine client | TokenLab | |---------|----------------------------|----------| | Base URL | Your Volcengine endpoint | `https://api.tokenlab.sh/` | | Authentication | AK/SK request signing | `Authorization: Bearer ` | | Task transport | Official `/api/v3/contents/generations/tasks` REST paths | Preserved | | Material/validation Actions | `Action` and `Version` query parameters | Preserved | | Task body | Volcengine-style `content[]` | Preserved | | Material body | PascalCase fields such as `GroupId` and `ProjectName` | Preserved | | Async result | Provider task lifecycle | TokenLab `cgt-...` task ID with polling and optional callback | Requests signed only with AK/SK return `401 AuthenticationError`. Every TokenLab request must include `Authorization: Bearer `. ## Choose an endpoint | Existing client | TokenLab endpoint | |-----------------|-------------------| | Volcengine REST task client | `/api/v3/contents/generations/tasks` and its task paths | | Material or validation Action client | `POST /?Action=...&Version=2024-01-01` or `POST /api/v3?Action=...&Version=2024-01-01` | For video tasks, keep the existing REST method, path, and JSON body. Material and verification requests continue to use Action and `Version` query parameters. ## Official Task Paths | Method and path | Purpose | |-----------------|---------| | `POST /api/v3/contents/generations/tasks` | Create a video task | | `GET /api/v3/contents/generations/tasks/{id}` | Read one task | | `GET /api/v3/contents/generations/tasks` | List tasks | | `DELETE /api/v3/contents/generations/tasks/{id}` | Cancel or delete a task | REST clients can use these equivalent references: - [Create Task (Volc Compatible)](/api-reference/video/create-volc-compatible-seedance-task) - [Get Task (Volc Compatible)](/api-reference/video/get-volc-compatible-seedance-task) - [List Tasks (Volc Compatible)](/api-reference/video/list-volc-compatible-seedance-tasks) - [Cancel Task (Volc Compatible)](/api-reference/video/delete-volc-compatible-seedance-task) ### Request Body Rules - `content[]` accepts `text`, `image_url`, `video_url`, `audio_url`, and `draft_task` items. - For `image_url`, omit `role` or use `first_frame` for image-to-video. Use `last_frame` together with a first frame, or `reference_image` for reference-to-video. - A public material URI such as `asset://asset-YYYYMMDDHHMMSS-xxxxx` can be used where the selected Seedance workflow accepts that media role. - The REST body accepts only official fields, including `model`, `content`, `ratio`, `duration`, `resolution`, `generate_audio`, `watermark`, `return_last_frame`, `seed`, `execution_expires_after`, and `safety_identifier`. `generate_audio` defaults to `false`. - `callback_url` accepts a public HTTP(S) endpoint. TokenLab sends the same query-task body for `queued`, `running`, `succeeded`, `failed`, and `expired`; only `succeeded` and `failed` use the official five-second, three-retry behavior. ## Minimal REST Example ```bash curl 'https://api.tokenlab.sh/api/v3/contents/generations/tasks' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Idempotency-Key: $CLIENT_JOB_ID" \ -H "Content-Type: application/json" \ -d '{ "model": "seedance-2.5", "content": [ {"type":"text","text":"A cinematic product reveal"}, {"type":"image_url","role":"reference_image","image_url":{"url":"https://example.com/reference.png"}} ], "ratio": "16:9", "duration": 5, "resolution": "720p" }' ``` Store the returned `cgt-...` ID. Poll until `status` becomes `succeeded`, `failed`, `cancelled`, or `expired`; do not treat the create response as a completed video. If you provide `callback_url`, allow repeated `succeeded` or `failed` notifications and keep polling as a recovery path. ### Keep v1 And v3 Types Separate Do not reuse a `/v1/tasks/{id}` response struct for this endpoint. The unified v1 API reports `pending`, `processing`, `completed`, and `failed`; the Volc-compatible v3 API reports `queued`, `running`, `succeeded`, `failed`, `cancelled`, and `expired`. The official v3 response also represents `duration` as a JSON string and omits `error` on non-failed tasks. For safe recovery after a create timeout or disconnect, add a unique `Idempotency-Key` to the REST create request. Retrying the same body with the same key returns the original `cgt-...` ID; the same key with a different body returns `409`. ## Materials And Real-Person Verification The same Action endpoint also supports the 10 material and material-group operations. See [Material Actions (Volc Compatible)](/api-reference/video/volc-compatible-material-actions) for their exact PascalCase bodies, filters, paging, response envelope, and 12-hour material URLs. Real-person materials add two Actions before upload: 1. [Create Visual Validation Session](/api-reference/video/create-visual-validation-session) 2. [Get Visual Validation Result](/api-reference/video/get-visual-validation-result) The second Action returns the verified `GroupId`. Use that ID with `CreateAsset`; a client cannot create a `LivenessFace` group directly. ## Migration Checklist 1. Replace the API host with `https://api.tokenlab.sh`. 2. Replace AK/SK signing with a TokenLab Bearer API key. 3. Keep the official REST task method, path, and request-body casing; keep Action and version only for material or validation operations. 4. Keep the same `ProjectName` across related material and verification requests. 5. Store the TokenLab task, group, and asset IDs returned by these endpoints. 6. Verify one create, poll, list, and error response before moving production traffic. --- ### Seedance Materials and Real-Person Verification > Reuse images, video, audio, or a verified person in Seedance URL: https://tokenlab.sh/docs/en/guides/seedance-materials Seedance materials let you reuse an image, video, or audio file without importing it for every generation. References to a real person require consent and visual verification; virtual people, products, and styles do not. ## Choose a material type | What you want to reuse | What to do | |------|---------------| | One image used once | Send its URL in a supported image field; this does not create a reusable material ID | | Reuse a virtual person, product, or style | Create an `aigc_avatar` group, create an asset, wait for `ACTIVE`, then use the asset ID | | Reuse a real person | Complete visual validation, receive its `GroupId`, create an asset in that group, wait for `ACTIVE`, then use the asset ID | | Migrate an existing Volcengine material client | Keep the Action request shape and use the [Volc-compatible material reference](/api-reference/video/volc-compatible-material-actions) | ## Groups and materials | Item | REST field | Action field | What it means | |---------|------------|--------------|---------------| | Material group | `group_id` | `GroupId` | Keeps related materials together | | Material | `id` | `Id` | One imported image, video, or audio file; use this ID after it becomes active | | Virtual-avatar group | `library_type: "aigc_avatar"` | `GroupType: "AIGC"` | Does not require real-person verification | | Real-person group | `library_type: "liveness_face"` | `GroupType: "LivenessFace"` | Created only by a successful visual-validation flow | | Project name | `project_name` | `ProjectName` | Keeps materials from different projects separate; defaults to `default` | Keep group IDs and asset IDs separate. A group organizes uploads; an asset ID is the reference used by video generation. Public IDs look like `group-20260720123456-vrt01` and `asset-20260720123457-def45`. ## Material Retention TokenLab keeps every material asset until you delete the asset or its group. It is not cleaned up for inactivity on TokenLab's side. The upstream Seedance provider may remove its own working copy of an asset after 30 days without use. This does not delete your asset or change its ID: the next time you use it in a generation request, TokenLab automatically prepares a new upstream copy from the stored original. - The first generation after a cleanup can take a little longer while the new copy is prepared. If it is not ready yet, the request returns `seedance_material_preparing`; retry shortly. - Deleting an asset or group is permanent and cannot be undone. ## Image URLs and reusable materials Send public HTTP(S) URLs or supported data URLs in the image fields accepted by your selected model. These inputs follow the normal media path and do not automatically create reusable material IDs. For reusable materials, create an asset through the material API, wait for `ACTIVE`, then use `material_asset_id`, `material_asset_ids`, or `asset://` in a supported media field. Keep the intended first-frame, last-frame, or reference-image role. When an explicit material is still preparing, `POST /v1/videos/generations` returns `409 seedance_material_preparing` with `inactive_asset_ids`. Poll those assets until `ACTIVE`, then retry with the same material IDs. If an asset is `FAILED`, inspect `error_message` and correct or reimport it before retrying. ## Virtual-Avatar Materials Use [Create Material Asset Group](/api-reference/video/create-material-asset-group) to create an `aigc_avatar` group. For REST asset creation, `group_id` is optional; TokenLab uses or creates the organization's default virtual-avatar group when it is omitted. After a group exists, manage it with: - [List Material Asset Groups](/api-reference/video/list-material-asset-groups) - [Get Material Asset Group](/api-reference/video/get-material-asset-group) - [Update Material Asset Group](/api-reference/video/update-material-asset-group) - [Delete Material Asset Group](/api-reference/video/delete-material-asset-group) Deleting a group also deletes the TokenLab materials it contains and cannot be undone. ## Real-Person Verification Use this flow when your product must collect consent and verify a face before reusing a real person as a Seedance reference. 1. Call [Create Visual Validation Session](/api-reference/video/create-visual-validation-session) with `CallbackURL`; save `Result.BytedToken` and open `Result.H5Link` for the person being verified. 2. After the H5 flow redirects to the callback, poll [Get Visual Validation Result](/api-reference/video/get-visual-validation-result) with the same `BytedToken` and `ProjectName`. 3. Store the returned `Result.GroupId`. TokenLab creates and binds the `LivenessFace` group during this step. 4. Create an asset with that group ID, then poll the asset until it becomes `ACTIVE`. `BytedToken` is valid for 30 minutes. A client cannot create a `LivenessFace` group with `CreateAssetGroup`; this prevents an unverified group from being presented as verified. Open the returned `H5Link` immediately after creation. The token validity does not mean the verification page can be opened for the first time at any point in that window. For a real-person group, uploads are checked against the verified face. An asset can fail if it contains multiple faces or the face does not match. For best results, upload a full-body front reference and a clear front-facing close-up. ## Create And Prepare An Asset Use [Create Material Asset](/api-reference/video/create-material-asset) to import one publicly reachable URL at a time. | Type | Supported input | |------|-----------------| | Image | `jpeg`, `png`, `webp`, `bmp`, `tiff`, `gif`, `heic`, `heif`; ≤ 30 MiB; width and height [300, 6000] px; aspect ratio [0.4, 2.5] | | Video | `mp4`, `mov`; ≤ 200 MiB | | Audio | `aac`, `wav`, `mp3`; ≤ 15 MiB | These are file-import limits. A successful import request does not guarantee that media or real-person validation will pass. Wait for `ACTIVE`; if the asset becomes `FAILED`, use `error_message` to correct the source before importing again. Material ingestion is asynchronous. Poll [Get Material Asset](/api-reference/video/get-material-asset) until `status` becomes `ACTIVE`. HTTP success means the import was accepted, not that the asset is ready. If the asset becomes `FAILED`, inspect `error_message`, correct the source, and create a new asset. The source URL is only used for import. TokenLab returns its own asset ID and keeps the asset in your organization's material library until you delete the asset or its group. The upstream copy is re-created automatically if it is cleaned up; see the retention section above. ## Use An Asset In Video Generation After an asset is `ACTIVE`, choose one of these forms when calling [Create Video](/api-reference/video/create-video): - `material_asset_id` for one generic material reference - `material_asset_ids` for multiple generic references - `asset://` in `start_image`, `end_image`, or `reference_images` when the media role must be explicit Material references count toward the selected model's Seedance reference limits. If the API returns `Seedance material asset not found or not accessible`, confirm that the value is an asset ID rather than a group ID, belongs to the same organization and project, is not deleted, and is `ACTIVE`. ## REST Or Volcengine Action TokenLab-native integrations can continue to use the snake_case `/v1/videos/assets*` REST endpoints. Existing Volcengine clients can retain PascalCase bodies with [Material Actions (Volc Compatible)](/api-reference/video/volc-compatible-material-actions). Both interfaces operate on the same organization- and project-scoped material data. ## REST Example: Virtual-Avatar Asset ```bash curl https://api.tokenlab.sh/v1/videos/assets/groups \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"library_type":"aigc_avatar","group_name":"Product references","project_name":"default"}' curl https://api.tokenlab.sh/v1/videos/assets \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"group_id":"group-20260720123456-abc12","asset_url":"https://example.com/reference.png","asset_name":"Front view","asset_type":"Image","project_name":"default"}' curl https://api.tokenlab.sh/v1/videos/assets/asset-20260720123457-def45?project_name=default \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ## Action Example: Verified Real-Person Asset First create and complete visual validation: ```bash curl 'https://api.tokenlab.sh/?Action=CreateVisualValidateSession&Version=2024-01-01' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"CallbackURL":"https://yourapp.example.com/seedance/callback","ProjectName":"default"}' curl 'https://api.tokenlab.sh/?Action=GetVisualValidateResult&Version=2024-01-01' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"BytedToken":"","ProjectName":"default"}' ``` Then pass the returned `GroupId` to `CreateAsset`: ```bash curl 'https://api.tokenlab.sh/?Action=CreateAsset&Version=2024-01-01' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "GroupId":"group-20260720123456-real1", "URL":"https://example.com/person-front.png", "Name":"Verified front view", "AssetType":"Image", "ProjectName":"default" }' ``` Poll with `GetAsset` until its status is `Active`, then pass the returned asset ID to video generation. For all 10 Action operations, filters, pagination, response shapes, and errors, see [Material Actions (Volc Compatible)](/api-reference/video/volc-compatible-material-actions). --- ### Music generation > Create songs or lyrics and wait for the finished audio URL: https://tokenlab.sh/docs/en/guides/music-generation Music generation is asynchronous. `POST /v1/music/generations` returns a task ID, status, and usually `poll_url`. Use that URL until the task is `completed` or `failed`. ## Choose what to create | What to create | Key fields | Notes | |----------|------------|-------| | Full song or instrumental | `model`, `mv`, `prompt`, optional `title`, `tags`, `action: "MUSIC"` | Use when the user expects final audio | | Lyrics only | `model`, `prompt`, `action: "LYRICS"` | Use only with models that expose lyric generation | | Continue an existing clip | `continue_clip_id`, optional `continue_at` | Keep the earlier clip ID | Get the current music-model list from: ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=music" \ -H "Authorization: Bearer sk-your-api-key" ``` The example below uses `suno_music` with `mv: "chirp-v4"`. For lyrics only, choose a model that documents lyric generation, send `action: "LYRICS"`, and omit `mv`. ## Create a music task ```bash curl https://api.tokenlab.sh/v1/music/generations \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "suno_music", "mv": "chirp-v4", "prompt": "An upbeat synth-pop track with warm vocals and a clean chorus", "title": "Morning Static", "tags": "synth-pop, upbeat", "action": "MUSIC" }' ``` Prompts, titles, and tags may appear in your product history. Never place API keys, private URLs, or diagnostic data in them. ## Get the finished audio Use `poll_url`. If your client needs a fixed URL, call `GET /v1/tasks/{id}` with the returned `id` or `task_id`. ```bash curl "https://api.tokenlab.sh/v1/tasks/$TASK_ID" \ -H "Authorization: Bearer sk-your-api-key" ``` ### Response Shape The create response is a task record, not the final audio: ```json { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "created": 1706000000, "model": "suno_music" } ``` A completed polling response can include the final media fields: ```json { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "completed", "audio_url": "https://api.tokenlab.sh/v1/media-results/2d7e9a41-8c3b-4b6f-a1e5-7f0c3d9b8e26.mp3?token=…", "video_url": "https://api.tokenlab.sh/v1/media-results/9a3f5c17-2e8d-4b0a-b6c4-1d7e8f2a5b39.mp4?token=…", "stream_audio_url": "https://cdn.example.com/music/abc123-stream.mp3", "image_url": "https://api.tokenlab.sh/v1/media-results/c4e8b2d6-7a1f-4e3c-9d5b-3f6a0e8c1b72.jpg?token=…", "title": "Morning Static", "lyrics": "[Verse 1]\n...", "media_retention": { "days": 30, "items": [ { "url": "https://api.tokenlab.sh/v1/media-results/2d7e9a41-8c3b-4b6f-a1e5-7f0c3d9b8e26.mp3?token=…", "kind": "audio", "status": "saving", "expires_at": "2024-02-22T08:54:20.000Z" }, { "url": "https://api.tokenlab.sh/v1/media-results/9a3f5c17-2e8d-4b0a-b6c4-1d7e8f2a5b39.mp4?token=…", "kind": "video", "status": "saving", "expires_at": "2024-02-22T08:54:20.000Z" }, { "url": "https://api.tokenlab.sh/v1/media-results/c4e8b2d6-7a1f-4e3c-9d5b-3f6a0e8c1b72.jpg?token=…", "kind": "image", "status": "saving", "expires_at": "2024-02-22T08:54:20.000Z" } ] } } ``` Final media fields are absent until `status` is `completed`. Failed tasks return `status: "failed"` with `error`. Statuses are `pending`, `processing`, `completed`, and `failed`. A completed task can include `audio_url`, `video_url`, `title`, `lyrics`, and other metadata. The media URLs are TokenLab URLs that stay valid for 30 days; download or save a result that you need for longer. `stream_audio_url` is a provider link for listening while the track is made. It is not a TokenLab media result and stops working when the track is finished. ## Show the right state - Show a pending state immediately after task creation. - Poll every `5-10s` for long tasks, then stop on `completed` or `failed`. - Do not display a final player until the task is `completed` and an `audio_url` exists. - For lyric-only tasks, render text output separately from audio tasks so users understand what they are buying. - On refresh, resume from the stored `task_id` instead of creating a new task. ## Billing records Music tasks can reserve an estimated amount when created. The final amount is recorded after completion or failure. Save `request_id`, `task_id`, model, endpoint, and `billing_transaction_id` when it appears; use Management API usage records for the final charge. ## Common Errors | Symptom | Likely cause | Fix | |---------|--------------|-----| | Task created but no player | Task is still pending or completed without `audio_url` | Keep polling until terminal, then handle missing output as a failed user job | | Duplicate songs after refresh | UI recreated the task instead of resuming | Persist and reuse `task_id` | | Lyric task returns no audio | `action: "LYRICS"` is text-only | Separate lyrics and music UI paths | | Unsupported parameter | The selected model does not accept the field | Remove it or choose a model that documents it | ## API Reference | Topic | Reference | |-------|-----------| | Create Music | [Create Music](/api-reference/music/create-music) | | Get Music Status | [Get Music Status](/api-reference/music/get-music-status) | | Get Task Status | [Get Task Status](/api-reference/tasks/get-task-status) | | List Models | [List Models](/api-reference/models/list-models) | | Billing & Pricing | [Billing & Pricing](/guides/billing) | --- ### Worlds > Create explorable Marble worlds from text, images, or video URL: https://tokenlab.sh/docs/en/guides/worlds Worlds creates a Marble environment that people can open and explore. If you need a downloadable object instead of an environment, use 3D generation. Downloadable World files, such as splats, meshes and panorama images, are kept as automatic 30-day copies with TokenLab media-result URLs. The interactive World page and World records are not. See [Data retention](/guides/data-retention). ## Inputs And Media Assets A world can start from text, one or more images, or video, depending on the model. Reusable media can be uploaded once and referenced by its asset ID. Access to media and world records is checked against the organization that owns the API key. Review the selected model’s `permission` options before sharing a world; a returned asset or viewer URL may have its own access rules. ## Create And Poll The create response contains a task ID and usually a `poll_url`. Keep the ID, then query `poll_url` or `GET /v1/tasks/{id}` until the status is `completed` or `failed`. ```bash curl -X POST "https://api.tokenlab.sh/v1/worlds/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "marble-1.1", "prompt": "A quiet coastal town at sunset with narrow alleys and warm lights" }' ``` ## Manage Completed Worlds Use the Worlds list and get endpoints for completed worlds available in your account. Use task status endpoints for pending or failed generation tasks. Delete only when the user intentionally removes the generated world from your product. ## Common Pitfalls - Do not assume every world model supports every input type. - Keep signed image or video URLs valid long enough for service fetch retries. - Store the returned task ID before retrying after a timeout. - Use 3D generation for downloadable model files rather than explorable worlds. ## API Reference | Topic | Reference | |-------|-----------| | Create World | [Create World](/api-reference/worlds/create-world) | | Get World Status | [Get World Status](/api-reference/worlds/get-world-status) | | Prepare Media Asset Upload | [Prepare Media Asset Upload](/api-reference/worlds/prepare-media-asset-upload) | | Get Media Asset | [Get Media Asset](/api-reference/worlds/get-media-asset) | | List Worlds | [List Worlds](/api-reference/worlds/list-worlds) | | Get World | [Get World](/api-reference/worlds/get-world) | | Delete World | [Delete World](/api-reference/worlds/delete-world) | | Async Jobs | [Async Jobs](/guides/async-jobs-polling) | | 3D Generation Guide | [3D Generation Guide](/guides/3d-generation) | --- ### 3D generation > Create downloadable 3D models from text or images URL: https://tokenlab.sh/docs/en/guides/3d-generation 3D generation is asynchronous. `POST /v1/3d/generations` creates a TokenLab task; completed status responses return downloadable model assets such as `model_url` and, when available, format-specific URLs. Downloadable 3D model files are kept as automatic 30-day copies, and their URLs are TokenLab media-result URLs. Task records and separately saved assets follow their own lifecycle. See [Data retention](/guides/data-retention). ## Choose the input type | Input | Required | Optional fields | Notes | |----------|----------------|-----------------|-------| | Text-to-3D | `model`, `prompt` | `format`, `quality`, `style`, `seed` | Best for generating a new asset from a description | | Image-to-3D | `model`, `prompt`, `image` or `image_url` | `format`, `quality`, `style`, `seed` | Use only when the selected model supports image input | This API does not use an `operation` field. A model with `text-to-3d` accepts prompts; a model with `image-to-3d` accepts image input. ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=3d" \ -H "Authorization: Bearer sk-your-api-key" ``` Do not assume every 3D model supports both input types or every output format. Check the selected model details before sending `image`, `image_url`, `format`, `quality`, `style`, or `seed`. ## Create a 3D task ```bash curl https://api.tokenlab.sh/v1/3d/generations \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "tripo-h3.1", "prompt": "A stylized low-poly robot mascot with clean topology" }' ``` For image-to-3D, use an `https` URL when the source can be fetched from the internet. Use inline base64 `image` when the source must stay private and your server can accept the larger request body. ## Output formats - `glb` works well for web previews. - `fbx` and `obj` are useful for DCC pipelines when the selected model supports them. - `usdz` is useful for Apple AR workflows when exposed by the model. - Higher `quality` values can increase wait time and cost. Show that choice to the user. - `seed` affects repeatability only on models that support it. ## Get the finished model Use the returned `poll_url`. If your client needs a fixed URL, use `GET /v1/tasks/{id}`. ```bash curl "https://api.tokenlab.sh/v1/tasks/$TASK_ID" \ -H "Authorization: Bearer sk-your-api-key" ``` Completed tasks return `model_url` and may include `glb_url`, `fbx_url`, `obj_url`, or `usdz_url`. Download or cache the selected asset in your own product if users need repeat access, version history, or long-lived downloads. ## Keep the result available - Persist `task_id`, `poll_url`, model, requested format, and your own asset record ID. - Resume polling after page refresh rather than creating a duplicate task. - Validate source image size and reachability before creating the task. - Keep generated asset URLs out of public pages unless the user has permission to access the asset. - Record `billing_transaction_id` when present for later reconciliation. ## Common Errors | Symptom | Likely cause | Fix | |---------|--------------|-----| | Create response has no asset URL | 3D generation is async | Poll until terminal status | | Requested format missing | Model did not return that format | Fall back to `model_url` or choose a model that supports the format | | Image-to-3D rejected | Selected model is text-only or image URL is unreachable | Check the model details and validate the URL | | Duplicate assets | Retry path recreated the task after timeout | Store task identity before retrying | ## API Reference | Topic | Reference | |-------|-----------| | Create 3D | [Create 3D](/api-reference/3d/create-3d) | | Get 3D Status | [Get 3D Status](/api-reference/3d/get-3d-status) | | Get Task Status | [Get Task Status](/api-reference/tasks/get-task-status) | | List Models | [List Models](/api-reference/models/list-models) | | Billing & Pricing | [Billing & Pricing](/guides/billing) | --- # API Reference ## Core ### API Reference > Endpoints, authentication, response headers, and errors URL: https://tokenlab.sh/docs/en/api-reference/introduction ## Overview TokenLab supports OpenAI-compatible endpoints plus Anthropic and Gemini request formats. Existing OpenAI clients can use `/v1`; choose another format only when your application needs behavior specific to that API. `POST /v1/responses` is optional and depends on model support. ## Base URL ``` https://api.tokenlab.sh ``` ## Authentication Model requests use a TokenLab API key. The standard authentication header is: ```bash Authorization: Bearer sk-your-api-key ``` `GET /v1/models`, `GET /v1/models/{model}`, and `GET /v1/pricing` are public and need no key. Anthropic Messages also accepts `x-api-key`; Gemini accepts `x-goog-api-key` or `?key=` as well as Bearer authentication. `/v1/management/*` requires a management token (`mt-...`). Get your API key from the [Console](https://tokenlab.sh/dashboard). ## Delivery policy Generation requests accept `X-TokenLab-Delivery-Policy: auto | verified | official`. A request header overrides the API key setting, which overrides the Workspace default. - `Auto` uses TokenLab Verified when available, then Official if needed. You pay for the option that completes your request. - `TokenLab Verified` is delivery verified by TokenLab. The Verified price applies. - `Official` bills the Official price, which is the model maker's published price. - Realtime sessions use the API key or Workspace setting and do not accept a query-string override. An invalid header returns `400`. If the requested option is unavailable, TokenLab returns `503 delivery_tier_unavailable` and a request ID. Read `retryable`: when the selected operation has no supply, it is `false`. Do not repeat the same request unchanged. > Note: API pages with a request form accept your TokenLab API key and send real requests. These requests use your account balance and limits. You can also use cURL, Postman, or an SDK. ## Supported Endpoints ### Chat & Text Generation | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/chat/completions` | POST | OpenAI-compatible chat completions | | `/v1/messages` | POST | Anthropic-compatible messages API | | `/v1/responses` | POST | OpenAI Responses API | ### Embeddings & Rerank | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/embeddings` | POST | Create text embeddings | | `/v1/rerank` | POST | Rerank documents | ### Images | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/images/generations` | POST | Generate images from text | | `/v1/images/edits` | POST | Edit images | | `/v1/images/generations/{id}` | GET | Image task status path for task-based image responses | > Info: Image models may return a finished image or an asynchronous task. If the response includes `poll_url`, use that URL to check the task. ### Audio | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/audio/speech` | POST | Text-to-speech (TTS) | | `/v1/audio/transcriptions` | POST | Speech-to-text (STT) | ### Realtime | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/realtime?model={model}` | WS | Realtime WebSocket sessions | > Info: Use `/v1/realtime` for WebSocket upgrades. A plain `GET /v1/realtime` returns endpoint metadata. This is not the OpenAI Realtime REST surface; client-secret, Calls, and legacy beta session endpoints are not available. ### Video | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/videos/generations` | POST | Create video generation task | | `/v1/tasks/{id}` | GET | Get async task status for video jobs | | `/v1/videos/generations/{id}` | GET | Legacy-compatible video task status path | > Info: Use the `poll_url` returned when the task is created. `/v1/videos/generations/{id}` remains available for older clients. ### Async Tasks | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/tasks/{id}` | GET | Status for an asynchronous task | > Info: Video, music, 3D, and some image requests may return this endpoint in `poll_url`. ### Music | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/music/generations` | POST | Create music generation task | | `/v1/music/generations/{id}` | GET | Music-specific status path | > Info: Use the returned `poll_url`. `/v1/music/generations/{id}` remains available for clients that need the music-specific path. ### 3D Generation | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/3d/generations` | POST | Create 3D model generation task | | `/v1/3d/generations/{id}` | GET | 3D-specific status path | > Info: Use the returned `poll_url`. `/v1/3d/generations/{id}` remains available for clients that need the 3D-specific path. ### Models | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/models` | GET | List all available models | | `/v1/models/{model}` | GET | Get specific model info | ### Gemini (v1beta) Native Google Gemini API format support: | Endpoint | Method | Description | |----------|--------|-------------| | `/v1beta/models/{model}:generateContent` | POST | Generate content (Gemini format) | | `/v1beta/models/{model}:streamGenerateContent` | POST | Stream generate content (Gemini format) | > Note: Gemini endpoints support `?key=` query parameter authentication in addition to standard Bearer token. ## Response Format Each endpoint preserves its API format. The success and error examples below use Chat Completions format. ### Success Response ```json { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1234567890, "model": "gpt-5.6-terra", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Hello!"}, "finish_reason": "stop"}], "usage": { "prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30 } } ``` ### Request identifiers Official request rejections may include `upstream` with the service’s reported error and public service name. Private delivery details and credentials are not part of the public response contract. See [Handle API errors](/guides/error-handling). Use the public headers below when they are present. | Header | Description | |-------|-------------| | `X-Routing-Time-MS` | Time spent selecting delivery, when available | | `X-Request-ID` | Request identifier for support and debugging, when available | | `X-Task-ID` | Public async task identifier for task-based responses, when available | | `X-Billing-Transaction-ID` | Billing transaction identifier after final billing, when available | ### Error Response ```json { "error": { "message": "Invalid API key provided", "type": "invalid_api_key", "code": "invalid_api_key" } } ``` ## Rate Limits Rate limits are role-based and configurable by administrators. Default values: | Role | Requests/min | |------|-------------| | User | 1,000 | | Partner | 10,000 | | VIP | 10,000 | > Note: Contact support for custom rate limits. Exact values may vary by account configuration. When rate limits are exceeded, the API returns a `429` status code with a `Retry-After` header indicating how long to wait. ## OpenAPI Specification #### [OpenAPI Spec](/openapi.json) Download the complete OpenAPI 3.1 specification --- ### List Models > List models currently available on TokenLab URL: https://tokenlab.sh/docs/en/api-reference/models/list-models The model catalog is public and does not require an API key. Each item includes a summary of current capabilities, prices, and API formats. Retrieve one model when you need full details. When you provide an API key, TokenLab validates it and filters the catalog for its model permissions and effective delivery policy. A key set to `verified` lists models with TokenLab Verified supply; `official` lists models with Official supply. Refresh your model selector when the key or delivery policy changes. The request's supported delivery override takes precedence over the key and workspace defaults. ## Query Parameters #### category Filter by `chat`, `image`, `video`, `audio`, `tts`, `stt`, `music`, `3d`, `embedding`, `rerank`, or `translation`. #### recommended_for Return current recommendations for a task such as `image`, `video`, `music`, `3d`, `tts`, `stt`, `embedding`, `rerank`, or `translation`. #### provider Filter by model maker, such as `openai`, `anthropic`, `google`, or `deepseek`. #### tag Filter by a tag such as `chat`, `image`, `video`, `embedding`, or `translation`. ## Response #### object Always `list`. #### data Models with `id`, `owned_by`, and a `tokenlab` summary. Common list fields under `tokenlab` include: - `tokenlab.aliases` - `tokenlab.capabilities` - `tokenlab.deliveryAvailability`: configured `verified` and `official` support, when provided. A missing field means unknown; support does not guarantee real-time availability. - `tokenlab.pricing`: the Verified price. Its price fields are `null` when `tokenlab.deliveryAvailability.verified` is `false`; such a model is billed at the Official price. - `tokenlab.official_pricing`: the Official price. Requests that Official delivers are billed at this price. - `tokenlab.pricing_unit` - `tokenlab.max_input_tokens` and `tokenlab.max_output_tokens` - `tokenlab.category` - `tokenlab.lifecycle`, `tokenlab.commercial`, and `tokenlab.badges` - `tokenlab.request_format_summary` - `tokenlab.agent_preferences` when `recommended_for` is used When `recommended_for` is used, results may also include recommendation status and an update time. Read the current response instead of pinning the order. ## Example ```bash curl "https://api.tokenlab.sh/v1/models?category=video" ``` ```json { "object": "list", "data": [ { "id": "veo3.1", "object": "model", "owned_by": "google", "tokenlab": { "category": "video", "capabilities": ["text-to-video", "image-to-video"] } } ] } ``` See [Get a Model](/api-reference/models/get-model) for complete details. --- ### Get a Model > Get a model's capabilities, pricing, and API formats URL: https://tokenlab.sh/docs/en/api-reference/models/get-model ## Path Parameter #### model Model ID, such as `gpt-5.6-terra` or `claude-sonnet-4-6`. ## Response #### id Model ID. #### object Always `model`. #### owned_by Model maker. #### tokenlab TokenLab model information, including capabilities, prices, billing units, context limits, accepted API formats, operations, and lifecycle status. Common `tokenlab` fields include: - `capabilities`: text, image, video, audio, and other capabilities. - `pricing`: the Verified price. Its price fields are `null` when `deliveryAvailability.verified` is `false`; such a model is billed at the Official price. - `official_pricing`: the Official price. Requests that Official delivers are billed at this price. - `max_input_tokens` and `max_output_tokens`: context and output limits. - `accepted_request_formats`: supported API formats. - `supported_operations`: operations such as text-to-image or image-to-video. - `lifecycle`: availability, release date, deprecation date, or replacement model. Some media models also include a recommendation status and update time. Recommendations can change with availability and performance, so do not hard-code their order. ## Example ```bash curl "https://api.tokenlab.sh/v1/models/gpt-5.6-terra" ``` ```json { "id": "gpt-5.6-terra", "object": "model", "owned_by": "openai", "tokenlab": { "category": "chat", "capabilities": ["coding", "json-mode", "prompt-cache", "reasoning", "tool-use", "vision"], "accepted_request_formats": ["openai_chat_completions", "openai_responses"] } } ``` An unknown model returns `404 model_not_found`. --- ### Get Pricing > Get pricing information for models URL: https://tokenlab.sh/docs/en/api-reference/pricing/get-pricing This public endpoint does not require an API key. ## Overview Return current public model prices. For complex billing details, see [Get a Model](/api-reference/models/get-model). ## Query Parameters #### provider Optional provider filter such as `openai`, `anthropic`, or `google`. #### tag Optional model tag filter such as `chat`, `image`, `video`, `embedding`, or `translation`. ## Response #### object Always `pricing`. #### updated_at ISO timestamp when the current public prices were published. #### currency Always `USD`. #### data Array of pricing entries. Each item includes `model`, `provider`, `is_lock_price`, `capabilities`, `aliases`, and the public scalar or structured pricing summary fields returned by the interactive schema above. ```bash cURL curl "https://api.tokenlab.sh/v1/pricing?provider=openai&tag=chat" ``` ```python Python import requests response = requests.get( "https://api.tokenlab.sh/v1/pricing", params={"provider": "openai", "tag": "chat"} ) print(response.json()) ``` ```javascript JavaScript const response = await fetch( 'https://api.tokenlab.sh/v1/pricing?provider=openai&tag=chat' ); const data = await response.json(); console.log(data); ``` ```go Go package main import ( "encoding/json" "fmt" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.tokenlab.sh/v1/pricing?provider=openai&tag=chat", nil) client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) } ``` ```php PHP true ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); print_r($data); ``` ```json Response { "object": "pricing", "updated_at": "2026-04-20T00:00:00.000Z", "currency": "USD", "data": [ { "model": "gpt-5.5", "provider": "openai", "deliveryAvailability": { "verified": true, "official": true }, "is_lock_price": false, "input_per_1m_tokens": "1.5", "output_per_1m_tokens": "9", "official_input_per_1m_tokens": "5", "official_output_per_1m_tokens": "30", "capabilities": ["chat"], "aliases": [] }, { "model": "grok-4.20-multi-agent", "provider": "xai", "deliveryAvailability": { "verified": false, "official": true }, "is_lock_price": false, "official_input_per_1m_tokens": "1.25", "official_output_per_1m_tokens": "2.5", "capabilities": ["chat"], "aliases": [] } ] } ``` > Note: `input_per_1m_tokens`, `output_per_1m_tokens`, and `lock_price_per_request` are Verified prices. They are omitted when `deliveryAvailability.verified` is `false`; such a model is billed at its Official price (`official_input_per_1m_tokens`, `official_output_per_1m_tokens`, or `official_lock_price_per_request`). Complex pricing entries may also include `has_complex_pricing`, `supported_operations`, and `pricing_summary`. ## Related interfaces `GET /v1/models/{model}/pricing` Use the pricing-only endpoint for prices; model details remain the source for request fields and supported operations. --- ## Text ### Create Chat Completion > Creates a completion for the chat message URL: https://tokenlab.sh/docs/en/api-reference/chat/create-completion ## Request Body > Note: Supported optional parameters, accepted values, and defaults depend on the selected model. Check its [model details](/api-reference/models/get-model) before setting sampling, reasoning, or tool options. #### model ID of the model to use. See [Models](https://tokenlab.sh/en/models) for available options. #### messages A list of messages comprising the conversation. Each message object contains: - `role` (string): `system`, `developer`, `user`, `assistant`, `tool`, `function` - `content` (string | array | null): The message content For an `assistant` message containing `tool_calls`, `content` may be omitted or set to `null`. When `content` is an array, TokenLab supports structured multimodal blocks for compatible models: - text: `{ "type": "text", "text": "..." }` - image: `{ "type": "image_url", "image_url": { "url": "https://..." } }` - video: `{ "type": "video_url", "video_url": { "url": "https://..." } }` - audio: `{ "type": "audio_url", "audio_url": { "url": "https://..." } }` For multimodal input, use publicly reachable `https` URLs. Supported media types depend on the selected model. #### temperature Sampling temperature. Support, allowed values, and the default depend on the selected model; omit this field to use its default. #### max_tokens Maximum number of tokens to generate. #### stream If true, partial message deltas will be sent as SSE events. #### stream_options Options for streaming. Set `include_usage: true` to receive token usage in stream chunks. #### top_p Nucleus sampling parameter. We recommend altering this or temperature, not both. #### frequency_penalty Number between -2.0 and 2.0. Positive values penalize repeated tokens. #### presence_penalty Number between -2.0 and 2.0. Positive values penalize tokens already in the text. #### stop Stop sequence or list of sequences. Support and sequence limits depend on the selected model. #### tools A list of tools the model may call (function calling). #### tool_choice Controls how the model uses tools. Options: `auto`, `none`, `required`, or a specific tool object. #### parallel_tool_calls Allow multiple tool calls in one assistant turn, when supported by the selected model. #### max_completion_tokens Maximum tokens for the completion. Alternative to `max_tokens`, useful for newer reasoning-enabled model families. #### reasoning_effort Reasoning effort for models that support it. Accepted values depend on the selected model. For Claude models, the value sets the model's thinking: an effort level on models with adaptive thinking, or a thinking budget on models that use one. `none` turns thinking off on models that allow it. While thinking is on, Claude rejects a `temperature` other than 1, `top_k`, a `top_p` outside 0.95–1, and a prefilled assistant message. A forced `tool_choice` works with adaptive thinking but not with a thinking budget. #### anthropic_options Options for Claude models. - `prompt_caching` (boolean): Set `true` to cache the start of the prompt. TokenLab marks the end of the system prompt and the latest user message as cache points. A later request that starts with the same content reads it from the cache. If any message already has `cache_control`, TokenLab adds no cache points. - `prompt_caching_ttl` (string): `5m` (default) or `1h`. If the serving route does not support one hour, TokenLab uses five minutes and does not reject the request. `extra.prompt_caching` and `extra.prompt_caching_ttl` are accepted as the same options. See [Prompt caching](/guides/billing#prompt-caching) for prices and how to check the cached tokens. #### seed Sampling seed for models that support it. Identical output is not guaranteed. #### n Number of completions to generate (1-128). #### logprobs Whether to return log probabilities. #### top_logprobs Number of top log probabilities to return (0-20). Requires `logprobs: true`. #### top_k Top-K sampling for models that support it. #### response_format Response format. Use `{"type": "json_object"}` for JSON mode or `{"type": "json_schema", "json_schema": {...}}` for a JSON schema. Support depends on the selected model. #### logit_bias Modify the likelihood of specified tokens appearing. Map token IDs (as strings) to bias values from -100 to 100. #### user A unique identifier representing your end-user for abuse monitoring. ## Response #### id Unique identifier for the completion. #### object Always `chat.completion`. #### created Unix timestamp of when the completion was created. #### model The model used for completion. #### choices List of completion choices. Each choice contains: - `index` (integer): Index of the choice - `message` (object): The generated message - `finish_reason` (string): Why the model stopped, for example `stop`, `length`, or `tool_calls` When the model thinks, `message` can also contain: - `reasoning_content` (string): The thinking text. Some models return only a summary, or no text. - `reasoning_details` (array): Thinking blocks with their signatures. To continue a conversation with thinking, for example after a tool call, send them back unchanged in the assistant message. In a stream, the same fields arrive as `delta.reasoning_content` and `delta.reasoning_details`. #### usage Token usage statistics. - `prompt_tokens` (integer): Tokens in the prompt - `completion_tokens` (integer): Tokens in the completion, including thinking tokens - `total_tokens` (integer): Total tokens used - `completion_tokens_details.reasoning_tokens` (integer): Thinking tokens, when the model reports them ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/chat/completions" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"} ], "max_tokens": 1000 }' ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.chat.completions.create( model="gpt-5.6-terra", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"} ], max_tokens=1000 ) print(response.choices[0].message.content) ``` ```javascript JavaScript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.chat.completions.create({ model: 'gpt-5.6-terra', messages: [ { role: 'system', content: 'You are a helpful assistant.' }, { role: 'user', content: 'Hello!' } ], max_tokens: 1000 }); console.log(response.choices[0].message.content); ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'gpt-5.6-terra', 'messages' => [ ['role' => 'system', 'content' => 'You are a helpful assistant.'], ['role' => 'user', 'content' => 'Hello!'] ], 'max_tokens' => 1000 ]) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['choices'][0]['message']['content']; ``` ## Multimodal Example ```json { "model": "gemini-2.5-pro", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this video briefly." }, { "type": "video_url", "video_url": { "url": "https://example.com/demo.mp4" } } ] } ], "max_tokens": 64 } ``` ```json Response { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1706000000, "model": "gpt-5.6-terra", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Hello! How can I help you today?" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 20, "completion_tokens": 9, "total_tokens": 29 } } ``` --- ### Create Response > Creates a response using the OpenAI Responses API format URL: https://tokenlab.sh/docs/en/api-reference/responses/create-response Use this endpoint when a model's `accepted_request_formats` includes `openai_responses`. Requests and responses use Responses API format. Optional fields and supported values depend on the selected model. ## Request Body #### model ID of the model to use. See [Models](https://tokenlab.sh/en/models) for available options. #### input Input for the response. It is optional when the request instead uses a reusable `prompt` or continues a stored response with `previous_response_id`. Each item can be: - `message`: A conversation message with role and content - `function_call`: A function call request - `function_call_output`: Output from a function call For multimodal input, `message.content` can be either a plain string or an array of content blocks. For image-capable models such as GPT-5.6 Terra variants, pass images as `input_image` blocks instead of embedding URLs or Base64 strings directly into plain text. Example content blocks: - `{ "type": "input_text", "text": "Describe this image" }` - `{ "type": "input_image", "image_url": "https://example.com/image.jpg" }` - `{ "type": "input_image", "image_url": "data:image/png;base64,..." }` #### instructions System instructions for the model (equivalent to system message). #### max_output_tokens Maximum number of tokens to generate. #### temperature Sampling temperature. Supported values and defaults depend on the selected model. #### tools Tools the model may call. Supported types and combinations depend on the selected model. #### stream If true, returns a stream of events. #### previous_response_id ID of a previous response to continue the conversation from. #### store Whether to store the response for later retrieval. #### background Request asynchronous execution. Support depends on the selected model. #### prompt Reference to a reusable prompt template and variables. #### metadata Custom metadata stored with the response. #### text Text output settings. Support for `text.format` depends on the selected model. #### parallel_tool_calls Whether to allow multiple tool calls in parallel. #### top_p Nucleus sampling parameter (0-1). #### reasoning Reasoning options, including `effort`, depend on the selected model. ## Response #### id Unique identifier for the response. #### object Always `response`. #### created_at Unix timestamp of when the response was created. #### status Response state: `queued`, `in_progress`, `completed`, `incomplete`, `failed`, or `cancelled`. Check this field even when the HTTP request succeeds. #### output List of output items generated by the model. #### usage Token usage statistics. ## Related Endpoints - `GET /v1/responses/{id}` retrieves a stored response and supports `include`, `include_obfuscation`, `stream`, and `starting_after`. - `DELETE /v1/responses/{id}` deletes a stored response. It does not cancel generation. - `POST /v1/responses/compact` returns a `response.compaction` object. - Cancel, input-items, and input-tokens endpoints are not currently available. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/responses" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "input": [ {"type": "message", "role": "user", "content": "Hello!"} ], "max_output_tokens": 1000 }' ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.responses.create( model="gpt-5.6-terra", input=[ {"type": "message", "role": "user", "content": "Hello!"} ], max_output_tokens=1000 ) print(response.output) ``` ```javascript JavaScript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.responses.create({ model: 'gpt-5.6-terra', input: [ { type: 'message', role: 'user', content: 'Hello!' } ], max_output_tokens: 1000 }); console.log(response.output); ``` ```go Go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { payload := map[string]interface{}{ "model": "gpt-5.6-terra", "input": []map[string]interface{}{ {"type": "message", "role": "user", "content": "Hello!"}, }, "max_output_tokens": 1000, } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1/responses", bytes.NewBuffer(body)) req.Header.Set("Authorization", "Bearer sk-your-api-key") req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result["output"]) } ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'gpt-5.6-terra', 'input' => [ ['type' => 'message', 'role' => 'user', 'content' => 'Hello!'] ], 'max_output_tokens' => 1000 ]) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); print_r($data['output']); ``` ## Vision Input Example Use image-capable models by placing images inside `message.content` as `input_image` blocks. The `image_url` value can be either a public URL or a Base64 data URL. ```json { "model": "gpt-5.6-terra", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "Please describe this image." }, { "type": "input_image", "image_url": "https://example.com/demo.jpg" } ] } ] } ``` ```json { "model": "gpt-5.6-terra", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "Please describe this image." }, { "type": "input_image", "image_url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..." } ] } ] } ``` ```json Response { "id": "resp_abc123", "object": "response", "created_at": 1706000000, "status": "completed", "model": "gpt-5.6-terra", "output": [ { "id": "msg_abc123", "type": "message", "status": "completed", "role": "assistant", "content": [ {"type": "output_text", "text": "Hello! How can I help you today?", "annotations": []} ] } ], "usage": { "input_tokens": 10, "output_tokens": 12, "total_tokens": 22 } } ``` --- ### Create Message > Creates a message using the Anthropic Messages API format URL: https://tokenlab.sh/docs/en/api-reference/messages/create-message ## Overview This endpoint provides native Anthropic Messages API compatibility. Use this for Claude models with features like extended thinking. This endpoint keeps the native Anthropic contract. `messages` must be an array of `user` / `assistant` messages, `system` belongs in the top-level `system` field, and `max_tokens` is required. If your payload uses OpenAI roles such as `system`, `developer`, or `tool` inside `messages`, send it to `/v1/chat/completions` instead. > Note: Base URL for Anthropic SDK: `https://api.tokenlab.sh` (no `/v1` suffix) ## Request Headers #### x-api-key Your TokenLab API key. Send either `x-api-key` or `Authorization: Bearer `. #### anthropic-version Anthropic API version. Use `2023-06-01`. ## Request Body > Note: Supported optional parameters, accepted values, and defaults depend on the selected model. Check its [model details](/api-reference/models/get-model) before setting sampling, reasoning, or tool options. #### model ID of a model that supports the `anthropic_messages` format. Check its [model details](/api-reference/models/get-model) for current capabilities. #### messages Array of message objects with `role` and `content`. For Claude models with vision support, `content` can be either a plain string or an array of content blocks. To send images, use structured content blocks rather than placing image URLs or Base64 strings directly into plain text. Example content blocks: - text block: `{ "type": "text", "text": "Describe this image" }` - image block via URL: `{ "type": "image", "source": { "type": "url", "url": "https://example.com/image.jpg" } }` - image block via Base64: `{ "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "iVBORw0KGgoAAA..." } }` #### max_tokens Maximum tokens to generate. #### system System prompt as a string or an array of content blocks, separate from the `messages` array. #### temperature Sampling temperature. Support, allowed values, and the default depend on the selected model; omit this field to use its default. #### stream Enable streaming responses. #### thinking Native thinking configuration. Supported modes, budgets, and combinations with other parameters depend on the selected model. #### tools Available tools for the model. #### tool_choice Native tool-choice object, for example `{"type":"auto"}`. Supported types depend on the selected model. #### top_p Nucleus sampling parameter. Use either temperature or top_p, not both. #### top_k Only sample from the top K options for each token. #### stop_sequences Custom stop sequences that will cause the model to stop generating. #### metadata Metadata to attach to the request for tracking purposes. ## Response #### id Unique message identifier. #### type Always `message`. #### role Always `assistant`. #### content Array of content blocks, such as `text`, `thinking`, or `tool_use`. #### model Model used. #### stop_reason Reason generation stopped, such as `end_turn`, `max_tokens`, `stop_sequence`, or `tool_use`. #### usage Token usage with `input_tokens` and `output_tokens`. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/messages" \ -H "x-api-key: sk-your-api-key" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-5", "max_tokens": 1024, "system": "You are a helpful assistant.", "messages": [ {"role": "user", "content": "Hello, Claude!"} ] }' ``` ```python Python from anthropic import Anthropic client = Anthropic( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh" ) message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, system="You are a helpful assistant.", messages=[ {"role": "user", "content": "Hello, Claude!"} ] ) print(message.content[0].text) ``` ```javascript JavaScript import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh' }); const message = await client.messages.create({ model: 'claude-sonnet-5', max_tokens: 1024, system: 'You are a helpful assistant.', messages: [ { role: 'user', content: 'Hello, Claude!' } ] }); console.log(message.content[0].text); ``` ```go Go package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) func main() { payload := map[string]interface{}{ "model": "claude-sonnet-5", "max_tokens": 1024, "system": "You are a helpful assistant.", "messages": []map[string]string{ {"role": "user", "content": "Hello, Claude!"}, }, } jsonData, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1/messages", bytes.NewBuffer(jsonData)) req.Header.Set("x-api-key", "sk-your-api-key") req.Header.Set("anthropic-version", "2023-06-01") req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```php PHP 'claude-sonnet-5', 'max_tokens' => 1024, 'system' => 'You are a helpful assistant.', 'messages' => [ ['role' => 'user', 'content' => 'Hello, Claude!'] ] ]; $ch = curl_init('https://api.tokenlab.sh/v1/messages'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'x-api-key: sk-your-api-key', 'anthropic-version: 2023-06-01', 'Content-Type: application/json' ], CURLOPT_POSTFIELDS => json_encode($payload) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['content'][0]['text']; ``` ```json Response { "id": "msg_abc123", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "Hello! How can I help you today?" } ], "model": "claude-sonnet-5", "stop_reason": "end_turn", "usage": { "input_tokens": 15, "output_tokens": 10 } } ``` ## Vision Input Example For Claude models with vision support, place images inside `messages[].content` as structured image blocks. ```json { "model": "claude-sonnet-5", "max_tokens": 1024, "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Please describe this image." }, { "type": "image", "source": { "type": "url", "url": "https://example.com/demo.jpg" } } ] } ] } ``` ```json { "model": "claude-sonnet-5", "max_tokens": 1024, "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Please describe this image." }, { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "/9j/4AAQSkZJRgABAQ..." } } ] } ] } ``` ## Extended Thinking Example Set `TOKENLAB_THINKING_MODEL` to a model that supports manual thinking with `type: "enabled"` and `budget_tokens`. Adjust both token budgets to that model's limits; other models may require a different thinking configuration. ```python import os from anthropic import Anthropic client = Anthropic( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh" ) message = client.messages.create( model=os.environ["TOKENLAB_THINKING_MODEL"], max_tokens=16000, thinking={ "type": "enabled", "budget_tokens": 10000 }, messages=[{"role": "user", "content": "Solve this math problem..."}] ) for block in message.content: if block.type == "thinking": print(f"Thinking: {block.thinking}") elif block.type == "text": print(f"Response: {block.text}") ``` ## Anthropic Message Batches TokenLab now exposes the native Anthropic Message Batches flow alongside `/v1/messages`. Available routes: - `POST /v1/messages/batches` - `GET /v1/messages/batches` - `GET /v1/messages/batches/:message_batch_id` - `GET /v1/messages/batches/:message_batch_id/results` - `POST /v1/messages/batches/:message_batch_id/cancel` - `DELETE /v1/messages/batches/:message_batch_id` Operational notes: - Use the same TokenLab API key plus Anthropic-native headers. - If batch items reference `file_id`, also include `anthropic-beta: files-api-2025-04-14`. - Batch jobs keep Anthropic-native request/response shapes while TokenLab tracks billing status for reconciliation. ## Related interfaces `POST /v1/messages/count_tokens` Token counting requires an API key and is not billed. The returned input count is an estimate for planning; use the final response usage and billing record for the charge. Verified upstream token counting is preferred. If the upstream counter is unavailable, TokenLab returns a local estimate with `X-TokenLab-Count-Tokens-Source: local_estimate`. Authentication, permission and invalid-input errors still fail; requests referencing bound files that cannot be read locally require upstream counting. --- ### Create Embedding > Creates an embedding vector representing the input text URL: https://tokenlab.sh/docs/en/api-reference/embeddings/create-embedding > Note: Use `GET /v1/models?recommended_for=embedding` to get current embedding models. ## Request Body This endpoint returns after the vectors are ready. For large batches, set your HTTP client timeout to at least `120s`. #### model ID of the embedding model to use (e.g., `text-embedding-3-small`). #### input Text to embed: a string or an array of strings. Models that support token input also accept `number[]` for one token sequence or `number[][]` for multiple sequences. #### encoding_format Format for the embeddings: `float` or `base64`. #### dimensions Number of dimensions for the output (model-specific). #### user A unique identifier representing your end-user for abuse monitoring. ## Choose a Model Use `GET /v1/models?recommended_for=embedding` for current models and dimensions. ## Response #### object Always `list`. #### data Array of embedding objects. Each object contains: - `object` (string): `embedding` - `index` (integer): Index in the input array - `embedding` (array | string): A numeric vector for `float`, or a Base64 string for `base64`. #### model Model used. #### usage Token usage with `prompt_tokens` and `total_tokens`. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/embeddings" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "text-embedding-3-small", "input": "The quick brown fox jumps over the lazy dog" }' ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.embeddings.create( model="text-embedding-3-small", input="The quick brown fox jumps over the lazy dog" ) embedding = response.data[0].embedding print(f"Embedding dimension: {len(embedding)}") print(f"First 5 values: {embedding[:5]}") ``` ```javascript JavaScript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.embeddings.create({ model: 'text-embedding-3-small', input: 'The quick brown fox jumps over the lazy dog' }); console.log(response.data[0].embedding.slice(0, 5)); ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'text-embedding-3-small', 'input' => 'The quick brown fox jumps over the lazy dog' ]) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); print_r(array_slice($data['data'][0]['embedding'], 0, 5)); ``` > Note: The example vector is shortened for readability. ```json Response { "object": "list", "data": [ { "object": "embedding", "index": 0, "embedding": [0.0023, -0.0194, 0.0081] } ], "model": "text-embedding-3-small", "usage": { "prompt_tokens": 9, "total_tokens": 9 } } ``` ## Batch Embeddings ```python # Embed multiple texts at once response = client.embeddings.create( model="text-embedding-3-small", input=[ "First document text", "Second document text", "Third document text" ] ) for i, data in enumerate(response.data): print(f"Document {i}: {len(data.embedding)} dimensions") ``` ## Related interfaces `POST /v1/multimodal/embeddings` Read the selected model’s current model details before using another interface. Availability and request fields depend on the model. Multimodal input uses one object or an array of objects with `text`, `image_url`, or `image_base64` plus `image_mime_type`. Image input also requires enabled image support and explicit support in the model details. It is not available for every embedding model. --- ### Rerank Documents > Reranks documents by relevance to a query URL: https://tokenlab.sh/docs/en/api-reference/rerank/create-rerank > Note: Use `GET /v1/models?recommended_for=rerank` to get current rerank models. Rerank documents using semantic similarity models. Useful for improving search results and RAG applications. ## Request Body This endpoint returns after ranking finishes. For large document sets, set your HTTP client timeout to at least `120s`. #### model ID of the reranker model to use (e.g., `qwen3-vl-rerank`). #### query The query to rank documents against. Maximum length: `32,000` characters. #### documents List of documents (strings) to rerank. Limits: up to `1,000` documents, each document up to `100,000` characters, and at most `2,000,000` total document characters. #### top_n Number of top results to return, from `1` to `documents.length`. If omitted, the selected model determines the number of results. #### return_documents Whether to include original document text in response. ## Response #### results Ranked list of documents with scores. Each result contains: - `index` (integer): Original document index - `relevance_score` (number): Relevance score. Its range depends on the selected model. - `document` (string): Original text (if `return_documents=true`) #### model The model used for reranking. When available. #### usage Token usage statistics. When available. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/rerank" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-vl-rerank", "query": "What is machine learning?", "documents": [ "Machine learning is a subset of AI", "The weather is nice today", "Deep learning uses neural networks" ], "top_n": 2, "return_documents": true }' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/rerank", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "qwen3-vl-rerank", "query": "What is machine learning?", "documents": [ "Machine learning is a subset of AI", "The weather is nice today", "Deep learning uses neural networks" ], "top_n": 2 } ) print(response.json()) ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/rerank', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'qwen3-vl-rerank', query: 'What is machine learning?', documents: [ 'Machine learning is a subset of AI', 'The weather is nice today', 'Deep learning uses neural networks' ], top_n: 2 }) }); const data = await response.json(); console.log(data.results); ``` ```go Go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { payload := map[string]interface{}{ "model": "qwen3-vl-rerank", "query": "What is machine learning?", "documents": []string{ "Machine learning is a subset of AI", "The weather is nice today", "Deep learning uses neural networks", }, "top_n": 2, } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1/rerank", bytes.NewBuffer(body)) req.Header.Set("Authorization", "Bearer sk-your-api-key") req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result["results"]) } ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'qwen3-vl-rerank', 'query' => 'What is machine learning?', 'documents' => [ 'Machine learning is a subset of AI', 'The weather is nice today', 'Deep learning uses neural networks' ], 'top_n' => 2 ]) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); print_r($data['results']); ``` ```json Response { "results": [ { "index": 0, "relevance_score": 0.95, "document": "Machine learning is a subset of AI" }, { "index": 2, "relevance_score": 0.82, "document": "Deep learning uses neural networks" } ], "model": "qwen3-vl-rerank", "usage": { "prompt_tokens": 45, "total_tokens": 45 } } ``` --- ### Evaluate Decisions > Evaluate named probability, choice, and score questions with a native decision model. URL: https://tokenlab.sh/docs/en/api-reference/systemone/create-decision Send `state` and named `questions` to a decision model. Use `GET /v1/models?category=decision` for current availability and `GET /v1/models/{model}` for its published contract. `state` and each question's `instructions` accept a string, JSON object, or JSON array. The endpoint returns synchronous JSON. | Question type | Criteria | Result | | --- | --- | --- | | `noul` | Optional object with both `true` and `false` guidance | `noul`, a probability from 0 to 1 | | `choice` | Object of 1–255 option names mapped to guidance or `null` | `choice`, optionally `probabilities` and `confidence` | | `score` | Ordered array of 2–10 descriptions | `score` from 0 to the last index; it can be fractional, with optional `legend`, `probabilities`, and `confidence` | The response preserves your question names under `answers`. `usage.input_tokens` and `usage.output_tokens` report observed usage when available. Pricing comes from the model's current tariff; free output does not imply zero output tokens. Chat messages, tools, streaming, and Batch are not part of this endpoint's contract. ```bash curl https://api.tokenlab.sh/v1/systemone \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "jev-1.13", "state": "I was charged twice. Please refund the duplicate payment.", "questions": { "refund_requested": {"type": "noul", "instructions": "Is a refund requested?"}, "department": { "type": "choice", "instructions": "Which team should handle this?", "criteria": {"billing": "Charges and refunds", "technical": "Software bugs"} }, "urgency": { "type": "score", "instructions": "Rate urgency", "criteria": ["Routine enquiry", "Money affected", "Safety emergency"] } } }' ``` The HTTP request is also usable with standard `fetch` or `requests` clients. A decision response is structured data, not a generated text explanation. --- ### Create Translation > Translates text into a target language URL: https://tokenlab.sh/docs/en/api-reference/translation/create-translation ## Overview Use `/v1/translations` for **text-to-text translation**. > Note: This endpoint is different from [Audio Translation](/api-reference/audio/create-translation), which accepts an audio file and always outputs English text. Use the Models API to get current text-translation models: ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=translation" ``` Then explicitly send the chosen model to `/v1/translations`. > Note: `recommended_for=translation` applies only to **text translation** (`POST /v1/translations`). It does not apply to [audio translation](/api-reference/audio/create-translation). ## Request Body #### model Translation model ID returned by `/v1/models?recommended_for=translation`. #### text Source text to translate. #### target_language Target language code or language name, depending on model support. #### source_language Optional source language hint. #### mime_type Source text format. Supported values: `text/plain`, `text/html`. #### user Optional end-user identifier for abuse monitoring and request tracing. ## Response #### text Translated text. #### model Model that generated the translation. #### source_language Source language detected or accepted by the model. #### target_language Target language used for the request. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/translations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seed-translation", "text": "你好,欢迎使用 TokenLab。", "target_language": "en" }' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/translations", headers={ "Authorization": "Bearer sk-your-api-key", "Content-Type": "application/json", }, json={ "model": "doubao-seed-translation", "text": "你好,欢迎使用 TokenLab。", "target_language": "en", }, timeout=30, ) print(response.json()["text"]) ``` ```json Response { "text": "Hello, welcome to TokenLab.", "model": "doubao-seed-translation", "source_language": "zh", "target_language": "en" } ``` --- ## Files & Batches ### Upload File > Upload a batch file, image reference, or Anthropic-format file. URL: https://tokenlab.sh/docs/en/api-reference/files/upload-file ## File Purposes - Batch files: use `purpose=batch` for `/v1/batches`. - Image/reference files: use `purpose=user_data` or `purpose=vision` for file-backed image requests. - Anthropic Files: add the `anthropic-beta: files-api-2025-04-14` header. ## Example ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/files" \ -H "Authorization: Bearer sk-your-api-key" \ -F "purpose=batch" \ -F "file=@batch-input.jsonl;type=application/jsonl" ``` ## Image File Inputs This same `/v1/files` route can upload image reference files for `/v1/images/edits`. - Use `purpose=user_data` or `purpose=vision`. - Optionally include `model` to select the image-edit model; it defaults to `gpt-image-2`. - The returned `file_id` is bound to the selected image-edit configuration and can be reused in `images[].file_id`. ## Anthropic Files API Mode - Add `anthropic-beta: files-api-2025-04-14` to use Anthropic Files. - In Anthropic mode, upload the original file directly and do not send `purpose=batch`. - The returned `file_id` is available only in the current Workspace and can be reused in `/v1/messages` requests. - If you plan to reference the uploaded file from Anthropic message content, keep using Anthropic-native headers (`x-api-key`, `anthropic-version`, and the beta header when required). ## Response example ```json 200 OK { "id": "file_abc123", "object": "file", "bytes": 32768, "created_at": 1706000000, "filename": "batch-input.jsonl", "purpose": "batch" } ``` ## Important fields #### id File ID. #### purpose Purpose assigned to the file. Uploads accept `batch`, `user_data`, or `vision`. #### bytes File size in bytes. --- ### List Files > List uploaded batch and image-reference files. URL: https://tokenlab.sh/docs/en/api-reference/files/list-files List batch and image-reference files. Filter with `purpose=batch`, `purpose=batch_output`, `purpose=user_data`, or `purpose=vision`. With Anthropic Files headers, the same API lists Anthropic-format files in the current Workspace. ## Example ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/files?purpose=batch&limit=20" \ -H "Authorization: Bearer sk-your-api-key" ``` ## Filters - For batch and image-reference files, `limit` is 1–10000 (default 10000), with `after` for pagination. In Anthropic Files mode, `limit` is 1–1000 (default 20); use either `after_id` or `before_id`, not both. - `purpose` can be `batch`, `batch_output`, `user_data`, or `vision`. - `user_data` and `vision` return TokenLab-created image reference files when available. ## Response example ```json 200 OK { "object": "list", "data": [{ "id": "file_abc123", "object": "file", "purpose": "batch" }], "first_id": "file_abc123", "last_id": "file_abc123", "has_more": false } ``` ## Important fields #### object Always `list`. #### data Files. #### has_more More results are available when `has_more` is true. --- ### Retrieve File > Retrieves metadata for a single uploaded file. URL: https://tokenlab.sh/docs/en/api-reference/files/retrieve-file Return a file's name, size, purpose, and creation time. Without Anthropic headers, this covers batch and image-reference files. With Anthropic Files headers, it covers Anthropic-format files. ## Example ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/files/file_abc123" \ -H "Authorization: Bearer sk-your-api-key" ``` ## Image Reference Files This endpoint can also return metadata for TokenLab-created image reference files. The `status` field is optional and may be absent for file families that do not provide one. ## Response example ```json 200 OK { "id": "file_abc123", "object": "file", "bytes": 32768, "created_at": 1706000000, "filename": "batch-input.jsonl", "purpose": "batch" } ``` ## Important fields #### id File ID. #### filename Original or generated file name. #### purpose File purpose, such as `batch`, `batch_output`, `user_data`, or `vision`. --- ### Retrieve File Content > Download file content. URL: https://tokenlab.sh/docs/en/api-reference/files/retrieve-file-content Download batch input, output, or error files. With Anthropic Files headers, the same API downloads Anthropic-format files in the current Workspace. ## Example ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/files/file_abc123/content" \ -H "Authorization: Bearer sk-your-api-key" \ --output batch-output.jsonl ``` ## Content Types Batch files are returned as JSONL or plain text. Image-reference files are returned as binary content. ## Response example ```jsonl 200 OK {"custom_id":"request-1","response":{"status_code":200,"body":{"id":"chatcmpl_abc123"}}} {"custom_id":"request-2","error":{"code":"invalid_request_error","message":"Invalid model"}} ``` ## Important fields #### body File content. Save or parse it according to `Content-Type`; parse JSONL one line at a time. #### custom_id ID that matches a batch input item to its result. --- ### Delete File > Delete an uploaded file from the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/files/delete-file ## Overview Delete a file created through `/v1/files`. A successful response includes `deleted: true`. With Anthropic Files headers, the same API deletes an Anthropic-format file from the current Workspace. ## Notes - Deleting a file does not cancel batch jobs that already reference it. - Missing or inaccessible file IDs return a not-found error. - Delete image reference files only after no pending image edit request needs them. ## Example ```bash cURL curl -X DELETE "https://api.tokenlab.sh/v1/files/file_abc123" \ -H "Authorization: Bearer sk-your-api-key" ``` --- ### Create Batch > Create a batch from an uploaded JSONL file. URL: https://tokenlab.sh/docs/en/api-reference/batches/create-batch ## Overview Upload a JSONL file with `purpose=batch`, then use its ID as `input_file_id`. Completed batches return output and error file IDs. ## Notes - Batch input files must use `purpose=batch`. - `completion_window` is currently `24h`. - Output order may differ from input order. Match each result by `custom_id`. - Streaming is not supported inside batch items. - Supported item endpoints are `/v1/chat/completions` and `/v1/embeddings`. `/v1/responses` is not supported. ## Example ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/batches" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "input_file_id": "file_abc123", "endpoint": "/v1/chat/completions", "completion_window": "24h" }' ``` Batch prices may differ from synchronous requests. Use the price shown when creating the batch and the final bill. ## Response example ```json 200 OK { "id": "batch_abc123", "object": "batch", "endpoint": "/v1/chat/completions", "input_file_id": "file_abc123", "completion_window": "24h", "status": "validating", "output_file_id": null, "error_file_id": null, "created_at": 1706000000 } ``` ## Important fields #### id Batch ID. #### status Batch status. #### output_file_id Output file ID after completion. --- ### List Batches > List batches in the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/batches/list-batches ## Overview Return batches created in the current Workspace. ## Example ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/batches?limit=20" \ -H "Authorization: Bearer sk-your-api-key" ``` ## Response example ```json 200 OK { "object": "list", "data": [{ "id": "batch_abc123", "object": "batch", "status": "completed" }], "first_id": "batch_abc123", "last_id": "batch_abc123", "has_more": false } ``` ## Important fields #### object Always `list`. #### data Batches. #### status Status of each batch. #### has_more More results are available when `has_more` is true. --- ### Retrieve Batch > Retrieves the current status, counts, and result files for a batch. URL: https://tokenlab.sh/docs/en/api-reference/batches/retrieve-batch ## Overview Return the batch status, completed and failed counts, and result file IDs. ## Example ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/batches/batch_abc123" \ -H "Authorization: Bearer sk-your-api-key" ``` ## Response example ```json 200 OK { "id": "batch_abc123", "object": "batch", "status": "completed", "input_file_id": "file_abc123", "output_file_id": "file_out123", "error_file_id": "file_err123", "request_counts": { "total": 100, "completed": 98, "failed": 2 } } ``` ## Important fields #### request_counts Counts of total, completed, failed, and cancelled requests. #### output_file_id Successful result file ID. #### error_file_id Failed result file ID. --- ### Cancel Batch > Cancel a batch that has not finished. URL: https://tokenlab.sh/docs/en/api-reference/batches/cancel-batch Request cancellation of a queued or running batch. The response contains its current status: cancellation may still be in progress or may already be complete. A batch that has already finished keeps its existing terminal status. If the returned status is not terminal, retrieve the batch again to check the result. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/batches/batch_abc123/cancel" \ -H "Authorization: Bearer sk-your-api-key" ``` ```json 200 OK { "id": "batch_abc123", "object": "batch", "status": "cancelling", "input_file_id": "file_abc123" } ``` #### id Batch ID. #### status Current batch status. Do not assume every successful cancellation request returns `cancelling`. --- ## Images ### Create Image > Creates an image given a prompt URL: https://tokenlab.sh/docs/en/api-reference/images/create-image ## Overview Use `GET /v1/models?recommended_for=image` to get current image models, then send the selected `model` explicitly. `gpt-image-2` is a token-priced GPT Image model. TokenLab follows OpenAI's official usage breakdown for text input, image input, reported cached input, and image output tokens; it is not billed as a fixed per-image model. For `gpt-image-2` image generation, supported public parameters are `prompt`, `n`, `size`, `quality`, `response_format`, `async`, `background`, `output_format`, `output_compression` or `compression`, `moderation`, and `user`. `background` accepts `auto` or `opaque`; `transparent` is not supported. Omit `size` or `quality` to let TokenLab use `auto`; custom `size` values must use the flexible `WIDTHxHEIGHT` contract documented below. `input_fidelity` is not part of the current TokenLab supported fields for `gpt-image-2`; omit it or the request returns `400 unsupported_parameter`. ### Model behavior notes Google Gemini image-family models do not share the same selector contract: - `gemini-3.1-flash-image`, `gemini-nano-banana-2.1`, `gemini-3-pro-image`, `nano-banana-2.1`, and `nano-banana-pro` support `aspect_ratio` plus `resolution` (`1k`, `2k`, `4k`) for their public text-to-image, image-to-image, and image-edit operations. - `nano-banana-2` supports `aspect_ratio` and `resolution` (`1k`, `2k`, `4k`) for both text-to-image and image-to-image generation. - `gemini-3.1-flash-lite-image` and `nano-banana-2-lite` support text-to-image, image-to-image, and image-edit at `1k` resolution only. - `nano-banana` supports `aspect_ratio` but does not expose public `resolution` selection. - For image-to-image with `gemini-3.1-flash-image`, `gemini-nano-banana-2.1`, `gemini-3.1-flash-lite-image`, or `gemini-3-pro-image`, use this endpoint with `operation: "image-to-image"` and reference images. Send image-edit requests for these models to `/v1/images/edits`. - For Nano Banana reference-image requests, use `nano-banana-2`, `nano-banana-2.1`, `nano-banana-2-lite`, or `nano-banana-pro` on this endpoint (`/v1/images/generations`) with `operation: "image-to-image"` and `image_urls`. Image-edit requests for `nano-banana-2`, `nano-banana-2.1`, `nano-banana-2-lite`, and `nano-banana-pro` also use this endpoint, with `operation: "image-edit"`. Do not send Nano Banana reference-image requests to `/v1/images/edits`. - Reference images on this endpoint can be supplied as JSON `image_url` / `image_urls`, or as a multipart `image` file. `images[]` and `file_id` are not accepted on `/v1/images/generations`; create `/v1/files` references only for `/v1/images/edits` models that document `images[].file_id`. For Google image families, prefer `aspect_ratio` and only send `resolution` when the model explicitly supports it. xAI Grok Imagine image models (`grok-imagine-image`, `grok-imagine-image-quality`, and legacy `grok-imagine-image-pro`) support `aspect_ratio` plus `resolution` (`1k`, `2k`). `grok-imagine-image-pro` is retained as a compatibility ID for `grok-imagine-image-quality`. ## Request Body **Synchronous request timeout:** Some image requests return the final image inline and wait for generation to finish. High-resolution or high-quality requests can take close to a minute or longer, so set your HTTP client timeout to at least `120s`. If the create response includes `status: "pending"`, `task_id`, or `poll_url`, follow the returned `poll_url` instead. #### model Model to use (for example, `gpt-image-2`, `flux-pro-1.1`, or `nano-banana-pro`). Query `GET /v1/models?recommended_for=image` for the current recommended list. #### prompt Text description of the desired image. #### image_url Public HTTPS reference image URL for image-to-image generation. For Nano Banana reference-image requests, set `operation` to `image-to-image`; `nano-banana-pro` may include `resolution`. #### image_urls Public HTTPS reference image URLs. Use this for one or more reference images in JSON requests. `file_id` and `images[]` are not supported on this endpoint. #### reference_image_urls Additional reference image URLs for models that distinguish primary inputs from references. #### image Multipart reference image file for image-to-image generation. Use this when the source image is private or header-protected. This is different from a /v1/files `file_id`, which is not accepted on this endpoint. #### n Number of images to generate (1-10, model dependent). #### size Image size. Use this for OpenAI-style image families and other models that accept exact pixel sizes. For `gpt-image-2`, `size` accepts `auto` or `WIDTHxHEIGHT`. Custom dimensions must both be multiples of 16, the longest edge must be at most `3840px`, the long/short ratio must be at most `3:1`, and total pixels must be between `655,360` and `8,294,400`. `aspect_ratio` and `resolution` are not part of the current TokenLab model details for `gpt-image-2`. For Google image models with resolution tiers (`gemini-3.1-flash-image`, `gemini-nano-banana-2.1`, `gemini-3-pro-image`, `gemini-3.1-flash-lite-image`, `nano-banana-2`, `nano-banana-2.1`, and `nano-banana-pro`), `size` accepts an output size from the model's size table, such as `1376x768` (16:9 at 1K), or a resolution tier: `1K`, `2K`, or `4K`. TokenLab sends a tier as `resolution`, so the request and its price are the same as with `resolution`. A tier that the model does not support (for example `2K` on `gemini-3.1-flash-lite-image`), or a tier that is different from `resolution`, returns `400` with code `invalid_image_size_contract` and the model's `allowed_resolutions`. For these models, the recommended form is `aspect_ratio` with `resolution`. #### aspect_ratio Model-dependent aspect ratio selector. Common Google image-family values include `1:1`, `16:9`, `9:16`, `3:2`, and `2:3`. #### resolution Model-dependent output resolution. `gemini-3.1-flash-image`, `gemini-nano-banana-2.1`, `gemini-3-pro-image`, `nano-banana-2`, `nano-banana-2.1`, and `nano-banana-pro` support `1k`, `2k`, and `4k`; Grok Imagine image models support `1k` and `2k`. Omit this field unless the selected model and operation explicitly support it. #### quality Image quality. GPT Image models such as `gpt-image-2` use `auto`, `low`, `medium`, or `high`. Other models may use different values; check the model details before sending a non-default value. #### response_format How the images are returned: `url` or `b64_json`. - Omitted: when the model returns image bytes, each image is in `data[].b64_json`, and also in `data[].url` when TokenLab stored it. When the model returns a link, each image is in `data[].url`. - `url`: each image is only in `data[].url`. - `b64_json`: each image is only in `data[].b64_json` as Base64 data. Links are TokenLab media-result URLs. They stay valid for 30 days. See [Data retention](/guides/data-retention). #### async Set to `true` with `gpt-image-2` or official FLUX/BFL image models to create a task first. Completed async image tasks return URLs regardless of the requested `response_format`; use synchronous requests when you need `b64_json`. #### style Optional style selector. Only send this when the selected model explicitly documents it; omit it for `gpt-image-2` unless the model metadata says otherwise. #### user A unique identifier for the end-user. ## Response ### Inline Response #### created Unix timestamp of creation. #### data Array of generated images. Each object contains: - `url` (string): TokenLab media-result URL of the image, valid for 30 days. Not present when `response_format` is `b64_json`. When `response_format` is omitted, it is missing if TokenLab could not store the image. - `b64_json` (string): Base64 image data. Present when `response_format` is `b64_json`, or when it is omitted and the model returns image bytes. - `revised_prompt` (string): Optional prompt revision, when the selected model returns one ### Async Task Response Set `async: true` with `gpt-image-2` or official FLUX/BFL image models to create a task instead of waiting for the final image in the create request. The response includes `status: "pending"`, `task_id`, and `poll_url`. Poll `/v1/tasks/{task_id}` until the task reaches `completed` or `failed`. Async image tasks return final image URLs only. If you need raw `b64_json` image data, use a synchronous request. Billing may reserve the estimated amount when the task is created. Completed tasks are billed by actual usage, and failed or timed-out tasks are released or refunded. #### created Unix timestamp of creation. #### task_id Unique task identifier for polling. #### status Initial status: `pending`. #### poll_url Relative URL to poll for results, for example `/v1/tasks/{id}`. #### data Empty while the task is pending. Completed image tasks return generated image URLs in `data[].url`. When you receive `status: "pending"`, use `poll_url` or `GET /v1/tasks/{task_id}` to retrieve the result. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/images/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gemini-3-pro-image", "prompt": "A cinematic portrait of a white cat sitting on a rainy windowsill", "aspect_ratio": "16:9", "resolution": "2k" }' ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.images.generate( model="gemini-3-pro-image", prompt="A cinematic portrait of a white cat sitting on a rainy windowsill", extra_body={"aspect_ratio": "16:9", "resolution": "2k"} ) print(response.data[0].url) ``` ```javascript JavaScript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.images.generate({ model: 'gemini-3-pro-image', prompt: 'A cinematic portrait of a white cat sitting on a rainy windowsill', aspect_ratio: '16:9', resolution: '2k' }); console.log(response.data[0].url); ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'gemini-3-pro-image', 'prompt' => 'A cinematic portrait of a white cat sitting on a rainy windowsill', 'aspect_ratio' => '16:9', 'resolution' => '2k' ]) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['data'][0]['url']; ``` Ratio-only image-family example: for `nano-banana`, send `aspect_ratio` but omit `resolution`: ```json { "model": "nano-banana", "prompt": "A clean editorial product shot of a citrus soda can", "aspect_ratio": "16:9" } ``` Nano Banana Pro reference-image example: send the request to `/v1/images/generations`, not `/v1/images/edits`. `resolution` is optional and may be set to `1k`, `2k`, or `4k`: ```json { "model": "nano-banana-pro", "prompt": "Create a clean cinematic character image based on the reference images", "operation": "image-to-image", "image_urls": ["https://example.com/reference-1.png"], "aspect_ratio": "1:1", "resolution": "2k" } ``` Direct multipart upload example for private or local source images. Do not pass a `file_id` to `/v1/images/generations`: ```bash curl -X POST "https://api.tokenlab.sh/v1/images/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=nano-banana-pro" \ -F "prompt=Create a clean cinematic character image based on this reference" \ -F "operation=image-to-image" \ -F "image=@reference.png" \ -F "aspect_ratio=1:1" \ -F "resolution=2k" ``` ```json Inline Response { "created": 1706000000, "data": [ { "url": "https://...", "revised_prompt": "A fluffy white cat with bright eyes sitting peacefully on a wooden windowsill, watching raindrops stream down the glass window..." } ] } ``` ```json Async Task Response { "created": 1706000000, "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "data": [] } ``` ## Choose a Model Query `GET /v1/models?recommended_for=image` or use the [Models page](https://tokenlab.sh/en/models) for current availability, capabilities, and prices. Do not hard-code a model as always synchronous or always asynchronous. If the create response returns `status: "pending"`, follow `poll_url` and poll until completion. ## Handling Task-Based Responses For image models, always check whether the response contains `status: "pending"` / `status: "processing"`: ```python import requests import time def generate_image(prompt, model="gpt-image-2"): # Create image request response = requests.post( "https://api.tokenlab.sh/v1/images/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={"model": model, "prompt": prompt, "async": True}, timeout=120 ) response.raise_for_status() data = response.json() # Check if task-based if data.get("status") in ("pending", "processing"): task_id = data["task_id"] poll_url = data.get("poll_url") print(f"Image task started: {task_id}") # Poll for result while True: status_resp = requests.get( f"https://api.tokenlab.sh{poll_url}" if poll_url else f"https://api.tokenlab.sh/v1/tasks/{task_id}", headers={"Authorization": "Bearer sk-your-api-key"}, timeout=30 ) status_resp.raise_for_status() status_data = status_resp.json() if status_data["status"] == "completed": return status_data["data"][0]["url"] elif status_data["status"] == "failed": raise Exception(status_data.get("error", "Generation failed")) time.sleep(3) else: # Inline response return data["data"][0]["url"] # Usage url = generate_image("a beautiful sunset over mountains", model="gpt-image-2") print(f"Generated image: {url}") ``` > Note: Hy Image 3.5 Preview creates 1024-pixel square images from text and edits reference images using written instructions. It is useful for drafting visuals and refining a composition. ```json { "model": "hy-image-v3.5-preview", "prompt": "A yellow lemon on a white table", "size": "1024x1024", "n": 1, "response_format": "url" } ``` --- ### Edit Image > Edit an image given a prompt and source image URL: https://tokenlab.sh/docs/en/api-reference/images/edit-image ## Overview Creates an edited or extended image given an original image and a prompt. This endpoint supports both: - the OpenAI-compatible `multipart/form-data` upload flow documented below - JSON requests that provide `image_url`, `image_urls`, or official `images` references for supported image-to-image families > Note: `gpt-image-2` is supported here. It accepts multipart `image` uploads, JSON `image_url` / `image_urls`, and official `images[]` references (`image_url` or `file_id`), with up to 16 source images. Create `file_id` values through `/v1/files` first. Set `async: true` to return a task first; official FLUX/BFL edit models also use the same task polling flow. > > `gpt-image-2` edits do not accept `resolution`; use `size` for output dimensions. `background` accepts `auto` or `opaque`; `transparent` is not supported. For multi-image or high-latency edits, prefer `async: true` and poll the returned task. > > Nano Banana reference-image requests (`nano-banana-2`, `nano-banana-2.1`, `nano-banana-2-lite`, and `nano-banana-pro`) are exposed on `/v1/images/generations` with `operation: "image-to-image"` and `image_urls`, not on this `/v1/images/edits` endpoint. Image-edit requests for `nano-banana-2`, `nano-banana-2.1`, `nano-banana-2-lite`, and `nano-banana-pro` also use `/v1/images/generations`, with `operation: "image-edit"`. > > `gemini-3.1-flash-image`, `gemini-nano-banana-2.1`, `gemini-3.1-flash-lite-image`, and `gemini-3-pro-image` use this endpoint for image-edit. Their image-to-image requests use `/v1/images/generations` with `operation: "image-to-image"`. > > xAI Grok Imagine image edit models (`grok-imagine-image`, `grok-imagine-image-quality`, and legacy `grok-imagine-image-pro`) accept at most 3 source images. Requests with more than 3 source images fail input validation with `400 too_many_images`. > > `input_fidelity` is not part of the current TokenLab supported fields for `gpt-image-2`; omit it or the request returns `400 unsupported_parameter`. ## Request Body **Synchronous request timeout:** Some image requests return the final image inline and wait for generation to finish. High-resolution or high-quality requests can take close to a minute or longer, so set your HTTP client timeout to at least `120s`. If the create response includes `status: "pending"`, `task_id`, or `poll_url`, follow the returned `poll_url` instead. Remote image URLs: when multipart input is needed, TokenLab fetches JSON `image_url`, `image_urls`, or `images[].image_url` and sends the bytes as multipart `image` parts. URLs must be public `http`/`https`, without embedded credentials or fragments, and must not resolve to localhost, private, or reserved IP ranges; each redirect is checked again. The fetched payload must be a real PNG, JPEG, or WebP image. Limits are `50 MiB` per image, `200 MiB` total for URL-fetched images in one request, `30s` fetch timeout, and up to `3` redirects. For JSON requests, provide exactly one of `image_url`, `image_urls`, or `images`. Each `images[]` object must contain exactly one of `image_url` or `file_id`. The 200 MiB aggregate limit includes all source images and the mask. #### image Multipart source images. Repeat `image` to provide multiple GPT Image sources. Files must be PNG, JPEG, or WebP, up to 16 source images and `50 MiB` each. xAI Grok Imagine edit models use the same input fields but cap source images at 3. #### prompt A text description of the desired edit. #### mask An additional image whose fully transparent areas indicate where the image should be edited. Must be a valid PNG file, less than 50 MiB, and have the same dimensions as `image`. For JSON requests, `mask` may also be an object with exactly one of `image_url` or `file_id`; `file_id` values must come from `/v1/files` and remain bound to the same image-edit configuration. #### model The model to use for image edits. Use `gpt-image-2` for GPT Image edits, or another current image-edit model returned by `GET /v1/models?recommended_for=image`. #### n Number of images to generate (1-10, model dependent). #### size The size of the generated image. For `gpt-image-2`, use `auto` or `WIDTHxHEIGHT`; dimensions must be multiples of 16, longest edge at most `3840px`, long/short ratio at most `3:1`, and total pixels between `655,360` and `8,294,400`. For `gemini-3.1-flash-image`, `gemini-nano-banana-2.1`, `gemini-3-pro-image`, and `gemini-3.1-flash-lite-image`, `size` accepts an output size from the model's size table or a resolution tier: `1K`, `2K`, or `4K`. TokenLab sends a tier as `resolution`. A tier that the model does not support, or a tier that is different from `resolution`, returns `400` with code `invalid_image_size_contract`. The recommended form is `aspect_ratio` with `resolution`. #### response_format How the images are returned: `url` or `b64_json`. - Omitted: when the model returns image bytes, each image is in `data[].b64_json`, and also in `data[].url` when TokenLab stored it. When the model returns a link, each image is in `data[].url`. - `url`: each image is only in `data[].url`. - `b64_json`: each image is only in `data[].b64_json` as Base64 data. Links are TokenLab media-result URLs. They stay valid for 30 days. See [Data retention](/guides/data-retention). #### async Set to `true` with `gpt-image-2` or official FLUX/BFL edit models to return a task before the final image is ready. Completed async edits return URLs regardless of the requested `response_format`; use synchronous requests when you need `b64_json`. #### user A unique identifier representing your end-user for abuse monitoring. ## Response #### created Unix timestamp of when the images were created. #### data Array of generated images. Each object contains: - `url` (string): TokenLab media-result URL of the edited image, valid for 30 days. Not present when `response_format` is `b64_json`. When `response_format` is omitted, it is missing if TokenLab could not store the image. - `b64_json` (string): Base64 image data. Present when `response_format` is `b64_json`, or when it is omitted and the model returns image bytes. ### Async Task Response Set `async: true` with `gpt-image-2` or official FLUX/BFL edit models to create a task instead of waiting for the edited image in the request. The response includes `status: "pending"`, `task_id`, and `poll_url`. Poll `/v1/tasks/{task_id}` until the task reaches `completed` or `failed`. Async edit tasks return final image URLs only. If you need raw `b64_json` image data, use a synchronous request. Billing may reserve the estimated amount when the task is created. Completed tasks are billed by actual usage, and failed or timed-out tasks are released or refunded. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2" \ -F "image=@sunlit_lounge.png" \ -F "mask=@mask.png" \ -F "prompt=A sunlit indoor lounge area with a pool" \ -F "n=1" \ -F "size=1024x1024" ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.images.edit( model="gpt-image-2", image=open("sunlit_lounge.png", "rb"), mask=open("mask.png", "rb"), prompt="A sunlit indoor lounge area with a pool", n=1, size="1024x1024" ) print(response.data[0].url) ``` ```javascript JavaScript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.images.edit({ model: 'gpt-image-2', image: fs.createReadStream('sunlit_lounge.png'), mask: fs.createReadStream('mask.png'), prompt: 'A sunlit indoor lounge area with a pool', n: 1, size: '1024x1024' }); console.log(response.data[0].url); ``` ```go Go package main import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "os" ) func main() { body := &bytes.Buffer{} writer := multipart.NewWriter(body) writer.WriteField("model", "gpt-image-2") image, _ := os.Open("sunlit_lounge.png") defer image.Close() part, _ := writer.CreateFormFile("image", "sunlit_lounge.png") io.Copy(part, image) mask, _ := os.Open("mask.png") defer mask.Close() maskPart, _ := writer.CreateFormFile("mask", "mask.png") io.Copy(maskPart, mask) writer.WriteField("prompt", "A sunlit indoor lounge area with a pool") writer.WriteField("n", "1") writer.WriteField("size", "1024x1024") writer.Close() req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1/images/edits", body) req.Header.Set("Authorization", "Bearer sk-your-api-key") req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() result, _ := io.ReadAll(resp.Body) fmt.Println(string(result)) } ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => [ 'model' => 'gpt-image-2', 'image' => $image, 'mask' => $mask, 'prompt' => 'A sunlit indoor lounge area with a pool', 'n' => 1, 'size' => '1024x1024' ] ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['data'][0]['url']; ``` ```json Response { "created": 1706000000, "data": [ { "url": "https://..." } ] } ``` ## Notes > Note: Remote image fetch failures are returned as input errors before generation begins. Unreachable URLs, timeouts, 403/404 responses, private/internal hosts, credentials or fragments in the URL, non-image content, unsupported formats, and size-limit violations return `400` or `413` and identify the `image_url` / `image_urls[n]` input. For private or header-protected assets, upload multipart `image` files directly or create `/v1/files` references. > Note: Hy Image 3.5 Preview creates 1024-pixel square images from text and edits reference images using written instructions. It is useful for drafting visuals and refining a composition. ```json { "model": "hy-image-v3.5-preview", "prompt": "Change the table to pale blue", "image_url": "https://example.com/reference.png", "size": "1024x1024", "n": 1, "response_format": "url" } ``` --- ### Create Image Variation > Creates a variation of a given image URL: https://tokenlab.sh/docs/en/api-reference/images/create-variation ## Overview This endpoint supports existing OpenAI Variations clients. For new image-to-image features, use `/v1/images/edits` or `/v1/images/generations` with a current image-to-image model. Requests use `multipart/form-data`. ## Request Body Some requests return the final image immediately. Set your HTTP client timeout to at least `120s`. If the response includes `status: "pending"`, `task_id`, or `poll_url`, use the returned URL to retrieve the result. #### image Multipart source image. The file and complete request body must each fit within 50 MiB. Supported formats and dimensions depend on the selected model; its current model details must explicitly list `/v1/images/variations`. #### model Model ID. This endpoint has no default model. #### n Number of images to generate (1-10, model dependent). #### size Generated image size. Supported values depend on the selected model. #### response_format The format in which the generated images are returned. Must be `url` or `b64_json`. #### user A unique identifier representing your end-user for abuse monitoring. ## Response #### created Unix timestamp of when the images were created. #### data Array of generated image variations. Each object contains: - `url` (string): URL of the variation image (if response_format is `url`) - `b64_json` (string): Base64-encoded image (if response_format is `b64_json`) ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/images/variations" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=your-legacy-variation-model" \ -F "image=@cat.png" \ -F "n=2" \ -F "size=1024x1024" ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.images.create_variation( model="your-legacy-variation-model", image=open("cat.png", "rb"), n=2, size="1024x1024" ) for image in response.data: print(image.url) ``` ```javascript JavaScript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.images.createVariation({ model: 'your-legacy-variation-model', image: fs.createReadStream('cat.png'), n: 2, size: '1024x1024' }); response.data.forEach(image => console.log(image.url)); ``` ```go Go package main import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "os" ) func main() { body := &bytes.Buffer{} writer := multipart.NewWriter(body) writer.WriteField("model", "your-legacy-variation-model") file, _ := os.Open("cat.png") defer file.Close() part, _ := writer.CreateFormFile("image", "cat.png") io.Copy(part, file) writer.WriteField("n", "2") writer.WriteField("size", "1024x1024") writer.Close() req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1/images/variations", body) req.Header.Set("Authorization", "Bearer sk-your-api-key") req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() result, _ := io.ReadAll(resp.Body) fmt.Println(string(result)) } ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => [ 'model' => 'your-legacy-variation-model', 'image' => $image, 'n' => 2, 'size' => '1024x1024' ] ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); foreach ($data['data'] as $image) { echo $image['url'] . "\n"; } ``` ```json Response { "created": 1706000000, "data": [ { "url": "https://..." }, { "url": "https://..." } ] } ``` ## Notes > Note: This is a legacy compatibility endpoint. It is not included in the current recommended image model catalog. For new integrations, use `/v1/images/edits` with `gpt-image-2`, or `/v1/images/generations` with `operation: "image-to-image"` and a current image-to-image model. --- ### Get Image Status > Get the status and result of an async image generation task URL: https://tokenlab.sh/docs/en/api-reference/images/get-image-status ## Path Parameters #### id The task ID returned from the initial image generation request. > Info: If the create response included `poll_url`, prefer calling that exact URL for polling. Some image tasks may surface a `poll_url` under `/v1/tasks/{id}` instead of the image-specific status path. > > You can also poll public image task IDs such as `ldtask_...` at `/v1/images/{id}`. This compatibility alias uses the same authorization, ownership checks, billing headers, terminal snapshots, and response shape as `/v1/images/generations/{id}`. > > An expired or unavailable public task can return `404 async_task_not_found`. If task ownership cannot be established for the current API key, the response is `403 task_not_owned`. ## Response > Note: Fields describing results, errors, timestamps, and the model are returned when available for the task. #### id Canonical async task identifier. #### poll_url Preferred polling URL when the create response supplies one. #### created Unix timestamp of creation. #### task_id The task identifier. #### status Task status: `pending`, `processing`, `completed`, or `failed`. #### data Array of generated images (populated when `status` is `completed`). Each object contains: - `url` (string): URL of the generated image - `revised_prompt` (string): Optional prompt revision, when the selected model returns one #### error Error message (only present when `status` is `failed`). ```bash cURL curl "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests response = requests.get( "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", headers={"Authorization": "Bearer sk-your-api-key"} ) response.raise_for_status() data = response.json() print(f"Status: {data['status']}") if data["status"] == "completed": print(f"Image URL: {data['data'][0]['url']}") ``` ```javascript JavaScript const response = await fetch( 'https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', { headers: { 'Authorization': 'Bearer sk-your-api-key' } } ); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); console.log(`Status: ${data.status}`); if (data.status === 'completed') { console.log(`Image URL: ${data.data[0].url}`); } ``` ```json Pending { "created": 1706000000, "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "data": [] } ``` ```json Completed { "created": 1706000000, "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "completed", "data": [ { "url": "https://api.tokenlab.sh/v1/media-results/0b6d8e2f-4c1a-4f3e-8a7d-5e9c2b1f6a04.png?token=…", "revised_prompt": "a beautiful sunset over mountains with vibrant orange and purple colors" } ], "media_retention": { "days": 30, "items": [ { "url": "https://api.tokenlab.sh/v1/media-results/0b6d8e2f-4c1a-4f3e-8a7d-5e9c2b1f6a04.png?token=…", "kind": "image", "content_type": "image/png", "status": "saved", "expires_at": "2024-02-22T08:54:20.000Z" } ] } } ``` ```json Failed { "created": 1706000000, "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "failed", "error": "Content policy violation", "data": [] } ``` ## Check Interval > Tip: Check every 3–5 seconds while the status is `pending` or `processing`, and stop at `completed` or `failed`. ```python import requests import time def poll_image_task(task_id, api_key, max_wait=300, interval=3): """Poll for image generation result with timeout.""" url = f"https://api.tokenlab.sh/v1/tasks/{task_id}" headers = {"Authorization": f"Bearer {api_key}"} start_time = time.time() while time.time() - start_time < max_wait: response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() data = response.json() if data["status"] == "completed": return data["data"][0]["url"] elif data["status"] == "failed": raise Exception(data.get("error", "Generation failed")) time.sleep(interval) raise TimeoutError(f"Task {task_id} did not complete within {max_wait}s") # Usage image_url = poll_image_task("ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "sk-your-api-key") print(f"Generated image: {image_url}") ``` --- ## Audio & Realtime ### Create Speech > Generates audio from the input text URL: https://tokenlab.sh/docs/en/api-reference/audio/create-speech > Note: Use `GET /v1/models?recommended_for=tts` to get current speech models. ## Request Body The response mode depends on the selected model and `stream_format`. For longer input, allow an HTTP client timeout of at least `120s`. Optional fields, voices, output formats, and SSE support depend on the selected model. Check `GET /v1/models/{model}` before sending them. Unsupported parameters can return `400 unsupported_parameter`. #### model TTS model ID. Find current choices with `GET /v1/models?recommended_for=tts`. There is no default model. #### input The text to generate audio for. Maximum 4096 characters. #### voice Voice selector. Pass a built-in voice name such as `eve`, a Gemini voice such as `Kore`, or an object like `{ "id": "voice-id" }` for compatible custom voices. #### voice_id Voice ID for models that support this parameter, such as compatible MiniMax speech models. #### instructions Optional style or delivery instructions for OpenAI-compatible TTS models that support them. #### prompt Optional speaking style prompt for Gemini TTS models. #### language_code Language code, such as `en-US`, for models that support it. #### response_format Audio format. Common values include `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`; supported values vary by model family. #### stream_format TokenLab delivery format: `audio` or `sse`. #### speed Speech speed for model families that support it (0.25 to 4.0). #### temperature Sampling temperature for Gemini TTS, from 0 to 2. ## Response With `stream_format=audio`, the response contains audio bytes. Models that support `stream_format=sse` return a `text/event-stream` response; parse its events instead of saving the entire response as an audio file. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/audio/speech" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "grok-tts", "voice": "eve", "input": "Hello, welcome to TokenLab!" }' \ --output speech.mp3 ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.audio.speech.create( model="grok-tts", voice="eve", input="Hello, welcome to TokenLab!" ) response.stream_to_file("speech.mp3") ``` ```javascript JavaScript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.audio.speech.create({ model: 'grok-tts', voice: 'eve', input: 'Hello, welcome to TokenLab!' }); const buffer = Buffer.from(await response.arrayBuffer()); fs.writeFileSync('speech.mp3', buffer); ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'grok-tts', 'voice' => 'eve', 'input' => 'Hello, welcome to TokenLab!' ]) ]); $audio = curl_exec($ch); curl_close($ch); file_put_contents('speech.mp3', $audio); ``` ## Voice Samples Voices depend on the model. Read the `voice` values in the model details from `GET /v1/models/{model}` before you send one. For example, `grok-tts` accepts `eve`, `ara`, `rex`, `sal`, and `leo`. ## Response example ```binary 200 OK ``` ## Important fields #### Content-Type HTTP response media type: the audio format, or `text/event-stream` for SSE. #### body Audio bytes for `audio`; an event stream for `sse`. Choose the parser using `Content-Type` and the requested mode. > Note: MiniMax Speech 2.8 Turbo synthesizes a script in a selected voice with a speed-focused workflow, useful for responsive spoken interfaces and narration. ```json { "model": "speech-2.8-turbo", "input": "Welcome to TokenLab.", "voice_id": "male-qn-qingse", "speed": 1, "response_format": "mp3", "stream_format": "audio" } ``` --- ### Create Transcription > Transcribes audio into the input language URL: https://tokenlab.sh/docs/en/api-reference/audio/create-transcription > Note: Use `GET /v1/models?recommended_for=stt` to get current transcription models. ## Request Body Transcription can return the transcript immediately or accept an asynchronous task with HTTP `200`, `id`, and `status`. For an accepted task, follow `poll_url`, or query `/v1/tasks/{id}`. For longer synchronous audio, allow a client timeout of at least `120s`. #### file Audio file to transcribe. Supported formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm. Maximum file size: 25 MB. #### model Transcription model ID, for example `whisper-large-v3` or `qwen3-asr-flash`. Find current choices with `GET /v1/models?recommended_for=stt`. There is no default model. #### language Language of the audio in ISO-639-1 format (e.g., `en`, `zh`, `ja`). #### prompt Optional text to guide the model's style or continue a previous segment. #### response_format Output format. Whisper supports `json`, `text`, `srt`, `verbose_json`, and `vtt`; other models may support a different subset. Check the selected model's details. #### temperature Sampling temperature from 0 to 1, only for models that support this parameter. #### timestamp_granularities `word` and/or `segment`, when supported by the selected model. For Whisper, use `response_format=verbose_json`. In multipart requests, repeat `timestamp_granularities[]` for multiple values. ## Response The fields below describe JSON transcript or translation responses. `text`, `srt`, and `vtt` return raw text or subtitles rather than a JSON object. Additional fields depend on the model and output format. #### text The transcribed text. For `verbose_json`: #### task Task name when returned, such as `transcribe`. #### language Detected language. #### duration Audio duration in seconds. #### segments Transcription segments with timestamps. #### words Word-level timestamps (if requested). An accepted asynchronous transcription task returns the following fields instead of the transcript: #### id Task ID. #### task_id Async task identifier alias when provided. #### status Task status: `pending`, `processing`, `completed`, or `failed`. #### poll_url Preferred polling URL when the create response supplies one. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/audio/transcriptions" \ -H "Authorization: Bearer sk-your-api-key" \ -F file="@audio.mp3" \ -F model="whisper-large-v3" \ -F language="en" ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) with open("audio.mp3", "rb") as audio_file: response = client.audio.transcriptions.create( model="whisper-large-v3", file=audio_file, language="en" ) print(response.text) ``` ```javascript JavaScript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.audio.transcriptions.create({ model: 'whisper-large-v3', file: fs.createReadStream('audio.mp3'), language: 'en' }); console.log(response.text); ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => [ 'file' => $file, 'model' => 'whisper-large-v3', 'language' => 'en' ] ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['text']; ``` ```json Response (json) { "text": "Hello, this is a test of the transcription API." } ``` ```json Response (verbose_json) { "task": "transcribe", "language": "english", "duration": 5.5, "text": "Hello, this is a test of the transcription API.", "segments": [ { "id": 0, "start": 0.0, "end": 2.5, "text": "Hello, this is a test", "tokens": [50364, 2425, 11] } ] } ``` ```json HTTP 200 (async) { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } ``` --- ### Create Translation > Translates audio into English text URL: https://tokenlab.sh/docs/en/api-reference/audio/create-translation ## Overview Translates audio in any supported language into English text. Unlike transcription, this endpoint always outputs English text regardless of the input language. > Note: This endpoint translates audio into English. Do not send `language`; it is not supported for audio translation and can return `unsupported_parameter`. For text translation, use `POST /v1/translations`; `recommended_for=translation` refers to that text API. ## Request Body Translation returns when processing finishes. For longer audio, allow an HTTP client timeout of at least `120s`. #### file The audio file to translate. Supported formats: `flac`, `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `ogg`, `wav`, `webm`. Maximum file size is 25 MB. #### model Audio translation model ID, such as `whisper-1`. Confirm that its current model details support `POST /v1/audio/translations`. There is no default model. #### prompt An optional text to guide the model's style or continue a previous segment. Should be in English. #### response_format Output format. Whisper supports `json`, `text`, `srt`, `verbose_json`, and `vtt`; other models may support a different subset. Check the selected model's details. #### temperature Sampling temperature from 0 to 1, only for models that support this parameter. ## Response The fields below describe JSON transcript or translation responses. `text`, `srt`, and `vtt` return raw text or subtitles rather than a JSON object. Additional fields depend on the model and output format. #### text The translated text in English. For `verbose_json` format, the response also includes: #### language Language label reported in the response, when provided. #### duration The duration of the input audio in seconds. #### segments Segments of the translated text with timestamps. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/audio/translations" \ -H "Authorization: Bearer sk-your-api-key" \ -F "file=@german_audio.mp3" \ -F "model=whisper-1" ``` ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) with open("german_audio.mp3", "rb") as audio_file: response = client.audio.translations.create( model="whisper-1", file=audio_file ) print(response.text) ``` ```javascript JavaScript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.tokenlab.sh/v1' }); const response = await client.audio.translations.create({ model: 'whisper-1', file: fs.createReadStream('german_audio.mp3') }); console.log(response.text); ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => [ 'file' => $file, 'model' => 'whisper-1' ] ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['text']; ``` ```json json { "text": "Hello, my name is Wolfgang and I come from Germany. Where are you from?" } ``` ```json verbose_json { "task": "translate", "duration": 8.470000267028809, "text": "Hello, my name is Wolfgang and I come from Germany. Where are you from?", "segments": [ { "id": 0, "seek": 0, "start": 0.0, "end": 4.0, "text": " Hello, my name is Wolfgang and I come from Germany.", "tokens": [50364, 2425, 11, 452, 1315, 307, 25329, 293, 286, 808, 490, 5765, 13, 50564], "temperature": 0.0, "avg_logprob": -0.45, "compression_ratio": 1.0, "no_speech_prob": 0.0 } ] } ``` ## Translation vs Transcription | Feature | Translation | Transcription | |---------|-------------|---------------| | Output language | Always English | Same as input | | Use case | Convert foreign audio to English | Preserve original language | | Language parameter | Not applicable | Optional hint | --- ### Realtime WebSocket > Connect to realtime speech and multimodal sessions over WebSocket URL: https://tokenlab.sh/docs/en/api-reference/realtime/connect ## Overview Use this endpoint for realtime sessions such as streaming speech recognition, speech synthesis, speech translation, or realtime multimodal models. A standard `GET` request returns endpoint metadata, while a WebSocket upgrade request opens a realtime session for the selected model. ## Supported surface TokenLab exposes a realtime WebSocket endpoint at `GET /v1/realtime` for metadata checks and WebSocket upgrades. Treat it as a WebSocket subset: it forwards supported realtime model events, and it does not support OpenAI Realtime REST helper surfaces such as session creation, `client_secrets`, `Calls` call-control APIs, or legacy beta session APIs. For browser or mobile apps, keep long-lived API keys on your server. This endpoint does not issue short-lived Realtime client secrets. > Note: Choose a current realtime model from `/v1/models` and verify `/v1/models/{model}` before setting `TOKENLAB_REALTIME_MODEL`. Session events and configuration depend on that model. The JSON response example below is for an ordinary HTTP GET, not a WebSocket session event. ## Connection #### model Realtime model ID. Use a model whose model details lists realtime support. #### Authorization Bearer API key. WebSocket clients should send `Authorization: Bearer sk-your-api-key` during the upgrade request. ```javascript JavaScript import WebSocket from 'ws'; const model = process.env.TOKENLAB_REALTIME_MODEL; const apiKey = process.env.TOKENLAB_API_KEY; if (!model || !apiKey) { throw new Error('Set TOKENLAB_REALTIME_MODEL and TOKENLAB_API_KEY'); } const url = new URL('wss://api.tokenlab.sh/v1/realtime'); url.searchParams.set('model', model); const socket = new WebSocket(url, { headers: { Authorization: `Bearer ${apiKey}` } }); socket.on('open', () => { console.log('Realtime connection open'); }); socket.on('message', (data) => { console.log('realtime event', data.toString()); }); socket.on('error', (error) => console.error(error.message)); socket.on('close', (code) => console.log('Realtime connection closed', code)); ``` ```bash cURL curl "https://api.tokenlab.sh/v1/realtime" \ -H "Authorization: Bearer sk-your-api-key" ``` ## Messages TokenLab relays WebSocket messages between your client and the selected realtime model. Use the event format documented for that model, and include `model` in the query string rather than in each event. ## Billing and closing Sessions are billed against your API key balance: TokenLab reserves an estimate at the start, then settles the actual usage at the end and refunds any difference. Close the client socket when the session is complete. If the service closes the session first, TokenLab sends a safe close event/code to your client when possible. ## Response example ```json HTTP GET { "object": "realtime.endpoint", "websocket_url": "/v1/realtime?model={model}", "protocol": "tokenlab_realtime_proxy" } ``` ## Important fields #### object `realtime.endpoint` (HTTP GET). #### websocket_url `/v1/realtime?model={model}` (HTTP GET). #### protocol `tokenlab_realtime_proxy` (HTTP GET). #### type Event or message type returned by the API. #### session.id Realtime session ID for logs and support. --- ## Music ### Create Music > Creates a music generation task using Suno URL: https://tokenlab.sh/docs/en/api-reference/music/create-music > Note: Use `GET /v1/models?recommended_for=music` to get current music models. Generate music and lyrics using AI. This is an asynchronous API. Create responses return a task identity, and may also return a preferred `poll_url` for status checks. ## Request Body #### model Music model ID, such as `suno-music`. For lyrics only, choose a model whose current public operations include `lyrics`. #### prompt Music description or replacement text. Required for music, lyrics, upload-cover, upload-extend, and replace-section; optional for add-instrumental and stem separation. #### title Title for the generated song. #### tags Style tags (e.g., "pop, upbeat, electronic"). #### negative_tags Styles to avoid. Required when `audio_operation` is `add-instrumental`. #### action Optional `MUSIC` or `LYRICS` value. Choose a model that supports the corresponding public operation; this field does not add lyrics support to a music-only model. #### mv Suno version selector. Required for generation, upload-cover, upload-extend, add-instrumental, and replacement from `audio_url`. Omit it for lyrics, replacement from `task_id` + `audio_id`, and stem separation. Accepted values: `chirp-v3-5`, `chirp-v4`, `chirp-v4-5`, `chirp-v4.5`, `chirp-v4-5plus`, `chirp-v4.5plus`, `chirp-v4-5all`, `chirp-v4.5all`, `chirp-v5`, `chirp-v5-5`, `chirp-v5.5`. #### make_instrumental Set to `true` to generate instrumental output without vocals. #### audio_url Publicly reachable reference or uploaded audio URL. When provided without `audio_operation`, TokenLab uses the upload-cover flow. #### audio_operation Audio operation: `upload-cover`, `upload-extend`, `add-instrumental`, `replace-section`, `vocal-separate`, `multi-stem-separation`, `advanced-split`. `vocal-separate`, `multi-stem-separation`, and `advanced-split` require `task_id` + `audio_id`; `advanced-split` also requires `stem_name`. `upload-extend` requires `continue_at` greater than 0. `add-instrumental` requires `audio_url`, `title`, `tags`, and `negative_tags`. For `replace-section`, send `prompt`, `tags`, `title`, `infill_start_s`, `infill_end_s`, and `full_lyrics`. Choose exactly one source: `task_id` + `audio_id`, or `audio_url` + `mv`. The replacement interval must be 6–60 seconds. #### task_id Source task ID belonging to your organization, for generated-audio replacement or stem separation. #### audio_id Source audio ID returned in the source task result. Required with `task_id` for generated-audio replacement and stem separation. #### infill_start_s Replacement start time in seconds, at least 0. Required for `replace-section`. #### infill_end_s Replacement end time in seconds. Required for `replace-section`; it must be 6–60 seconds after `infill_start_s`. #### full_lyrics Full lyrics for the resulting song. Required for `replace-section`. #### stem_name Stem name. Required for `advanced-split`. #### continue_clip_id ID of a previous clip to continue from. #### continue_at Time in seconds from which to continue. Required and greater than 0 for `upload-extend`. > Note: `prompt` accepts up to 5000 characters; `tags` and `negative_tags` accept up to 1000 characters each. Supported operations and parameters depend on the selected model's current details. ## Response #### id Task ID for polling status. #### task_id Alias of the same asynchronous task ID, when returned. #### poll_url Preferred polling URL for this task when provided. #### status Task status: `pending`, `processing`, `completed`, or `failed`. #### created Task creation time as a Unix timestamp, when returned. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/music/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "suno-music", "mv": "chirp-v4", "prompt": "An upbeat electronic dance track with heavy bass", "title": "Night Drive", "tags": "electronic, EDM, energetic" }' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/music/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "suno-music", "mv": "chirp-v4", "prompt": "An upbeat electronic dance track with heavy bass", "title": "Night Drive", "tags": "electronic, EDM, energetic" } ) response.raise_for_status() task = response.json() task_id = task["id"] poll_url = task.get("poll_url") or f"/v1/tasks/{task_id}" print(f"Task ID: {task_id}; poll URL: {poll_url}") ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/music/generations', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'suno-music', mv: 'chirp-v4', prompt: 'An upbeat electronic dance track with heavy bass', title: 'Night Drive', tags: 'electronic, EDM, energetic' }) }); if (!response.ok) throw new Error(await response.text()); const data = await response.json(); const pollUrl = data.poll_url || `/v1/tasks/${data.id}`; console.log(`Task ID: ${data.id}; poll URL: ${pollUrl}`); ``` ```go Go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { payload := map[string]interface{}{ "model": "suno-music", "mv": "chirp-v4", "prompt": "An upbeat electronic dance track with heavy bass", "title": "Night Drive", "tags": "electronic, EDM, energetic", } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1/music/generations", bytes.NewBuffer(body)) req.Header.Set("Authorization", "Bearer sk-your-api-key") req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("Task ID: %s\n", result["id"]) } ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'suno-music', 'mv' => 'chirp-v4', 'prompt' => 'An upbeat electronic dance track with heavy bass', 'title' => 'Night Drive', 'tags' => 'electronic, EDM, energetic' ]) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo "Task ID: " . $data['id']; ``` ```json Response { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "model": "suno-music" } ``` --- ### Get Music Status > Retrieves the status and result of a music generation task URL: https://tokenlab.sh/docs/en/api-reference/music/get-music-status Poll this endpoint to check the status of your music generation task. > Info: If the create response includes `poll_url`, call that URL directly. `id` and `task_id` identify the same task; you can also use `/v1/tasks/{id}`. > > An expired or unavailable public task can return `404 async_task_not_found`. If task ownership cannot be established for the current API key, the response is `403 task_not_owned`. ## Path Parameters #### id The music generation task ID. ## Response #### id Task ID. #### task_id Alias of the same asynchronous task ID, when returned. #### poll_url Preferred polling URL when the create response supplies one. #### status Task status: `pending`, `processing`, `completed`, or `failed`. #### progress Optional progress value. Returned only when a real progress value is available; use `status` to determine completion. Result fields depend on the operation. Music may return `audio_url` and `audio_id`; lyrics-only tasks return `lyrics`; separation can return multiple files in `data`. `video_url`, title, progress, and other metadata are optional. Check `status` for completion and read the fields actually returned. #### audio_id Optional source audio ID, usable with this task's ID in supported follow-up operations. #### data Optional output array, including separated audio files with `audio_url` or `url`. #### audio_url URL to download the audio file (when completed). #### video_url URL to the video version with visualizer (when completed). #### title Generated song title. #### lyrics Generated or provided lyrics. #### error Error message (when failed). ```bash cURL curl --fail-with-body "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests import time task_id = "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" poll_url = f"/v1/tasks/{task_id}" while True: response = requests.get( f"https://api.tokenlab.sh{poll_url}", headers={"Authorization": "Bearer sk-your-api-key"} ) response.raise_for_status() result = response.json() if result["status"] == "completed": print(result) break elif result["status"] == "failed": print(result.get("error", "Task failed")) break time.sleep(5) # Poll every 5 seconds ``` ```javascript JavaScript const taskId = 'ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; const pollUrl = `/v1/tasks/${taskId}`; async function pollForMusic() { while (true) { const response = await fetch( `https://api.tokenlab.sh${pollUrl}`, { headers: { 'Authorization': 'Bearer sk-your-api-key' } } ); if (!response.ok) throw new Error(await response.text()); const result = await response.json(); if (result.status === 'completed') { console.log(result); return result; } else if (result.status === 'failed') { throw new Error(typeof result.error === 'string' ? result.error : JSON.stringify(result.error ?? result)); } await new Promise(r => setTimeout(r, 5000)); } } await pollForMusic(); ``` ```go Go package main import ( "encoding/json" "fmt" "io" "log" "net/http" "time" ) func main() { taskID := "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" pollURL := fmt.Sprintf("https://api.tokenlab.sh/v1/tasks/%s", taskID) for { req, err := http.NewRequest("GET", pollURL, nil) if err != nil { log.Fatal(err) } req.Header.Set("Authorization", "Bearer sk-your-api-key") client := &http.Client{} resp, err := client.Do(req) if err != nil { log.Fatal(err) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { body, _ := io.ReadAll(resp.Body) resp.Body.Close() log.Fatalf("HTTP %d: %s", resp.StatusCode, body) } var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) resp.Body.Close() if err != nil { log.Fatal(err) } if result["status"] == "completed" { fmt.Println(result) break } else if result["status"] == "failed" { fmt.Println(result) break } time.Sleep(5 * time.Second) } } ``` ```php PHP true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk-your-api-key' ] ]); $response = curl_exec($ch); $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($response === false) { throw new RuntimeException($curlError); } if ($httpStatus < 200 || $httpStatus >= 300) { throw new RuntimeException("HTTP {$httpStatus}: {$response}"); } $result = json_decode($response, true, 512, JSON_THROW_ON_ERROR); if ($result['status'] === 'completed') { echo json_encode($result, JSON_UNESCAPED_SLASHES) . "\n"; break; } elseif ($result['status'] === 'failed') { echo json_encode($result, JSON_UNESCAPED_SLASHES) . "\n"; break; } sleep(5); } ``` ```json Response (Completed) { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "completed", "audio_url": "https://api.tokenlab.sh/v1/media-results/2d7e9a41-8c3b-4b6f-a1e5-7f0c3d9b8e26.mp3?token=…", "title": "Night Drive", "lyrics": "[Verse 1]\nDriving through the night...", "audio_id": "audio_abc123", "media_retention": { "days": 30, "items": [ { "url": "https://api.tokenlab.sh/v1/media-results/2d7e9a41-8c3b-4b6f-a1e5-7f0c3d9b8e26.mp3?token=…", "kind": "audio", "content_type": "audio/mpeg", "status": "saved", "expires_at": "2024-02-22T08:54:20.000Z" } ] } } ``` --- ## Video & Materials ### Create Video > Start a video generation task URL: https://tokenlab.sh/docs/en/api-reference/video/create-video ## Overview Video generation takes time. A successful create response returns a task ID and `poll_url`; use that URL to check progress and retrieve the result. ### Check Task Status Call the `poll_url` in the create response. `id` and `task_id` identify the same task; you can also use `GET /v1/tasks/{id}`. ### Audio and Media Input Audio behavior depends on the selected model **and operation**. A video may contain sound even when no audio selector is exposed. Omitting a selector is different from sending `false`. - `veo3.1` and `veo3.1-fast` always generate audio under the Gemini API contract. For `wan-2.6` and `wan-2.7` video generation, audio cannot be disabled. Omit `output_audio` or use `true` where the model detail permits it. - `hailuo-h3` and Grok video models generate native audio. Do not add an audio switch that the selected model detail does not list. - Seedance 1.5/2.x and `viduq3-pro` / `viduq3-turbo` default to audio on and support silent output. PixVerse C1/V5.6/V6 default to audio off. Use `output_audio` only for operations that list it; Vidu also accepts its declared boolean `audio` field. - `audio_url` / `audio_urls` provide input or reference audio. They are not audio-output switches. Video editing, motion transfer, and style transfer may retain the input soundtrack; preserving source audio does not mean muting it. Check the model detail for allowed values and audio-dependent prices. Supported aliases `outputAudio`, `generate_audio`, and boolean `audio` must agree with `output_audio` when combined. Do not assume every version or operation in a model family shares the same audio controls. Use publicly reachable `https` URLs for images, videos, and audio. Some models also accept inline `data:` URLs; URLs are more reliable for large files. ## Request Body #### model Video model ID. Use the [Models API](/api-reference/models/list-models) for current models and `operation` for text-to-video, image-to-video, and other generation modes. > Note: **PixVerse** > - Model: `pixverse-c1`, `pixverse-v6`, `pixverse-v5.6` > - Operations: `text-to-video`, `image-to-video`, `start-end-to-video`, `reference-to-video` > - Audio selector: `output_audio`, default `false` > > On TokenLab, the PixVerse models above do not accept `operation=video-extension`. > > **HappyHorse** > - Model: `happyhorse-1.0` > - Operations: `text-to-video`, `image-to-video`, `reference-to-video`, `video-to-video` > - Audio selector: Do not send `output_audio` #### prompt Text description of the video to generate. Required for most public video models. #### operation Generation mode. Supported values include `text-to-video`, `image-to-video`, `reference-to-video`, `start-end-to-video`, `video-to-video`, `video-extension`, `audio-to-video`, and `motion-control`. If omitted, TokenLab infers it from the inputs. Sending it explicitly catches mismatched fields earlier. #### image_url Publicly accessible URL of the starting image for image-to-video generation. For Seedance material assets, you may also pass `asset://asset-YYYYMMDDHHMMSS-xxxxx` to use an `ACTIVE` TokenLab material as the first frame. For best cross-model compatibility, prefer `image_url` with an `https` URL. #### image Inline image as a data URL (for example, `data:image/jpeg;base64,...`). Supported by compatible models, but `image_url` provides the broadest compatibility across public video models. #### reference_images Reference image inputs for models that support dedicated reference conditioning. The supported count is model-dependent. For `seedance-2.0` and `seedance-2.0-fast`, TokenLab currently supports up to 9 reference images plus up to 3 reference videos and 3 reference audios. Each Seedance reference may be an `https` URL or an `asset://asset-YYYYMMDDHHMMSS-xxxxx` URI for an `ACTIVE` TokenLab material. For model selection, 4K boundaries, and Mini notes, see the [Seedance 2.0 video models guide](/guides/seedance-2-video). Public `https` URLs are recommended for non-material inputs; compatible models also accept inline `data:` URLs. For `grok-imagine-video`, reference-to-video accepts up to 7 image references and `duration` is capped at 10 seconds. `grok-imagine-video-1.5-preview` is image-to-video only and does not accept reference images. #### material_asset_id TokenLab Seedance material asset ID returned by [Create Material Asset](/api-reference/video/create-material-asset). Use it after the asset is `ACTIVE` with Seedance models that can use the TokenLab material library. This field is a generic reference input; to assign first-frame, last-frame, or reference-image semantics explicitly, put `asset://` in `start_image`, `end_image`, or `reference_images` instead. #### material_asset_ids Multiple TokenLab Seedance material asset IDs. They share the same Seedance image-reference limit as `reference_images`; the selected model must be able to use the TokenLab material library. > Info: Ordinary image URLs are passed as image inputs; they do not create reusable material assets. Create reusable assets through the material API, then use their TokenLab IDs or `asset://asset-YYYYMMDDHHMMSS-xxxxx` URIs. If explicit materials return `409 seedance_material_preparing`, check the returned `inactive_asset_ids` and retry after those materials become `ACTIVE`. #### reference_image_type Optional reference role for models that distinguish between `asset` and `style` references. #### kling_elements Use `kling_elements` only when the selected model’s current public detail lists this field. Provide image inputs and 1–3 elements, each with `name`, optional `description`, and 2–4 `element_input_urls`; reference them as `@name` in `prompt`. Do not combine element references with `output_audio=true`. #### video_url Publicly accessible URL of the source video. Required for video-url based `video-to-video` flows and for `motion-control`; some derivative flows use `task_id` instead. #### video_urls Additional reference video inputs for models that support multimodal reference conditioning. The supported count is model-dependent. For `seedance-2.0` and `seedance-2.0-fast`, TokenLab currently supports up to 3 reference videos. #### audio_url Public audio URL for an audio-driven or reference-audio operation supported by the selected model. #### audio_urls Additional reference audio inputs for models that support multimodal reference conditioning. The supported count is model-dependent. For `seedance-2.0` and `seedance-2.0-fast`, TokenLab currently supports up to 3 reference audios. #### task_id Task identifier used by some continuation, extension, or derivative flows. #### extend_at Model-specific extension start offset used by some `video-extension` flows. #### extend_times Model-specific extension multiplier or repeat count used by some `video-extension` flows. #### duration Generated output video duration in seconds. For Seedance 1.5/2.0 models, omitting this field uses `5`; sending `-1` lets the model choose within its supported duration range, and billing is estimated conservatively until the task finishes. #### seconds Compatibility alias for `duration`. If both `seconds` and `duration` are sent, they must be identical. For Seedance, `seconds=-1` has the same auto-duration meaning as `duration=-1`. #### aspect_ratio Aspect ratio, for example `adaptive`, `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, or `21:9`. Seedance defaults to `adaptive` when omitted. #### resolution Model-dependent output resolution. Seedance defaults to `720p`; `seedance-2.0` supports `480p`, `720p`, `1080p`, and `4k`, while `seedance-2.0-fast` and `seedance-2.0-mini` are limited to `480p` and `720p`. #### output_audio Audio-output selector for operations that declare it. Omission follows the model default; `false` requests silent output only when allowed. See the audio behavior above and the selected model detail. #### draft Enable Seedance 1.5 Pro draft mode. Do not send it with `draft_task_id`. #### draft_task_id Seedance 1.5 Pro draft promotion task ID. Send a previous draft task ID to create the final video; this is not a generic video field. #### ratio Compatibility alias for `aspect_ratio`. If both `ratio` and `aspect_ratio` are sent, they must be identical. #### generate_audio Compatibility alias for `output_audio`. If `generate_audio`, `output_audio`, and `outputAudio` appear together, all values must match. #### execution_expires_after Optional execution expiry in seconds for compatible video models. Seedance defaults to `172800` seconds when omitted. #### priority Optional task priority from `0` to `9` for compatible video models. Do not combine `priority` with `service_tier=flex`. #### safety_identifier Optional end-user safety identifier for compatible video models. If omitted for Seedance, TokenLab uses `user` when provided. #### service_tier `default` is accepted as a compatibility no-op for Seedance 2.0 models. `flex` is only allowed when the selected model supports it. #### frames Optional frame count for compatible video models. Seedance 2.0 models and Seedance 1.5 Pro do not support this field. #### camera_fixed Optional fixed-camera selector for compatible video models. Seedance 2.0 models do not support this field. #### fps Frames per second (1-120) for models that expose FPS control. #### negative_prompt What to avoid in the generated video. #### seed Random seed for reproducible generation. Seedance uses `-1` for a random seed when omitted. #### cfg_scale Prompt adherence strength (0-20) for models that expose CFG-style control. #### motion_strength Motion intensity (0-1) for models that expose it. #### start_image URL or compatible image input for the first frame in `start-end-to-video`. #### end_image URL or compatible image input for the last frame in `start-end-to-video`. #### size Model-specific size tier for compatible video models. #### watermark Optional watermark toggle for models that expose it. Seedance defaults to `false` when omitted. #### effect_type Model-specific effect selector for specialized editing flows. #### user A unique identifier for the end-user. For Seedance, TokenLab also uses this value as `safety_identifier` when that field is omitted. ## Compatible Field Names - Primary fields use snake_case: `aspect_ratio`, `output_audio`, `reference_images`, and `reference_image_type`. - For compatibility, TokenLab also accepts `ratio`, `generate_audio`, `outputAudio`, `seconds`, `referenceImages`, and `referenceImageType`. - If a primary field and its alias are both sent, their values must match. - If `operation` is omitted, TokenLab infers it from the inputs. ## Media Input - Prefer publicly reachable `https` URLs over inline base64 for `image_url`, `reference_images`, `video_url`, and `audio_url`. - Avoid mixing inline base64 and remote URLs in the same request. - Keep remote media URLs valid until task processing starts. ## Seedance Parameters For Seedance 1.5/2.0 models, the unified endpoint follows TokenLab field names while accepting the compatible aliases `seconds`, `ratio`, and `generate_audio`. Omitted Seedance selectors use these defaults: `duration=5`, `resolution=720p`, `aspect_ratio=adaptive`, `output_audio=true`, `watermark=false`, `return_last_frame=false`, `execution_expires_after=172800`, `priority=0`, and `seed=-1`. `duration=-1` or `seconds=-1` lets Seedance choose the output duration within the model-supported range. TokenLab estimates cost conservatively before the task finishes, then settles from the completed task usage when available. `service_tier=default` is accepted for Seedance 2.0 as a compatibility no-op; `service_tier=flex`, `frames`, and `camera_fixed` are rejected where the selected model does not support them. ### Seedance Example ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/videos/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "seedance-2.5", "prompt": "A sleek product reveal with cinematic camera movement", "operation": "text-to-video", "duration": -1, "aspect_ratio": "adaptive", "resolution": "720p", "output_audio": true }' ``` ## Response > Note: Fields describing results, errors, timestamps, and the model are returned when available for the task. #### id Canonical async task identifier. Treat this as the same identity as `task_id` when both are present. #### task_id Canonical async task identifier for polling. This is the same task identity used by async status endpoints. #### poll_url Preferred polling URL for this task. Use this exact path when checking status. #### billing_transaction_id TokenLab billing transaction ID when settlement already completed. This is the dashboard/accounting transaction identifier and is separate from the async `id` / `task_id`. #### status Task status: `pending`, `processing`, `completed`, `failed`. #### created Unix timestamp when the task was created. #### model Model used. #### video_url Direct video URL when the result is already available. #### video Single video result with `url`, `duration`, `width`, and `height` when available. #### videos Multiple video results when the task returns more than one output. #### error Error message or structured error object when the task fails. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/videos/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "veo3.1", "prompt": "A cat walking through a garden, cinematic lighting", "operation": "text-to-video", "duration": 4, "aspect_ratio": "16:9" }' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/videos/generations", headers={ "Authorization": "Bearer sk-your-api-key", "Content-Type": "application/json" }, json={ "model": "veo3.1", "prompt": "A cat walking through a garden, cinematic lighting", "operation": "text-to-video", "duration": 4, "aspect_ratio": "16:9" } ) data = response.json() task_id = data["task_id"] print(f"Poll URL: {data['poll_url']}") print(f"Task ID: {task_id}") ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/videos/generations', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'veo3.1', prompt: 'A cat walking through a garden, cinematic lighting', operation: 'text-to-video', duration: 4, aspect_ratio: '16:9' }) }); const data = await response.json(); console.log(`Poll URL: ${data.poll_url}`); console.log(`Task ID: ${data.task_id}`); ``` ```json Response { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "model": "veo3.1", "created": 1706000000 } ``` ## Image to Video ```python response = requests.post( "https://api.tokenlab.sh/v1/videos/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "hailuo-2.3", "prompt": "The scene begins from the provided image and adds gentle natural motion.", "operation": "image-to-video", "image_url": "https://example.com/image.jpg", "duration": 6, "resolution": "768p" } ) ``` ## Kling 3.0 Elements Use `kling_elements` only when the selected model’s current public detail lists this field. Provide image inputs and 1–3 elements, each with `name`, optional `description`, and 2–4 `element_input_urls`; reference them as `@name` in `prompt`. Do not combine element references with `output_audio=true`. ## Reference to Video Use `operation=reference-to-video` when the model supports dedicated reference conditioning. In TokenLab requests, image references use `reference_images`, while multimodal reference videos and audios use `video_urls` and `audio_urls`. For `seedance-2.0` and `seedance-2.0-fast`, TokenLab currently supports up to 9 reference images plus up to 3 reference videos and 3 reference audios. For model selection, 4K boundaries, and Mini notes, see the [Seedance 2.0 video models guide](/guides/seedance-2-video). `duration` controls generated output length only; it does not set a separate limit for reference video input duration. For `grok-imagine-video`, reference-to-video accepts up to 7 image references (`reference_images` or `image_urls`) and `duration` is capped at 10 seconds. Do not combine reference images with `image_url` / `image` first-frame inputs. `grok-imagine-video-1.5-preview` is image-to-video only. ```python response = requests.post( "https://api.tokenlab.sh/v1/videos/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "veo3.1", "prompt": "Keep the same subject identity, palette, and framing while adding subtle natural motion.", "operation": "reference-to-video", "reference_images": [ "https://example.com/ref-a.jpg", "https://example.com/ref-b.jpg" ], "reference_image_type": "asset", "duration": 8, "resolution": "720p", "aspect_ratio": "9:16" } ) ``` ## Keyframe Control Use `start_image` and `end_image` to control the first and last frames: ```python response = requests.post( "https://api.tokenlab.sh/v1/videos/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "viduq2-pro", "operation": "start-end-to-video", "start_image": "https://example.com/day.jpg", "end_image": "https://example.com/night.jpg", "duration": 5, "resolution": "720p", "aspect_ratio": "16:9" } ) ``` ## Video to Video For `grok-imagine-video` video-to-video, send a public HTTPS `.mp4` URL in `video_url` and the editing `prompt`. Omit `resolution`, `duration`, and `aspect_ratio` for this operation. Use `operation=video-to-video` when the model accepts an existing video as the primary input. ```python response = requests.post( "https://api.tokenlab.sh/v1/videos/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "grok-imagine-video", "operation": "video-to-video", "video_url": "https://example.com/source.mp4", "prompt": "Enhance the clip while preserving the original motion." } ) ``` ## Motion Control Use `operation=motion-control` when the model expects both a subject image and a motion reference video. TokenLab maps the public `image_url` + `video_url` request shape to the motion-control request format for that model. ```python response = requests.post( "https://api.tokenlab.sh/v1/videos/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "kling-3.0-motion-control", "operation": "motion-control", "prompt": "Keep the subject identity stable while following the motion reference.", "image_url": "https://example.com/subject.png", "video_url": "https://example.com/motion.mp4", "resolution": "720p" } ) ``` ## Model Discovery Public video inventory and supported operations change over time. Use the [Models API](/api-reference/models/list-models) to check current support before wiring a model-specific flow: ```bash curl "https://api.tokenlab.sh/v1/models?recommended_for=video" curl "https://api.tokenlab.sh/v1/models/veo3.1" ``` Read the model detail response before relying on model-specific operations or fields. Operations such as `audio-to-video` and `video-extension` are model-specific; confirm current availability there instead of relying on static examples in this page. --- ### Create Task (Volc Compatible) > Create a Seedance task with the Volc-compatible API. URL: https://tokenlab.sh/docs/en/api-reference/video/create-volc-compatible-seedance-task ## Overview Existing Volc-style Seedance clients can use TokenLab by changing the API address and key. > Note: The examples use Seedance 2.0. This endpoint also supports Seedance 2.5 with the model-specific differences below. The reusable TokenLab material URI examples on this page apply to Seedance 2.0. See also [Seedance 2.0 Video Models](/guides/seedance-2-video) and [Video Generation](/guides/video-generation). ## Authentication And Endpoints - Use `Authorization: Bearer `. - Volc AK/SK signing is not accepted. Requests must include a TokenLab Bearer key. - Use the official task path: `POST /api/v3/contents/generations/tasks`. ## Content Rules - `type: "text"` is the prompt text. - `type: "image_url"` without `role`, or with `role: "first_frame"`, is treated as the first frame. - `role: "last_frame"` must be paired with a first frame. - `role: "reference_image"`, `reference_video`, and `reference_audio` are used as references. - `image_url.url` accepts a public image URL or a material URI such as `asset://asset-YYYYMMDDHHMMSS-xxxxx`. The `role` determines whether that material is a first frame, last frame, or reference image. - Do not mix first/last frame inputs with reference media in one request. - Top-level `material_asset_id` and `material_asset_ids` are rejected. Put a TokenLab material URI in `image_url.url`. `priority` is supported only for Seedance 2.5. ## Parameter Notes `duration` is an integer number of seconds; `-1` selects automatic duration. Defaults and limits differ by model: | Parameter | Seedance 2.0 | Seedance 2.5 | |---|---|---| | `duration` | `4`–`15` / `-1` (default: `5`) | `4`–`30` / `-1` (default: `-1`) | | `resolution` | `480p`, `720p`, `1080p` (default: `720p`) | `480p`, `720p` (default: `720p`) | | `generate_audio` | `boolean` (default: `false`) | `boolean` (default: `true`) | | `priority` | Not supported | `integer`: `0`–`9` | | `seed` | `integer`: `-1`–`4294967295` (default: `-1`) | Not supported | For Seedance 2.5, first-frame, first/last-frame, video extension, and video-to-video requests require `ratio: "adaptive"`; video-to-video also requires `duration: -1`. `output_format` accepts `mp4` or `mov` only for Seedance 2.5. - `ratio` accepts `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `21:9`, or `adaptive`. - `watermark`, `return_last_frame`, `seed`, `execution_expires_after`, and `safety_identifier` are accepted when valid for the selected model. - `callback_url` may point to a public HTTP(S) endpoint. ## Callback Delivery When `callback_url` is present, TokenLab sends an HTTP `POST` when task status changes. Callback statuses are `queued`, `running`, `succeeded`, `failed`, and `expired`. The JSON body matches the get-task response. A `2xx` response acknowledges delivery. For `succeeded` and `failed`, a delivery that does not succeed within five seconds is retried up to three times. The callback has only the standard JSON content header and no TokenLab-specific delivery headers. Redirects are not followed, and private or reserved network targets are rejected. Save the task ID. If a callback is not delivered, you can still retrieve the result with the get-task endpoint. ## Image Preparation Public HTTP(S) image URLs and supported data URLs are used as supplied; they are not automatically saved as reusable materials. Explicit `asset://asset-...` references use existing TokenLab materials. TokenLab checks ownership and readiness before generation. If a referenced material is still preparing, wait until it is ready and retry; inspect `error.code` and `error.message` when creation fails. For existing materials, use the public `asset-YYYYMMDDHHMMSS-xxxxx` ID rather than an original asset ID returned by another system. TokenLab verifies material ownership before generation. ## Create Response ```json { "id": "cgt-20260102030405-a1b2c" } ``` The create response contains only the task ID. Save it so you can retrieve status and results at any time. ## Prevent Duplicate Tasks Send a unique `Idempotency-Key` with create requests. If the connection closes before the response arrives, retry with the same API key, idempotency key, and request body: - If the original task was created, TokenLab returns the same `cgt-...` ID and adds `Idempotency-Replayed: true`. - If the original request is still being registered, TokenLab returns `409 IdempotencyRequestInProgress`; retry later with the same key and body. - Reusing the key with a different body returns `409 IdempotencyConflict` and never creates a second task. Idempotency applies to the official v3 REST create path. It does not change the JSON response shape, and it is not inferred from `X-Request-ID` or from identical request bodies without a key. ## Example ### REST Create ```bash curl https://api.tokenlab.sh/api/v3/contents/generations/tasks \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Idempotency-Key: $CLIENT_JOB_ID" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2.0", "content": [ {"type": "text", "text": "A cinematic forest at sunset"}, {"type": "image_url", "role": "reference_image", "image_url": {"url": "https://example.com/ref.png"}} ], "ratio": "16:9", "duration": 5, "resolution": "720p", "generate_audio": false, "callback_url": "https://example.com/webhooks/seedance" }' ``` For existing materials, put each URI in the official `content[]` item and declare its role: ```json [ { "type": "image_url", "role": "first_frame", "image_url": {"url": "asset://asset-20260720123458-start"} }, { "type": "image_url", "role": "last_frame", "image_url": {"url": "asset://asset-20260720123459-end01"} } ] ``` ## Next Step Use the returned `cgt-...` ID with [Get Task (Volc Compatible)](/api-reference/video/get-volc-compatible-seedance-task) until the task reaches a terminal status. --- ### Get Task (Volc Compatible) > Get a Seedance compatibility task by its cgt task ID. URL: https://tokenlab.sh/docs/en/api-reference/video/get-volc-compatible-seedance-task ## Overview Use the `cgt-...` ID returned by create. Poll every few seconds while the task is `queued` or `running`. Stop when the task is `succeeded`, `failed`, `cancelled`, or `expired`. > Warning: Do not reuse the unified v1 task enum or response struct here. `/v1/tasks/{id}` uses `pending`, `processing`, `completed`, and `failed`; this v3 endpoint uses the official enum above. In the v3 JSON object, `duration` is a string when present and `error` is optional and contains error details when available. A strict Go client should declare `Duration` as `string` with the JSON tag `duration,omitempty`, and stop on every v3 terminal status. See also [Seedance 2.0 Video Models](/guides/seedance-2-video) and [Video Generation](/guides/video-generation). ## Status Values - `queued`: accepted and waiting to run. - `running`: generation is in progress. - `succeeded`: result is ready and `content.video_url` is present. - `failed`: generation failed; inspect `error.code` and `error.message`. - `cancelled`: the task was cancelled before completion. - `expired`: the task expired before completion. ## Response Fields - `id`: compatibility task ID, starting with `cgt-`. - `model`: model identifier echoed for compatibility. - `status`: one of the status values above. - `content.video_url`: final video URL on success. - `content.last_frame_url`: present only when requested and available. - `usage`: token usage when available. - `created_at` and `updated_at`: task timestamps. - `error`: Optional error details when available. Inspect `error.code` and `error.message`; do not infer field presence solely from `status`. The response never adds TokenLab fields such as `progress`, `return_last_frame`, `watermark`, `priority`, or `camera_fixed`. ## Example ```bash curl https://api.tokenlab.sh/api/v3/contents/generations/tasks/cgt-20260102030405-a1b2c \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ```json { "id": "cgt-20260102030405-a1b2c", "model": "doubao-seedance-2.0", "status": "succeeded", "content": { "video_url": "https://api.tokenlab.sh/v1/media-results/3f2b8c1e-6d4a-4e9b-8a7c-2d5e1f0b9c4a.mp4?token=…" }, "usage": { "completion_tokens": 123456 } } ``` ```go Go type SeedanceTask struct { ID string `json:"id"` Status string `json:"status"` Duration string `json:"duration,omitempty"` } func isTerminal(status string) bool { switch status { case "succeeded", "failed", "cancelled", "expired": return true default: return false } } ``` --- ### List Tasks (Volc Compatible) > List recent Seedance compatibility tasks for the current organization. URL: https://tokenlab.sh/docs/en/api-reference/video/list-volc-compatible-seedance-tasks ## Overview List recent compatibility tasks in the authenticated Workspace using a TokenLab Bearer API key. Without `filter.task_ids`, results cover recent tasks from the past seven days; supplying exact IDs queries those tasks directly. See also [Seedance 2.0 Video Models](/guides/seedance-2-video) and [Video Generation](/guides/video-generation). ## Listing And Filters - `page_num`: default `1`, integer from `1` to `500`. - `page_size`: default `20`, integer from `1` to `500`. - `filter.status`: `queued`, `running`, `succeeded`, `failed`, `cancelled`, `expired`. - `filter.task_ids`: Repeat this parameter for multiple exact task IDs; do not join them with commas. - `filter.model`: Model identifier. - `filter.service_tier`: `default`, `flex`. The response contains `items` and `total` for the filtered results. Each item uses the get-task response shape. Use the get endpoint to refresh a known task’s status. ## Example ```bash curl "https://api.tokenlab.sh/api/v3/contents/generations/tasks?page_num=1&page_size=20&filter.status=succeeded" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ```bash curl "https://api.tokenlab.sh/api/v3/contents/generations/tasks?filter.task_ids=cgt-20260102030405-a1b2c" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` --- ### Cancel Task (Volc Compatible) > Cancel a queued or cancellable Seedance compatibility task. URL: https://tokenlab.sh/docs/en/api-reference/video/delete-volc-compatible-seedance-task ## Overview Cancellation is best-effort and only succeeds while the task is still cancellable. A successful DELETE returns HTTP 200 with an empty body. Query the task afterward if you need to confirm the final state. See also [Seedance 2.0 Video Models](/guides/seedance-2-video) and [Video Generation](/guides/video-generation). ## Cancellation Semantics - The request can succeed while the task is queued or still cancellable. - If the task has already completed, failed, expired, or is no longer cancellable, the endpoint returns an error; query the task to confirm its current state. - A cancelled task does not return a video result. ## Example ```bash curl -X DELETE https://api.tokenlab.sh/api/v3/contents/generations/tasks/cgt-20260102030405-a1b2c \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` --- ### Get Video Status > Get a video task's status and result URL: https://tokenlab.sh/docs/en/api-reference/video/get-video-status ## Path Parameters #### id The task ID returned by the create video request. `id` and `task_id` identify the same task. > Info: If the create response includes `poll_url`, call that URL directly. > > An expired or unavailable public task can return `404 async_task_not_found`. If task ownership cannot be established for the current API key, the response is `403 task_not_owned`. ## Response > Note: Fields describing results, errors, timestamps, and the model are returned when available for the task. #### id Task ID. #### task_id Same task ID as `id`. #### poll_url URL for checking status and retrieving the result. #### billing_transaction_id Billing transaction ID after settlement. It is separate from the task ID. #### status Task status: `pending`, `processing`, `completed`, `failed`. #### progress Optional progress value. Returned only when a real progress value is available; use `status` to determine completion. #### video_url URL of the generated video (when completed). If it points to the TokenLab API, download it with the same `Authorization: Bearer` header used for task polling. Only the organization that owns the task can access the result. #### video Single video result with `url`, `duration`, `width`, and `height` when available. #### videos Multiple video results when the task returns more than one output. #### error Error message (if failed). #### created Creation timestamp. #### updated Last update timestamp. #### model Model used for the task. ```bash cURL curl "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests import time task_id = "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" while True: response = requests.get( f"https://api.tokenlab.sh/v1/tasks/{task_id}", headers={"Authorization": "Bearer sk-your-api-key"} ) response.raise_for_status() data = response.json() print(f"Status: {data['status']}") if data["status"] == "completed": print(f"Video URL: {data['video_url']}") break elif data["status"] == "failed": print(f"Error: {data['error']}") break time.sleep(5) # Poll every 5 seconds ``` ```javascript JavaScript const taskId = 'ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; async function pollForVideo() { while (true) { const response = await fetch( `https://api.tokenlab.sh/v1/tasks/${taskId}`, { headers: { 'Authorization': 'Bearer sk-your-api-key' } } ); if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`); const data = await response.json(); console.log(`Status: ${data.status}`); if (data.status === 'completed') { console.log(`Video URL: ${data.video_url}`); return data.video_url; } else if (data.status === 'failed') { throw new Error(data.error); } await new Promise(r => setTimeout(r, 5000)); } } ``` ```go Go package main import ( "encoding/json" "fmt" "net/http" "time" ) func main() { taskID := "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" for { req, _ := http.NewRequest("GET", fmt.Sprintf("https://api.tokenlab.sh/v1/tasks/%s", taskID), nil) req.Header.Set("Authorization", "Bearer sk-your-api-key") client := &http.Client{} resp, err := client.Do(req) if err != nil { panic(err) } if resp.StatusCode >= 400 { panic(fmt.Sprintf("HTTP %d", resp.StatusCode)) } var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) resp.Body.Close() fmt.Printf("Status: %s\n", result["status"]) if result["status"] == "completed" { fmt.Printf("Video URL: %s\n", result["video_url"]) break } else if result["status"] == "failed" { fmt.Printf("Error: %s\n", result["error"]) break } time.Sleep(5 * time.Second) } } ``` ```php PHP true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk-your-api-key' ] ]); $response = curl_exec($ch); $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($response === false || $httpStatus >= 400) { throw new RuntimeException("HTTP request failed: {$httpStatus}"); } $data = json_decode($response, true); echo "Status: {$data['status']}\n"; if ($data['status'] === 'completed') { echo "Video URL: {$data['video_url']}\n"; break; } elseif ($data['status'] === 'failed') { echo "Error: {$data['error']}\n"; break; } sleep(5); } ``` ```json Response (pending) { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "model": "veo3.1", "created": 1706000000, "updated": 1706000000 } ``` ```json Response (processing) { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "processing", "model": "veo3.1", "created": 1706000000, "updated": 1706000030 } ``` ```json Response (completed) { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "billing_transaction_id": "cmo164zyc0s48jpgegi80vqv4", "status": "completed", "video_url": "https://api.tokenlab.sh/v1/media-results/6f1c2a9e-3b7d-4e2a-9c51-8d0f4a7b2e13.mp4?token=…", "model": "veo3.1", "created": 1706000000, "updated": 1706000060, "media_retention": { "days": 30, "items": [ { "url": "https://api.tokenlab.sh/v1/media-results/6f1c2a9e-3b7d-4e2a-9c51-8d0f4a7b2e13.mp4?token=…", "kind": "video", "content_type": "video/mp4", "status": "saved", "expires_at": "2024-02-22T08:54:20.000Z" } ] } } ``` ```json Response (failed) { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "failed", "error": "Content policy violation", "model": "veo3.1", "created": 1706000000, "updated": 1706000060 } ``` ## Check Interval - Check every 5–10 seconds. - Increase the interval for long-running tasks. - Set a maximum wait time for your application. - Stop when the status is `failed` and show the returned `error`. ```python import requests import time def wait_for_video(task_id, max_wait=600, interval=5): """Wait for video with timeout.""" start = time.time() while time.time() - start < max_wait: response = requests.get( f"https://api.tokenlab.sh/v1/tasks/{task_id}", headers={"Authorization": "Bearer sk-your-api-key"} ) response.raise_for_status() data = response.json() if data["status"] == "completed": return data["video_url"] elif data["status"] == "failed": raise Exception(data.get("error", "Video generation failed")) time.sleep(interval) raise TimeoutError("Video generation timed out") ``` --- ### Create Visual Validation Session > Creates a Volc-compatible real-person visual validation session. URL: https://tokenlab.sh/docs/en/api-reference/video/create-visual-validation-session Create the real-person visual validation session before uploading reusable `liveness_face` materials. TokenLab uses the Volc Action API shape and TokenLab Bearer authentication. Use the same authenticated organization, user, and case-sensitive `ProjectName` that created the session. Open `Result.H5Link` immediately after creating the session. Its initial launch can expire before the 30-minute `BytedToken`; do not delay opening it. ## Endpoint And Authentication ```text POST https://api.tokenlab.sh/api/v3?Action=CreateVisualValidateSession&Version=2024-01-01 Authorization: Bearer Content-Type: application/json ``` Volc AK/SK signing is not accepted. `CallbackURL` is required and must be a public HTTPS URL. `ProjectName` is optional and defaults to `default`. ## Flow 1. Save `Result.BytedToken` and open `Result.H5Link` for the person being verified. 2. To select the H5 language, append `lng` to `H5Link`, for example `&lng=en`. The language is not a request-body field. 3. After the H5 flow finishes, the browser opens `Result.CallbackURL`. The callback query can contain `bytedToken`, `resultCode`, `algorithmBaseRespCode`, `reqMeasureInfoValue`, and `verify_type`. 4. Poll [Get Visual Validation Result](/api-reference/video/get-visual-validation-result) with the saved `BytedToken` until `Result.GroupId` is returned. `BytedToken` is valid for 30 minutes and is scoped to the authenticated organization and `ProjectName`. The H5 link supports one completed authentication and becomes invalid after use. Treat `Result.H5Link` as an opaque browser URL: open it as a top-level page, and do not parse, decode, rewrite, or embed it. TokenLab owns the browser handoff and callback bridge. ## Example Request ```bash curl 'https://api.tokenlab.sh/api/v3?Action=CreateVisualValidateSession&Version=2024-01-01' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"CallbackURL":"https://yourapp.example.com/seedance/callback","ProjectName":"default"}' ``` ## Example Response ```json { "ResponseMetadata": { "RequestId": "req_abc123", "Action": "CreateVisualValidateSession", "Version": "2024-01-01", "Service": "ark", "Region": "cn-beijing" }, "Result": { "BytedToken": "ZXhhbXBsZS10b2tlbg", "H5Link": "https://api.tokenlab.sh/api/v3/visual-validation/sessions/svv_example/h5?Version=2024-01-01", "CallbackURL": "https://yourapp.example.com/seedance/callback" } } ``` Errors use the same top-level envelope. Read `ResponseMetadata.Error.Code` and `ResponseMetadata.Error.Message` instead of expecting the fields from TokenLab's other `/v1` APIs. --- ### Get Visual Validation Result > Gets the real-person material group produced by visual validation. URL: https://tokenlab.sh/docs/en/api-reference/video/get-visual-validation-result Poll this Action after the H5 visual validation flow. A successful result returns the TokenLab material group ID for the verified person. Use the same authenticated organization, user, and case-sensitive `ProjectName` that created the session. ## Endpoint And Authentication ```text POST https://api.tokenlab.sh/api/v3?Action=GetVisualValidateResult&Version=2024-01-01 Authorization: Bearer Content-Type: application/json ``` Send the `BytedToken` returned by [Create Visual Validation Session](/api-reference/video/create-visual-validation-session). If you set `ProjectName` during creation, send the same case-sensitive value here; otherwise omit it or use `default`. The result may not be ready immediately after the browser callback. Retry the same request until `Result.GroupId` is returned or the 30-minute `BytedToken` expires. Repeating a successful request with the same token returns the same mapped `GroupId`. Use the returned `GroupId` as the `group_id` when creating `liveness_face` material assets. ## Example Request ```bash curl 'https://api.tokenlab.sh/api/v3?Action=GetVisualValidateResult&Version=2024-01-01' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"BytedToken":"ZXhhbXBsZS10b2tlbg","ProjectName":"default"}' ``` ## Example Response ```json { "ResponseMetadata": { "RequestId": "req_def456", "Action": "GetVisualValidateResult", "Version": "2024-01-01", "Service": "ark", "Region": "cn-beijing" }, "Result": { "GroupId": "group-20260720123456-real1" } } ``` Errors use `ResponseMetadata.Error.Code` and `ResponseMetadata.Error.Message`. A success response contains `ResponseMetadata` and `Result`; it does not use the response envelope from TokenLab's other `/v1` APIs. --- ### Material Actions (Volc Compatible) > Reference for the 10 Volcengine-compatible material and material-group Actions. URL: https://tokenlab.sh/docs/en/api-reference/video/volc-compatible-material-actions These endpoints support the material Actions and fields listed below, using TokenLab Bearer authentication and the `ResponseMetadata + Result` envelope. ## Endpoint And Authentication ```text POST https://api.tokenlab.sh/?Action=&Version=2024-01-01 Authorization: Bearer Content-Type: application/json ``` `POST /api/v3?Action=&Version=2024-01-01` is also supported. All material Actions are POST-only. Volcengine AK/SK signatures are not accepted; a request with no valid TokenLab Bearer key returns `401 InvalidCredential`. `ProjectName` defaults to `default` and is a real isolation boundary inside the authenticated organization. Use the same value when creating, listing, reading, updating, or deleting related groups and assets. ## Material-Group Actions | Action | Purpose | Request body | |--------|---------|--------------| | `CreateAssetGroup` | Create a virtual-avatar material group | `Name`; optional `Description`; optional `GroupType: "AIGC"`; `ProjectName` | | `ListAssetGroups` | List groups in one project | Optional `Filter.GroupIds`, `Filter.GroupType`, `Filter.Name`; `PageNumber`, `PageSize`, `SortBy`, `SortOrder`, `ProjectName` | | `GetAssetGroup` | Read one group | `Id`, `ProjectName` | | `UpdateAssetGroup` | Change group name or description | `Id`; `Name` and/or `Description`; `ProjectName` | | `DeleteAssetGroup` | Delete a group and its assets | `Id`, `ProjectName` | `CreateAssetGroup` accepts only `GroupType: "AIGC"`. A `LivenessFace` group is created by successful real-person verification, not by this Action. `ListAssetGroups` can filter both `AIGC` and `LivenessFace` groups. ### Create A Group ```bash curl 'https://api.tokenlab.sh/?Action=CreateAssetGroup&Version=2024-01-01' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "Name":"Product references", "Description":"Reusable product shots", "GroupType":"AIGC", "ProjectName":"default" }' ``` ```json { "ResponseMetadata": { "RequestId": "req-id", "Action": "CreateAssetGroup", "Version": "2024-01-01", "Service": "ark", "Region": "cn-beijing" }, "Result": { "Id": "group-20260720123456-abc12" } } ``` ## Material-Asset Actions | Action | Purpose | Request body | |--------|---------|--------------| | `CreateAsset` | Import one public image, video, or audio URL | `GroupId`, `URL`, `Name`, `AssetType`, `ProjectName` | | `ListAssets` | List assets in one project | Optional `Filter.GroupIds`, `Filter.GroupType`, `Filter.Statuses`, `Filter.Name`; `PageNumber`, `PageSize`, `SortBy`, `SortOrder`, `ProjectName` | | `GetAsset` | Read one asset and its temporary URL | `Id`, `ProjectName` | | `UpdateAsset` | Rename an asset | `Id`; optional `Name`; `ProjectName` | | `DeleteAsset` | Delete one asset | `Id`, `ProjectName` | `AssetType` accepts `Image`, `Video`, or `Audio`. The target group determines whether the asset is a normal `AIGC` material or a verified `LivenessFace` material. ### Create An Asset ```bash curl 'https://api.tokenlab.sh/?Action=CreateAsset&Version=2024-01-01' \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "GroupId":"group-20260720123456-abc12", "URL":"https://example.com/reference.png", "Name":"Front view", "AssetType":"Image", "ProjectName":"default" }' ``` Use TokenLab material IDs. `CreateAsset` returns `Result.Id` before processing finishes; poll `GetAsset` until `Result.Status` is `Active` or `Failed`. `ListAssets` also accepts `SortBy: "GroupId"`. ## List Behavior `PageNumber` defaults to `1`; `PageSize` defaults to `10` and accepts up to `100`. `SortBy` accepts `CreateTime` or `UpdateTime`; `SortOrder` accepts `Desc` or `Asc`. List responses return: ```json { "ResponseMetadata": { "RequestId": "req-id", "Action": "ListAssets", "Version": "2024-01-01", "Service": "ark", "Region": "cn-beijing" }, "Result": { "Items": [], "TotalCount": 0, "PageNumber": 1, "PageSize": 10 } } ``` Asset status values are `Active`, `Processing`, and `Failed`. `GetAsset` and `ListAssets` return a temporary `URL` valid for 12 hours; do not store it as the permanent asset identity. Store `Id` instead. ## Real-Person Materials Before importing an asset for a real person: 1. Call [Create Visual Validation Session](/api-reference/video/create-visual-validation-session). 2. Complete the H5 verification flow. 3. Call [Get Visual Validation Result](/api-reference/video/get-visual-validation-result) until it returns `Result.GroupId`. 4. Call `CreateAsset` with that verified group ID. Uploads to a `LivenessFace` group are checked against the verified face. Active verified groups can also have stricter deletion rules than ordinary AIGC groups. ## Errors Errors keep the Volcengine response envelope and place the code and message under `ResponseMetadata.Error`. ```json { "ResponseMetadata": { "RequestId": "req-id", "Action": "CreateAsset", "Version": "2024-01-01", "Service": "ark", "Region": "cn-beijing", "Error": { "Code": "InvalidParameter", "Message": "GroupId is required" } } } ``` Common codes include `InvalidCredential`, `InvalidVersion`, `InvalidParameter`, `NotFound`, `Conflict`, `OperationDenied`, `MethodNotAllowed`, and `InternalError`. ## REST Alternative TokenLab's `/v1/videos/assets*` REST endpoints remain available. New TokenLab-native integrations may prefer their snake_case bodies; existing Volcengine clients can use the Actions on this page without reshaping requests. See [Seedance Materials and Real-Person Verification](/guides/seedance-materials) for the complete asset lifecycle. --- ### Create Material Asset Group > Creates a Seedance virtual-avatar material group. URL: https://tokenlab.sh/docs/en/api-reference/video/create-material-asset-group Create a `library_type: "aigc_avatar"` material group. Use this endpoint for virtual avatars, products, styles, and other reusable references that do not require real-person verification. New real-person material groups are created through [visual validation](/api-reference/video/create-visual-validation-session) and cannot be created directly with this endpoint. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl https://api.tokenlab.sh/v1/videos/assets/groups \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"library_type":"aigc_avatar","group_name":"Product references","description":"Reusable product shots","project_name":"default"}' ``` ## Example Response ```json { "id": "group-20260720123456-vrt01", "object": "seedance.material_asset_group", "project_name": "default", "library_type": "aigc_avatar", "group_name": "Product references", "description": "Reusable product shots", "status": "ACTIVE", "metadata": null, "created_at": 1782998400000 } ``` --- ### List Material Asset Groups > Lists Seedance material groups owned by the current organization. URL: https://tokenlab.sh/docs/en/api-reference/video/list-material-asset-groups List material groups owned by the authenticated organization. Filter by `library_type` and `status` to separate virtual-avatar groups from real-person groups. Use a returned `id` when uploading materials. Do not pass a `group_id` as `material_asset_id` during video generation; generation requires a material asset `id`. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl "https://api.tokenlab.sh/v1/videos/assets/groups?library_type=aigc_avatar&page_size=20&project_name=default" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ## Example Response ```json { "groups": [ { "id": "group-20260720123456-vrt01", "object": "seedance.material_asset_group", "project_name": "default", "library_type": "aigc_avatar", "group_name": "Product references", "status": "ACTIVE", "created_at": 1782998400000 } ], "next_page_token": null } ``` --- ### Get Material Asset Group > Retrieves one Seedance material group by TokenLab group ID. URL: https://tokenlab.sh/docs/en/api-reference/video/get-material-asset-group Retrieve one material group. The `library_type` tells you whether the group is a virtual-avatar group or a real-person group. Use this endpoint before uploading if you need to confirm that the group still exists and has `status: "ACTIVE"`. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl "https://api.tokenlab.sh/v1/videos/assets/groups/group-20260720123456-vrt01?project_name=default" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ## Example Response ```json { "id": "group-20260720123456-vrt01", "object": "seedance.material_asset_group", "project_name": "default", "library_type": "aigc_avatar", "group_name": "Product references", "description": "Reusable product shots", "status": "ACTIVE", "metadata": null, "created_at": 1782998400000 } ``` --- ### Update Material Asset Group > Updates a Seedance material group owned by the current organization. URL: https://tokenlab.sh/docs/en/api-reference/video/update-material-asset-group Update the display name, description, or metadata for a material group. For managed real-person groups, the verified person mapping remains unchanged. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl https://api.tokenlab.sh/v1/videos/assets/groups/group-20260720123456-vrt01 \ -X PUT \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"group_name":"Updated references","description":"Images approved for summer videos","project_name":"default"}' ``` ## Example Response ```json { "id": "group-20260720123456-vrt01", "object": "seedance.material_asset_group", "project_name": "default", "library_type": "aigc_avatar", "group_name": "Updated references", "description": "Images approved for summer videos", "status": "ACTIVE", "metadata": null, "created_at": 1782998400000 } ``` --- ### Delete Material Asset Group > Deletes a Seedance material group owned by the current organization. URL: https://tokenlab.sh/docs/en/api-reference/video/delete-material-asset-group Delete a material group and the TokenLab material assets inside it. This cannot be undone. If the TokenLab material library cannot complete the deletion because the current authorization state does not allow it, TokenLab returns a neutral material-library error. After deletion, the group and its assets can no longer be used in video generation. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl "https://api.tokenlab.sh/v1/videos/assets/groups/group-20260720123456-vrt01?project_name=default" \ -X DELETE \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ## Example Response ```json { "deleted": true, "id": "group-20260720123456-vrt01" } ``` --- ### Create Material Asset > Uploads a Seedance material asset into a TokenLab material group. URL: https://tokenlab.sh/docs/en/api-reference/video/create-material-asset Import one publicly reachable source URL into a Seedance material group. TokenLab stores the material for later use; use the returned material asset `id` for generation, not the source URL. For `library_type: "aigc_avatar"`, `group_id` is optional; TokenLab uses or creates the organization default virtual-avatar group. For `library_type: "liveness_face"`, `group_id` is required and must be the TokenLab group returned by [Get Visual Validation Result](/api-reference/video/get-visual-validation-result). | Type | Supported input | |------|-----------------| | Image | `jpeg`, `jpg`, `png`, `webp`, `bmp`, `tiff`, `gif`, `heic`, `heif`; ≤30 MiB; width and height `[300, 6000]` px; aspect ratio `[0.4, 2.5]`. | | Video | `mp4`, `mov`; ≤200 MiB. | | Audio | `aac`, `wav`, `mp3`; ≤15 MiB. | TokenLab checks the source URL, media type, file size, and image dimensions when readable. Import acceptance does not mean the asset is ready: wait for `ACTIVE` and inspect `error_message` if processing fails. Other media and face requirements are evaluated during processing. Creation is asynchronous. Poll [Get Material Asset](/api-reference/video/get-material-asset) until `status` is `ACTIVE`. For real-person groups, one group maps to one verified person; uploads with multiple faces or a face that does not match the verified person can fail. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl https://api.tokenlab.sh/v1/videos/assets \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"library_type":"aigc_avatar","group_id":"group-20260720123456-vrt01","asset_url":"https://example.com/reference.png","asset_type":"Image","asset_name":"Front view","project_name":"default"}' ``` ## Example Response ```json { "id": "asset-20260720123456-qn7wr", "object": "seedance.material_asset", "project_name": "default", "group_id": "group-20260720123456-vrt01", "library_type": "aigc_avatar", "asset_name": "Front view", "asset_type": "Image", "status": "PROCESSING", "ready": false, "error_message": null, "metadata": null, "created_at": 1782998400000 } ``` --- ### List Material Assets > List Seedance materials in the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/video/list-material-assets List materials in the current Workspace. Filter by `library_type`, `group_id`, or `status`. Use only assets whose `status` is `ACTIVE`. Use the returned TokenLab material asset `id` when creating videos. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl "https://api.tokenlab.sh/v1/videos/assets?library_type=aigc_avatar&group_id=group-20260720123456-vrt01&page_size=20&project_name=default" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ## Example Response ```json { "assets": [ { "id": "asset-20260720123456-qn7wr", "object": "seedance.material_asset", "project_name": "default", "group_id": "group-20260720123456-vrt01", "library_type": "aigc_avatar", "asset_name": "Front view", "asset_type": "Image", "status": "ACTIVE", "ready": true, "created_at": 1782998400000 } ], "next_page_token": null } ``` --- ### Get Material Asset > Retrieves one Seedance material asset by TokenLab material asset ID. URL: https://tokenlab.sh/docs/en/api-reference/video/get-material-asset Retrieve one material asset and inspect its processing status. The asset can be used for video generation only when `ready` is `true` and `status` is `ACTIVE`. If video generation returns `Seedance material asset not found or not accessible`, verify that you passed this material asset `id`, not the material `group_id`. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl "https://api.tokenlab.sh/v1/videos/assets/asset-20260720123456-qn7wr?project_name=default" \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ## Example Response ```json { "id": "asset-20260720123456-qn7wr", "object": "seedance.material_asset", "project_name": "default", "group_id": "group-20260720123456-vrt01", "library_type": "aigc_avatar", "asset_name": "Front view", "asset_type": "Image", "status": "ACTIVE", "ready": true, "error_message": null, "metadata": null, "created_at": 1782998400000 } ``` --- ### Update Material Asset > Updates a Seedance material asset owned by the current organization. URL: https://tokenlab.sh/docs/en/api-reference/video/update-material-asset Update the material name or metadata. The source URL and asset type are fixed after creation; create a new asset when the source file changes. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl https://api.tokenlab.sh/v1/videos/assets/asset-20260720123456-qn7wr \ -X PUT \ -H "Authorization: Bearer $TOKENLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"asset_name":"Front view approved","metadata":{"approved_by":"creative-team"},"project_name":"default"}' ``` ## Example Response ```json { "id": "asset-20260720123456-qn7wr", "object": "seedance.material_asset", "project_name": "default", "group_id": "group-20260720123456-vrt01", "library_type": "aigc_avatar", "asset_name": "Front view approved", "asset_type": "Image", "status": "ACTIVE", "ready": true, "error_message": null, "metadata": {"approved_by": "creative-team"}, "created_at": 1782998400000 } ``` --- ### Delete Material Asset > Deletes a Seedance material asset owned by the current organization. URL: https://tokenlab.sh/docs/en/api-reference/video/delete-material-asset Delete one material asset. This does not delete the containing material group. After deletion, the asset ID can no longer be used as `material_asset_id`. `project_name` isolates resources within the authenticated organization. It defaults to `default`; use the same case-sensitive value for related groups and assets. Send it in the JSON body for POST/PUT and in the query string for GET/DELETE. ## Example Request ```bash curl "https://api.tokenlab.sh/v1/videos/assets/asset-20260720123456-qn7wr?project_name=default" \ -X DELETE \ -H "Authorization: Bearer $TOKENLAB_API_KEY" ``` ## Example Response ```json { "deleted": true, "id": "asset-20260720123456-qn7wr" } ``` --- ## Worlds ### Prepare Media Asset Upload > Creates a World Labs media asset upload target URL: https://tokenlab.sh/docs/en/api-reference/worlds/prepare-media-asset-upload Create a World Labs media asset record and receive an upload URL for an image or video. Upload the binary file to `upload_info.upload_url`, then reference the returned `media_asset.media_asset_id` from `world_prompt` with `source: "media_asset"`. The asset is available only in the Workspace that created it. ## Request Body #### file_name Original file name, up to 64 characters. #### kind Media type: `image` or `video`. #### extension Optional extension such as `png`, `jpg`, or `mp4`. #### metadata Custom metadata stored with the asset. ## Response #### media_asset Prepared World Labs media asset metadata. #### upload_info.upload_url Signed URL for uploading the file. #### upload_info.upload_method HTTP method to use when uploading the binary file to `upload_info.upload_url`, when returned. #### upload_info.required_headers Headers to include with the binary upload, when returned. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/worlds/media-assets/prepare-upload" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "file_name": "reference.png", "kind": "image", "extension": "png" }' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/worlds/media-assets/prepare-upload", headers={"Authorization": "Bearer sk-your-api-key"}, json={"file_name": "reference.png", "kind": "image", "extension": "png"}, ) print(response.json()["media_asset"]["media_asset_id"]) ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/worlds/media-assets/prepare-upload', { method: 'POST', headers: { Authorization: 'Bearer sk-your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ file_name: 'reference.png', kind: 'image', extension: 'png' }) }); const prepared = await response.json(); console.log(prepared.media_asset.media_asset_id, prepared.upload_info.upload_url); ``` --- ### Get Media Asset > Retrieves a World Labs media asset prepared through TokenLab URL: https://tokenlab.sh/docs/en/api-reference/worlds/get-media-asset Retrieve a World asset's name, type, and metadata. Only the Workspace that created it can access it. ## Path Parameters #### id World Labs media asset ID. ## Response #### media_asset_id World Labs media asset ID. #### file_name Original file name, up to 64 characters. #### kind Media type: `image` or `video`. #### metadata Custom metadata stored with the asset. ```bash cURL curl "https://api.tokenlab.sh/v1/worlds/media-assets/media_asset_123" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests response = requests.get( "https://api.tokenlab.sh/v1/worlds/media-assets/media_asset_123", headers={"Authorization": "Bearer sk-your-api-key"}, ) print(response.json()) ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/worlds/media-assets/media_asset_123', { headers: { Authorization: 'Bearer sk-your-api-key' } }); console.log(await response.json()); ``` --- ### Create World > Creates a World Labs Marble world generation task URL: https://tokenlab.sh/docs/en/api-reference/worlds/create-world Generate explorable 3D worlds with World Labs Marble. This is an asynchronous API: create responses return a task identity and a `poll_url` for status checks. Choose a currently available Marble model using the [Models API](/api-reference/models/list-models). ## Request Body #### model Choose a currently available Marble model using the [Models API](/api-reference/models/list-models). #### prompt Text prompt for text-only generation or as guidance with image/video inputs. #### world_prompt Native World Labs `world_prompt` object for advanced callers. Supported prompt types are `text`, `image`, `multi-image`, and `video`. #### image Base64 or data URL image prompt. #### image_url Image URL prompt. #### images Multiple image prompts for shortcut multi-image generation. Provide up to 4 images. For native World Labs reconstruction mode, pass `world_prompt.type="multi-image"` with `reconstruct_images: true` and up to 8 images. #### video_url Video URL prompt. #### is_pano For image inputs, set `true` for an existing panorama, `false` for a normal single image, or `auto`. #### seed Optional seed from 0 through 4294967295. #### display_name Optional display name for the generated world, up to 64 characters. #### tags Optional World Labs tags. Provide up to 10 tags, each up to 32 characters. #### permission Optional visibility and access settings: `public`, `allow_id_access`, `allowed_readers`, and `allowed_writers`. `public: true` requests a publicly visible world. ## Response #### id Public task ID for polling. #### task_id Async task identifier alias. #### operation_id World Labs operation ID. #### poll_url Preferred polling URL for this task. #### status Task status: `pending`, `processing`, `completed`, or `failed`. #### world_marble_url URL for the generated Marble world when completed. #### glb_url Collider mesh GLB URL when available. #### pano_url Panorama image URL when available. ## Pricing See the [Pricing API](/api-reference/pricing/get-pricing) for current rates. The final billed amount is shown in your usage records; a returned `cost.total_credits` value is generation usage, not a currency amount. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/worlds/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "marble-1.1", "prompt": "A quiet coastal town at sunset with narrow alleys and warm lights" }' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/worlds/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "marble-1.1", "prompt": "A quiet coastal town at sunset with narrow alleys and warm lights", }, ) task = response.json() print(task["id"], task["poll_url"]) ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/worlds/generations', { method: 'POST', headers: { Authorization: 'Bearer sk-your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'marble-1.1', prompt: 'A quiet coastal town at sunset with narrow alleys and warm lights' }) }); const task = await response.json(); console.log(task.id, task.poll_url); ``` --- ### Get World Status > Retrieves the status and result of a World Labs Marble world generation task URL: https://tokenlab.sh/docs/en/api-reference/worlds/get-world-status Poll this endpoint to check the status of a World Labs Marble generation task. If the create response included `poll_url`, prefer calling that exact URL. > Info: An expired or unavailable public task can return `404 async_task_not_found`. If task ownership cannot be established for the current API key, the response is `403 task_not_owned`. ## Path Parameters #### id World generation task ID. ## Response Only `id` and `status` are required in a successful status response. Task aliases, operation IDs, world assets, captions, cost, and the string `error` are returned when available. A completed task does not guarantee that every asset URL is present. #### id Public task ID. #### task_id Async task identifier alias. #### operation_id World Labs operation ID. #### status Task status: `pending`, `processing`, `completed`, `failed`, or `timeout`. #### world_id World Labs world ID when completed. #### world_marble_url URL for the generated Marble world when completed. #### model_url Alias for the primary generated world/model URL. #### glb_url Collider mesh GLB URL when available. #### pano_url Panorama image URL when available. #### thumbnail_url Thumbnail image URL when available. #### caption Caption for the generated world. #### cost Generation cost details when available, including `total_credits`. Check your usage records for the billed amount. #### error Error message when the task failed. ```bash cURL curl --fail-with-body "https://api.tokenlab.sh/v1/worlds/generations/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests import time task_id = "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" while True: response = requests.get( f"https://api.tokenlab.sh/v1/worlds/generations/{task_id}", headers={"Authorization": "Bearer sk-your-api-key"}, ) response.raise_for_status() result = response.json() if result["status"] in ["completed", "failed", "timeout"]: print(result) break time.sleep(10) ``` ```javascript JavaScript const taskId = 'ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; const response = await fetch(`https://api.tokenlab.sh/v1/worlds/generations/${taskId}`, { headers: { Authorization: 'Bearer sk-your-api-key' } }); if (!response.ok) throw new Error(await response.text()); const result = await response.json(); console.log(result); ``` --- ### List Worlds > Lists completed World Labs Marble worlds created through TokenLab URL: https://tokenlab.sh/docs/en/api-reference/worlds/list-worlds List completed Marble Worlds in the current Workspace. Use `/v1/worlds/generations/{id}` for pending or failed generation tasks. This list returns completed worlds that TokenLab has indexed after generation. ## Request Body #### page_size Number of worlds to return, from 1 to 100. #### page_token Pagination token from the previous response. #### status Optional filter: `SUCCEEDED`, `PENDING`, `RUNNING`, or `FAILED`. This endpoint lists completed worlds; the other three values return an empty list. Query generation tasks for their progress. #### model Optional Marble model filter. #### tags Optional tag filter. #### is_public Optional visibility filter. #### created_after Only return worlds created after this timestamp. #### created_before Only return worlds created before this timestamp. #### sort_by Sort by `created_at` or `updated_at`. #### sort_order Sort direction: `asc` or `desc`. ## Response #### worlds Array of world records. #### next_page_token Token for the next page, or `null`. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/worlds/list" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{"page_size": 20, "status": "SUCCEEDED", "model": "marble-1.1"}' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/worlds/list", headers={"Authorization": "Bearer sk-your-api-key"}, json={"page_size": 20, "status": "SUCCEEDED", "model": "marble-1.1"}, ) print(response.json()["worlds"]) ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/worlds/list', { method: 'POST', headers: { Authorization: 'Bearer sk-your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ page_size: 20, status: 'SUCCEEDED', model: 'marble-1.1' }) }); console.log((await response.json()).worlds); ``` --- ### Get World > Retrieves a completed World Labs Marble world URL: https://tokenlab.sh/docs/en/api-reference/worlds/get-world Retrieve a completed Marble World. It must belong to the Workspace associated with the API key. ## Path Parameters #### id World Labs world ID. ## Response #### world_id World Labs world ID. #### world_marble_url World Labs Marble URL. #### assets Generated world assets when available. ```bash cURL curl "https://api.tokenlab.sh/v1/worlds/world_123" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests response = requests.get( "https://api.tokenlab.sh/v1/worlds/world_123", headers={"Authorization": "Bearer sk-your-api-key"}, ) print(response.json()["world_id"]) ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/worlds/world_123', { headers: { Authorization: 'Bearer sk-your-api-key' } }); console.log(await response.json()); ``` --- ### Delete World > Deletes a World Labs Marble world URL: https://tokenlab.sh/docs/en/api-reference/worlds/delete-world Permanently delete a Marble World created through TokenLab. It cannot be restored or accessed through TokenLab afterward. ## Path Parameters #### id World Labs world ID. ## Response #### deleted Whether the deletion was accepted. #### world_id World Labs world ID. ```bash cURL curl -X DELETE "https://api.tokenlab.sh/v1/worlds/world_123" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests response = requests.delete( "https://api.tokenlab.sh/v1/worlds/world_123", headers={"Authorization": "Bearer sk-your-api-key"}, ) print(response.json()) ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/worlds/world_123', { method: 'DELETE', headers: { Authorization: 'Bearer sk-your-api-key' } }); console.log(await response.json()); ``` --- ## 3D ### Create 3D Model > Creates a 3D model generation task URL: https://tokenlab.sh/docs/en/api-reference/3d/create-3d > Note: Use `GET /v1/models?recommended_for=3d` to get current 3D models. Generate 3D models from text or images using Tripo3D and other providers. This is an asynchronous API. Create responses return a task identity, and may also return a preferred `poll_url` for status checks. Public 3D operations are inferred from the request shape: prompt-only requests resolve to `text-to-3d`, and requests with `image` or `image_url` resolve to `image-to-3d`. This endpoint does not expose an `operation` request field; do not send the legacy `3d-generation` value. ## Request Body Before sending optional fields, check `GET /v1/models/{model}` for the model’s accepted parameters. The example uses only `model` and `prompt`. #### model Model to use, for example `tripo-h3.1` or `tripo-p1.0`. Query `GET /v1/models?recommended_for=3d` before relying on a specific input type or output format. #### prompt Text description of the 3D model to generate. #### image Base64-encoded image for image-to-3D generation. #### image_url URL of image for image-to-3D generation. #### format Output format; support and allowed values depend on the selected model. #### quality Quality setting; support and allowed values depend on the selected model. #### style Style preset for the model. #### seed Integer seed, when supported by the selected model. #### user A unique identifier for the end-user. ## Response #### id Task ID for polling status. #### task_id Async task identifier alias when returned by the adapter. #### poll_url Preferred polling URL for this task when provided. #### status Task status: `pending`, `processing`, `completed`, or `failed`. #### created Unix timestamp of task creation. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/3d/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "tripo-h3.1", "prompt": "A detailed medieval castle with towers" }' ``` ```python Python import requests response = requests.post( "https://api.tokenlab.sh/v1/3d/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "tripo-h3.1", "prompt": "A detailed medieval castle with towers" } ) task_id = response.json()["id"] print(f"Task ID: {task_id}") ``` ```javascript JavaScript const response = await fetch('https://api.tokenlab.sh/v1/3d/generations', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'tripo-h3.1', prompt: 'A detailed medieval castle with towers' }) }); const data = await response.json(); console.log(`Task ID: ${data.id}`); ``` ```go Go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { payload := map[string]interface{}{ "model": "tripo-h3.1", "prompt": "A detailed medieval castle with towers", } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1/3d/generations", bytes.NewBuffer(body)) req.Header.Set("Authorization", "Bearer sk-your-api-key") req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("Task ID: %s\n", result["id"]) } ``` ```php PHP true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer sk-your-api-key' ], CURLOPT_POSTFIELDS => json_encode([ 'model' => 'tripo-h3.1', 'prompt' => 'A detailed medieval castle with towers' ]) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo "Task ID: " . $data['id']; ``` ```json Response { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "pending", "created": 1706000000, "model": "tripo-h3.1" } ``` --- ### Get 3D Model Status > Retrieves the status and result of a 3D generation task URL: https://tokenlab.sh/docs/en/api-reference/3d/get-3d-status Poll this endpoint to check the status of your 3D model generation task. > Info: If the create response includes `poll_url`, call that URL directly. `id` and `task_id` identify the same task; you can also use `/v1/tasks/{id}`. > > An expired or unavailable public task can return `404 async_task_not_found`. If task ownership cannot be established for the current API key, the response is `403 task_not_owned`. ## Path Parameters #### id The 3D generation task ID. ## Response > Note: Fields describing results, errors, timestamps, and the model are returned when available for the task. #### id Task ID. #### task_id Async task identifier alias when provided. #### poll_url Preferred polling URL when the create response supplies one. #### status Task status: `pending`, `processing`, `completed`, or `failed`. #### progress Optional progress value. Returned only when a real progress value is available; use `status` to determine completion. #### model_url URL to download the 3D model file (when completed). #### glb_url URL to download the GLB format model (when available). #### fbx_url URL to download the FBX format model (when available). #### error Error message (when failed). ```bash cURL curl "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests import time task_id = "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" poll_url = f"/v1/tasks/{task_id}" while True: response = requests.get( f"https://api.tokenlab.sh{poll_url}", headers={"Authorization": "Bearer sk-your-api-key"}, timeout=30 ) response.raise_for_status() result = response.json() if result["status"] == "completed": print(f"3D Model URL: {result['model_url']}") break elif result["status"] == "failed": print(f"Error: {result['error']}") break time.sleep(5) # Poll every 5 seconds ``` ```javascript JavaScript const taskId = 'ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; const pollUrl = `/v1/tasks/${taskId}`; async function pollFor3DModel() { while (true) { const response = await fetch( `https://api.tokenlab.sh${pollUrl}`, { headers: { 'Authorization': 'Bearer sk-your-api-key' } } ); if (!response.ok) throw new Error(`HTTP ${response.status}`); const result = await response.json(); if (result.status === 'completed') { console.log(`3D Model URL: ${result.model_url}`); return result.model_url; } else if (result.status === 'failed') { throw new Error(JSON.stringify(result.error)); } await new Promise(r => setTimeout(r, 5000)); } } pollFor3DModel().catch(console.error); ``` ```go Go package main import ( "encoding/json" "fmt" "net/http" "time" ) func main() { taskID := "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" pollURL := fmt.Sprintf("https://api.tokenlab.sh/v1/tasks/%s", taskID) for { req, _ := http.NewRequest("GET", pollURL, nil) req.Header.Set("Authorization", "Bearer sk-your-api-key") client := &http.Client{} resp, err := client.Do(req) if err != nil { panic(err) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { resp.Body.Close() panic(fmt.Sprintf("HTTP %d", resp.StatusCode)) } var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) resp.Body.Close() if err != nil { panic(err) } if result["status"] == "completed" { fmt.Printf("3D Model URL: %s\n", result["model_url"]) break } else if result["status"] == "failed" { fmt.Printf("Error: %v\n", result["error"]) break } time.Sleep(5 * time.Second) } } ``` ```php PHP true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk-your-api-key' ] ]); $response = curl_exec($ch); $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($response === false || $httpStatus < 200 || $httpStatus >= 300) { throw new RuntimeException("HTTP {$httpStatus}: {$curlError}"); } $result = json_decode($response, true); if ($result['status'] === 'completed') { echo "3D Model URL: {$result['model_url']}\n"; break; } elseif ($result['status'] === 'failed') { echo "Error: " . json_encode($result['error']) . "\n"; break; } sleep(5); } ``` ```json Response (Completed) { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "completed", "model_url": "https://api.tokenlab.sh/v1/media-results/5e2a7c90-1d4b-4f8e-a3c6-8b9d0e1f2a47.glb?token=…", "glb_url": "https://api.tokenlab.sh/v1/media-results/5e2a7c90-1d4b-4f8e-a3c6-8b9d0e1f2a47.glb?token=…", "fbx_url": "https://api.tokenlab.sh/v1/media-results/8d1b3f6a-9c2e-4a5d-b7e0-2f4c6a8e0d15.fbx?token=…", "media_retention": { "days": 30, "items": [ { "url": "https://api.tokenlab.sh/v1/media-results/5e2a7c90-1d4b-4f8e-a3c6-8b9d0e1f2a47.glb?token=…", "kind": "3d", "content_type": "model/gltf-binary", "status": "saved", "expires_at": "2024-02-22T08:54:20.000Z" }, { "url": "https://api.tokenlab.sh/v1/media-results/8d1b3f6a-9c2e-4a5d-b7e0-2f4c6a8e0d15.fbx?token=…", "kind": "3d", "content_type": "application/octet-stream", "status": "saved", "expires_at": "2024-02-22T08:54:20.000Z" } ] } } ``` --- ## Async Jobs ### Get Task Status > Retrieve the status and result of any async task using the poll_url returned by create endpoints URL: https://tokenlab.sh/docs/en/api-reference/tasks/get-task-status ## Overview Use this endpoint for unified async polling across task types such as video, image, music, and 3D generation. If a create response includes `poll_url`, call that exact path. Some image models may return task-based responses under either an image-specific status path or `/v1/tasks/{id}`. ## Path Parameters #### id The task ID returned by the create request. ## Response > Note: Fields describing results, errors, timestamps, and the model are returned when available for the task. #### id Canonical async task identifier. #### task_id Async task identifier alias. #### poll_url Preferred polling URL when the create response supplies one. #### status Task status such as `pending`, `processing`, `completed`, or `failed`. Cancelled tasks are represented as `failed` with `cancelled: true`. #### cancelled `true` when a queued async task was cancelled before execution. #### cancellation_status Cancellation marker. Present as `cancelled` when cancellation succeeds. #### data For completed image tasks, generated image results are returned here. Image tasks return URLs in `data[].url`. #### progress Optional progress value. Returned only when a real progress value is available; use `status` to determine completion. #### video_url Result asset URL when the task completes and produces a video. #### video Single video payload with `url`, `duration`, `width`, and `height` when available. #### videos Multiple video payloads when more than one output is available. #### error Error message when the task fails. #### created Creation timestamp when available. #### updated Last update timestamp when available. #### model Model used by the task when available. ## Error Behavior An expired or unavailable public task can return `404 async_task_not_found`. If task ownership cannot be established for the current API key, the response is `403 task_not_owned`. ```json { "error": { "message": "Task not found or no longer available.", "type": "invalid_request_error", "code": "async_task_not_found" } } ``` ## Examples ```bash cURL curl "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` ```python Python import requests poll_url = "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" response = requests.get( f"https://api.tokenlab.sh{poll_url}", headers={"Authorization": "Bearer sk-your-api-key"}, ) print(response.json()) ``` ```javascript JavaScript const pollUrl = '/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; const response = await fetch(`https://api.tokenlab.sh${pollUrl}`, { headers: { Authorization: 'Bearer sk-your-api-key' }, }); console.log(await response.json()); ``` --- ### Cancel Task > Cancel a queued async task when the provider and model support cancellation URL: https://tokenlab.sh/docs/en/api-reference/tasks/cancel-task ## Overview Use `DELETE /v1/tasks/{id}` to cancel an asynchronous task that has not started running yet. Use the same task ID from the create response or the returned `poll_url`. ## Supported Tasks Cancellation is available for queued Seedance tasks using `seedance-1.5-pro`, `seedance-2.0`, `seedance-2.0-fast`, or `seedance-2.5` when that task supports cancellation. A task already running or in a terminal state cannot be cancelled. Status remains queryable while its public task record is retained. ## Path Parameters #### id The async task ID returned by the create request or embedded in `poll_url`. ## Response #### id Canonical async task identifier. #### task_id Async task identifier alias. #### poll_url Preferred polling URL for the task. It remains valid after cancellation. #### status Task status. A successfully cancelled task is returned as `failed` with `cancelled: true` for compatibility with existing status values. #### cancelled `true` when the task was cancelled before execution. #### cancellation_status Cancellation marker. The value is `cancelled` when cancellation succeeds. #### error Cancellation reason or task failure message. ## Error Behavior `400 unsupported_task_cancel` means the task type, provider, or model does not support cancellation. `409 task_not_cancellable` means the task is already processing, terminal, or otherwise no longer cancellable. `403` means the API key cannot access the task. ## Example ```bash cURL curl -X DELETE "https://api.tokenlab.sh/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ -H "Authorization: Bearer sk-your-api-key" ``` ```json Response { "id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "task_id": "ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "poll_url": "/v1/tasks/ldtask_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "status": "failed", "cancelled": true, "cancellation_status": "cancelled", "error": "Task cancelled before execution" } ``` --- ## Gemini Native ### List Models > List available models using Google Gemini API format URL: https://tokenlab.sh/docs/en/api-reference/gemini/list-models Returns a list of available models in Google Gemini API format. ## Query Parameters #### pageSize Maximum number of models to return. Default: `50`, maximum: `1000`. #### pageToken Continuation token returned by a previous page. Pass it back unchanged to fetch the next slice. ## Authentication Authentication is not required for the public model catalog read endpoints. Gemini-style auth hints such as `?key=` or `x-goog-api-key` do not change the response contract here. ## Response #### models Array of model objects. #### nextPageToken Returned when more models are available. Pass this value as `pageToken` to continue pagination. ## Errors - Invalid `pageToken` returns `400 INVALID_ARGUMENT`. - Unexpected list failures return `500 INTERNAL`. ```bash cURL curl "https://api.tokenlab.sh/v1beta/models?pageSize=5" \ -H "x-goog-api-key: sk-your-api-key" ``` ```python Python (google-genai) from google import genai client = genai.Client( api_key="sk-your-api-key", http_options={"base_url": "https://api.tokenlab.sh", "api_version": "v1beta"}, ) for model in client.models.list(): print(model.name) ``` ```javascript JavaScript const response = await fetch( "https://api.tokenlab.sh/v1beta/models?pageSize=5", { headers: { "x-goog-api-key": "sk-your-api-key" } } ); const { models } = await response.json(); models.forEach(m => console.log(m.name)); ``` ```json Response { "models": [ { "name": "models/gemini-2.5-pro", "version": "gemini-2.5-pro", "displayName": "gemini-2.5-pro", "description": "gemini-2.5-pro model available via TokenLab", "inputTokenLimit": 1048576, "outputTokenLimit": 65536, "supportedGenerationMethods": [ "generateContent", "countTokens" ], "baseModelId": "gemini-2.5-pro" }, { "name": "models/gemini-3.5-flash", "version": "gemini-3.5-flash", "displayName": "gemini-3.5-flash", "description": "gemini-3.5-flash model available via TokenLab", "inputTokenLimit": 1048576, "outputTokenLimit": 65536, "supportedGenerationMethods": [ "generateContent", "countTokens" ], "baseModelId": "gemini-3.5-flash" } ] } ``` --- ### Get Model > Get model metadata using Google Gemini API format URL: https://tokenlab.sh/docs/en/api-reference/gemini/get-model Returns metadata for a specific model in Google Gemini API format. ## Path Parameters #### model Model name (e.g., `gemini-2.5-pro`, `gemini-3.5-flash`). Aliases are also accepted. ## Authentication Authentication is not required for the public model metadata read endpoint. ## Response #### name Model resource name in `models/{model}` format. #### displayName Human-readable model name. #### inputTokenLimit Maximum input tokens (context window). #### outputTokenLimit Maximum output tokens. #### supportedGenerationMethods List of supported generation methods (e.g., `generateContent`, `countTokens`, `embedContent`). #### version Public version string when available. #### description Human-readable model description when available. #### temperature Default temperature when the model provides one. #### topP Default top-p value when available. #### topK Default top-k value when available. #### maxTemperature Maximum supported temperature when available. ## Errors - Invalid GETs that accidentally include `:generateContent` or `:streamGenerateContent` return `405 METHOD_NOT_ALLOWED`. - Missing models return `404 NOT_FOUND`. ```bash cURL curl "https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro" ``` ```python Python (google-genai) from google import genai client = genai.Client( api_key="sk-your-api-key", http_options={"base_url": "https://api.tokenlab.sh", "api_version": "v1beta"}, ) model = client.models.get(model="gemini-2.5-pro") print(f"Name: {model.name}") print(f"Input limit: {model.input_token_limit}") print(f"Output limit: {model.output_token_limit}") ``` ```json Response { "name": "models/gemini-2.5-pro", "version": "gemini-2.5-pro", "displayName": "gemini-2.5-pro", "description": "gemini-2.5-pro model available via TokenLab", "inputTokenLimit": 1048576, "outputTokenLimit": 65536, "supportedGenerationMethods": [ "generateContent", "countTokens" ], "baseModelId": "gemini-2.5-pro" } ``` --- ### Generate Content > Generates content using Google Gemini API format URL: https://tokenlab.sh/docs/en/api-reference/gemini/generate-content Use Gemini request and response formats with a model returned by the Gemini Models API. ProtoJSON lowerCamelCase and the corresponding proto snake_case field names are accepted. Supported optional fields and values depend on the selected model. ## Path Parameters #### model Model name (e.g., `gemini-2.5-pro`, `gemini-3.5-flash`). ## Query Parameters #### key API key (alternative to header authentication). ## Authentication Gemini endpoints support multiple authentication methods: - `?key=YOUR_API_KEY` query parameter - `x-goog-api-key: YOUR_API_KEY` header - `Authorization: Bearer YOUR_API_KEY` header ## Request Body #### contents Conversation contents with `parts` and an optional `role`. Media parts use `inlineData` / `inline_data` or `fileData` / `file_data`. Files and Cache IDs created by TokenLab can only be used in their owning Workspace. #### systemInstruction System instruction for the model. #### generationConfig Generation settings such as `temperature`, `topP`, `topK`, `maxOutputTokens`, `stopSequences`, `candidateCount`, `responseModalities`, `responseMimeType`, `responseSchema`, and `thinkingConfig`. Corresponding snake_case names are also accepted. Supported values and combinations depend on the model. #### safetySettings Safety filter settings. Every lowerCamelCase field shown above may also use its official proto `snake_case` spelling, for example `system_instruction`, `generation_config`, `safety_settings`, `tool_config`, `cached_content`, `file_data.file_uri`, and `inline_data.mime_type`. ## Supported Methods The Gemini-compatible API includes model list/get, `generateContent`, `streamGenerateContent`, `countTokens`, `embedContent`, and `batchEmbedContents`. Gemini Interactions and Live are not available. ## Response #### candidates Generated content candidates. #### usageMetadata Token usage information. ```bash cURL curl -X POST "https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro:generateContent?key=sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [ { "parts": [{"text": "Hello, Gemini!"}] } ], "generationConfig": { "temperature": 0.7, "maxOutputTokens": 1024 } }' ``` ```python Python (google-genai) from google import genai client = genai.Client( api_key="sk-your-api-key", http_options={"base_url": "https://api.tokenlab.sh", "api_version": "v1beta"}, ) response = client.models.generate_content( model="gemini-2.5-pro", contents="Hello, Gemini!" ) print(response.text) ``` ```javascript JavaScript (@google/genai) import { GoogleGenAI } from "@google/genai"; const ai = new GoogleGenAI({ apiKey: "sk-your-api-key", httpOptions: { baseUrl: "https://api.tokenlab.sh", apiVersion: "v1beta" }, }); const response = await ai.models.generateContent({ model: "gemini-2.5-pro", contents: "Hello, Gemini!", }); console.log(response.text); ``` ```go Go package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) func main() { payload := map[string]interface{}{ "contents": []map[string]interface{}{ { "parts": []map[string]string{ {"text": "Hello, Gemini!"}, }, }, }, "generationConfig": map[string]interface{}{ "temperature": 0.7, "maxOutputTokens": 1024, }, } jsonData, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro:generateContent?key=sk-your-api-key", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```php PHP [ [ 'parts' => [ ['text' => 'Hello, Gemini!'] ] ] ], 'generationConfig' => [ 'temperature' => 0.7, 'maxOutputTokens' => 1024 ] ]; $ch = curl_init('https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro:generateContent?key=sk-your-api-key'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json' ], CURLOPT_POSTFIELDS => json_encode($payload) ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['candidates'][0]['content']['parts'][0]['text']; ``` ## Vision Input Example For Gemini multimodal requests, place media inside `contents[].parts[]` using either inline bytes or URL-based file references. Supported media categories in the public Gemini contract: - image - audio - video For inline media, use either `inlineData` or `inline_data` and pass Base64-encoded file bytes. For URL-based media, use either `fileData` or `file_data` and pass a public `https` URL. ## Video Input Example ```json { "contents": [ { "role": "user", "parts": [ { "text": "Please describe this video." }, { "fileData": { "mimeType": "video/mp4", "fileUri": "https://example.com/demo.mp4" } } ] } ] } ``` ## Audio Input Example ```json { "contents": [ { "role": "user", "parts": [ { "text": "Please describe this audio." }, { "fileData": { "mimeType": "audio/mpeg", "fileUri": "https://example.com/demo.mp3" } } ] } ] } ``` ## Image Input Example Use inline image bytes: ```json { "contents": [ { "role": "user", "parts": [ { "text": "Please describe this image." }, { "inlineData": { "mimeType": "image/jpeg", "data": "/9j/4AAQSkZJRgABAQ..." } } ] } ] } ``` Use an image URL: ```json { "contents": [ { "role": "user", "parts": [ { "text": "Please describe this image." }, { "fileData": { "mimeType": "image/jpeg", "fileUri": "https://example.com/demo.jpg" } } ] } ] } ``` ## Audio Input Example Use an audio URL: ```json { "contents": [ { "role": "user", "parts": [ { "text": "Transcribe and summarize this audio." }, { "file_data": { "mime_type": "audio/mpeg", "file_uri": "https://example.com/sample.mp3" } } ] } ] } ``` ## Video Input Example Use a video URL: ```json { "contents": [ { "role": "user", "parts": [ { "text": "Describe this video briefly." }, { "fileData": { "mimeType": "video/mp4", "fileUri": "https://example.com/sample.mp4" } } ] } ] } ``` ```json Response { "candidates": [ { "content": { "role": "model", "parts": [ {"text": "Hello! How can I assist you today?"} ] }, "finishReason": "STOP", "safetyRatings": [ {"category": "HARM_CATEGORY_HARASSMENT", "probability": "NEGLIGIBLE"} ] } ], "usageMetadata": { "promptTokenCount": 5, "candidatesTokenCount": 10, "totalTokenCount": 15 } } ``` ## Related interfaces Read the selected model’s current model details before using another interface. Availability and request fields depend on the model. | HTTP | API | | --- | --- | | POST | `/v1beta/models/{model}:countTokens` | | POST | `/v1beta/models/{model}:embedContent` | | POST | `/v1beta/models/{model}:batchEmbedContents` | Token counting requires an API key and is not billed. The returned input count is an estimate for planning; use the final response usage and billing record for the charge. `countTokens`: `contents` / `generateContentRequest` `embedContent`: `content`, `taskType`, `title`, `outputDimensionality` `batchEmbedContents`: `requests` --- ### Stream Generate Content > Streams content generation using Google Gemini API format URL: https://tokenlab.sh/docs/en/api-reference/gemini/stream-generate-content SSE version of Gemini `generateContent`. Events may contain only metadata or intermediate content without `finishReason`. The stream ends at natural EOF and does not append a `[DONE]` marker. ## Path Parameters #### model Model name (e.g., `gemini-2.5-pro`, `gemini-3.5-flash`). ## Query Parameters #### key API key (alternative to header authentication). ## Request Body Same as [Generate Content](/api-reference/gemini/generate-content). This includes multimodal `parts` arrays with structured `inline_data` image parts for vision requests. Support for `generationConfig.candidateCount`, tools, media types, and other optional fields depends on the selected model. ## Response Returns Gemini Server-Sent Events. Metadata-only events, intermediate chunks without `finishReason`, and natural EOF are valid. The stream does not append Chat Completions' `[DONE]` sentinel. ```bash cURL curl -N -X POST "https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro:streamGenerateContent?key=sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [ { "parts": [{"text": "Tell me a story"}] } ] }' ``` ```python Python (google-genai) from google import genai client = genai.Client( api_key="sk-your-api-key", http_options={"base_url": "https://api.tokenlab.sh", "api_version": "v1beta"}, ) for chunk in client.models.generate_content_stream( model="gemini-2.5-pro", contents="Tell me a story" ): if chunk.text: print(chunk.text, end="") ``` ```javascript JavaScript const response = await fetch( 'https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro:streamGenerateContent?key=sk-your-api-key', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: 'Tell me a story' }] }] }) } ); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log(decoder.decode(value, { stream: true })); } ``` ```go Go package main import ( "bufio" "bytes" "encoding/json" "fmt" "net/http" ) func main() { payload := map[string]interface{}{ "contents": []map[string]interface{}{ {"parts": []map[string]string{{"text": "Tell me a story"}}}, }, } jsonData, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro:streamGenerateContent?key=sk-your-api-key", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { fmt.Println(scanner.Text()) } } ``` ```php PHP [ ['parts' => [['text' => 'Tell me a story']]] ] ]; $ch = curl_init('https://api.tokenlab.sh/v1beta/models/gemini-2.5-pro:streamGenerateContent?key=sk-your-api-key'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_WRITEFUNCTION => function($ch, $data) { echo $data; return strlen($data); } ]); curl_exec($ch); curl_close($ch); ``` ## Vision Input Example Streaming vision requests use the same `contents[].parts[]` structure as the non-streaming endpoint. ```json { "contents": [ { "role": "user", "parts": [ { "text": "Please describe this image." }, { "inline_data": { "mime_type": "image/jpeg", "data": "/9j/4AAQSkZJRgABAQ..." } } ] } ] } ``` ```json Stream Chunk { "candidates": [ { "content": { "role": "model", "parts": [ {"text": "Once upon a time"} ] } } ] } ``` --- ## Gemini Files & Cache ### Gemini Files and cached content > Upload Gemini files and manage Workspace-owned files and cached content. URL: https://tokenlab.sh/docs/en/guides/gemini-files-cache Use Gemini Files and cached content with a current TokenLab model that supports the operation. Check the model details for supported methods before sending a request. ## Workspace ownership Files and cached content belong to the Workspace associated with the authenticated request. Use that same Workspace to create, list, read, reference, update, and delete them. A resource name or file URI does not transfer ownership. ## Files Use `register` for a supported source; it does not upload local bytes. For a local file, send its bytes to the upload endpoint. List or read the file and wait until it is ready before using its returned URI in Gemini content. Files can be registered, read, and deleted; these routes do not provide a file update operation. ## Cached content Create cached content for a supported model, then use the returned `name` as `cachedContent` in a later `generateContent` request from the same Workspace. `GET` reads cache metadata, `PATCH` updates its lifetime, and `DELETE` removes it. A deleted or expired cache cannot be used for generation. ## Resumable upload Start a session with `POST /upload/v1beta/files`, `X-Goog-Upload-Protocol: resumable`, and `X-Goog-Upload-Command: start`. Send metadata and read the exact `X-Goog-Upload-URL` response header. POST each byte chunk to that URL with the upload command and acknowledged offset. The URL is a private capability: it authorizes continuation and expires after one hour. Keep it secret; do not publish, log, or share it. Do not follow it as a generic HTTP redirect. A `308` means the chunk was accepted but the upload is still incomplete, so continue at the same URL. A `404` means the session is missing or expired. A `410` means it can no longer continue; start a new session. Treat upload as complete only when the final successful response contains the file resource. ## SDK setup Set `TOKENLAB_API_KEY` in the process environment. Configure the Google Gen AI SDK with the TokenLab host and `v1beta` API version: ```python import os from google import genai client = genai.Client( api_key=os.environ["TOKENLAB_API_KEY"], http_options={"base_url": "https://api.tokenlab.sh", "api_version": "v1beta"}, ) ``` ```javascript import { GoogleGenAI } from "@google/genai"; const client = new GoogleGenAI({ apiKey: process.env.TOKENLAB_API_KEY, httpOptions: { baseUrl: "https://api.tokenlab.sh", apiVersion: "v1beta" }, }); ``` Keep the key and upload URL out of source control, browser storage, and logs. ## Operations | Method | Path | Operation | | --- | --- | --- | | POST | `/v1beta/files:register` | Register a supported source | | POST | `/v1beta/files` | Create file metadata | | GET | `/v1beta/files` | List files | | GET | `/v1beta/files/{fileId}` | Read file metadata | | DELETE | `/v1beta/files/{fileId}` | Delete a file | | POST | `/v1beta/cachedContents` | Create cached content | | GET | `/v1beta/cachedContents` | List cached content | | GET | `/v1beta/cachedContents/{cacheId}` | Read cached content | | PATCH | `/v1beta/cachedContents/{cacheId}` | Update cache lifetime | | DELETE | `/v1beta/cachedContents/{cacheId}` | Delete cached content | | POST | `/upload/v1beta/files` | Upload bytes or start a session | | POST | `/upload/v1beta/files/session/{sessionId}` | Continue a resumable upload | The list endpoints support `pageSize` and `pageToken`. Use the returned cached-content name as `cachedContent` in a later Gemini request. See the [OpenAPI specification](https://tokenlab.sh/docs/openapi.json) and [Generate Content](/api-reference/gemini/generate-content) for all fields. --- ### Register Gemini files > Registers files from a supported source using Gemini JSON. URL: https://tokenlab.sh/docs/en/api-reference/gemini/files-register Registers files from a supported source using Gemini JSON. The service validates the source and request fields. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ### Create Gemini file metadata > Creates a Gemini file resource from JSON metadata. URL: https://tokenlab.sh/docs/en/api-reference/gemini/files-create Creates a Gemini file resource from JSON metadata. Upload file bytes through /upload/v1beta/files. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ### List Gemini files > Lists files belonging to the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/gemini/files-list Lists files belonging to the current Workspace. Use nextPageToken to continue. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ### Get Gemini file > Reads metadata for a Gemini file. URL: https://tokenlab.sh/docs/en/api-reference/gemini/files-get Reads metadata for a Gemini file. Resources can only be read, referenced, changed, or deleted in their owning Workspace. Missing, expired, or inaccessible resources return a Gemini-format error. --- ### Delete Gemini file > Deletes a Gemini file. URL: https://tokenlab.sh/docs/en/api-reference/gemini/files-delete Deletes a Gemini file. Save anything needed before deletion. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ### Upload Gemini file > Uploads bytes or starts a resumable upload. URL: https://tokenlab.sh/docs/en/api-reference/gemini/files-upload Uploads bytes or starts a resumable upload. For resumable start, send metadata and read X-Goog-Upload-URL. Continue only at that returned URL. --- ### Continue Gemini file upload > Sends bytes or commands to the exact URL returned by resumable start. URL: https://tokenlab.sh/docs/en/api-reference/gemini/files-upload-resume Sends bytes or commands to the exact URL returned by resumable start. The session in the URL authorizes this upload; an API key is not required. Keep the URL private. --- ### Create Gemini cached content > Creates cached content for a current TokenLab Gemini model. URL: https://tokenlab.sh/docs/en/api-reference/gemini/cached-contents-create Creates cached content for a current TokenLab Gemini model. Supported fields, resource references, and lifetime depend on the service and model; lowerCamelCase and the corresponding proto snake_case names are accepted. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ### List Gemini cached content > Lists cached content belonging to the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/gemini/cached-contents-list Lists cached content belonging to the current Workspace. Use nextPageToken to continue. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ### Get Gemini cached content > Reads cached content metadata. URL: https://tokenlab.sh/docs/en/api-reference/gemini/cached-contents-get Reads cached content metadata. Resources can only be read, referenced, changed, or deleted in their owning Workspace. Missing, expired, or inaccessible resources return a Gemini-format error. --- ### Update Gemini cached content > Updates the cache lifetime. URL: https://tokenlab.sh/docs/en/api-reference/gemini/cached-contents-update Updates the cache lifetime. Invalid lifetime or unsupported fields return a Gemini-format error. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ### Delete Gemini cached content > Deletes cached content. URL: https://tokenlab.sh/docs/en/api-reference/gemini/cached-contents-delete Deletes cached content. It cannot be used by subsequent generation after deletion. Resources can only be read, referenced, changed, or deleted in their owning Workspace. --- ## Management ### Management API > Check Workspace balances, manage API keys, and retrieve usage and billing URL: https://tokenlab.sh/docs/en/api-reference/management/introduction Use the Management API from your own backend or automation to manage a TokenLab Workspace. It uses a management token, so the program does not need a regular API key. Create a management token in [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens): ```bash Authorization: Bearer mt-your-management-token ``` > Warning: Management tokens and API keys have different roles. Use `mt-...` only with `/v1/management/*`. Use `sk-...` with model APIs such as `/v1/responses`. ## Endpoints | Endpoint | Method | Use | | --- | --- | --- | | `/v1/management/balance` | GET | Get the Workspace balance | | `/v1/management/api-keys` | GET | List API keys | | `/v1/management/api-keys` | POST | Create an API key | | `/v1/management/api-keys/{keyId}` | PATCH | Change its name, limit, models, expiration, or status | | `/v1/management/api-keys/{keyId}/usage` | GET | Get individual usage records | | `/v1/management/api-keys/{keyId}/billing` | GET | Get a billing summary | ## API Keys - A new key's full secret appears only in the create response. - `models: []` adds no key-level model restriction. - `limitAmount: null` means unlimited. `0` prevents the key from incurring spend. - Amounts use USD. Sending `CNY` returns `400 currency_retired`. - `inactive` can be enabled again. Use `revoked` to retire a key permanently. ## Usage and Billing `/usage` returns paginated records and supports model, model maker, scene, and date filters. Settled records can include `billing_transaction_id`; save it with `request_id` when you need to match a charge to a request. `/billing` returns totals grouped by model, model maker, scene, and date. It does not include Workspace top-ups or invoices. ## Example ```bash curl "https://api.tokenlab.sh/v1/management/balance" \ -H "Authorization: Bearer mt-your-management-token" ``` ## Next - [Get Workspace Balance](/api-reference/management/get-balance) - [List API Keys](/api-reference/management/list-api-keys) - [Create an API Key](/api-reference/management/create-api-key) - [Update an API Key](/api-reference/management/update-api-key) - [Get API Key Usage](/api-reference/management/get-api-key-usage) - [Get API Key Billing](/api-reference/management/get-api-key-billing) - [Webhook management API](/guides/webhooks) --- ### Get Workspace Balance > Get the balance, top-ups, and usage spend for the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/management/get-balance ## Overview Return the balance, total top-ups, and total usage spend for the Workspace associated with the management token. ## Notes - Monetary fields are USD-only. - Amounts are returned in USD. Use `balance_decimal`, `total_recharge_decimal`, and `total_used_decimal` for exact calculations. ## Example ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/management/balance" \ -H "Authorization: Bearer mt-your-management-token" ``` ## Request / Response This page provides the OpenAPI schema and copyable request examples. Send management requests from your terminal or client with a Management Token; direct sending is not available on this page. ## Response example ```json 200 OK { "object": "organization_balance", "organization_id": "org_123", "balance": 42.5, "balance_decimal": "42.50", "total_recharge": 100, "total_recharge_decimal": "100", "total_used": 57.5, "total_used_decimal": "57.50" } ``` ## Important fields #### object Always `organization_balance`. #### organization_id Organization associated with the current management token. #### balance Current organization balance in USD as a JSON number for display. #### balance_decimal Exact current organization balance in USD as a decimal string for reconciliation. #### total_recharge Total successful organization recharge amount in USD as a JSON number for display. #### total_recharge_decimal Exact total successful organization recharge amount in USD as a decimal string for reconciliation. #### total_used Total organization usage spend in USD as a JSON number for display. #### total_used_decimal Exact total organization usage spend in USD as a decimal string for reconciliation. --- ### List API Keys > Lists user-managed API keys for the current organization management token. URL: https://tokenlab.sh/docs/en/api-reference/management/list-api-keys ## Overview Returns user API keys in the organization associated with the current management token. ## Notes - Monetary fields are USD-only. - System-managed keys are excluded. ## Example #### page Page number starting at 1. #### limit Page size from 1 to 100. ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/management/api-keys" \ -H "Authorization: Bearer mt-your-management-token" ``` ## Request / Response This page provides the OpenAPI schema and copyable request examples. Send management requests from your terminal or client with a Management Token; direct sending is not available on this page. ## Response example ```json 200 OK { "object": "list", "data": [ { "id": "key_abc123def456", "name": "Backend Worker", "key_prefix": "sk-live...", "status": "active", "limit_amount": 500, "limit_amount_decimal": "500", "used_amount": 148.25, "used_amount_decimal": "148.25", "models": [ "gpt-5.6-luna", "claude-sonnet-4-6" ], "expires_at": "2026-12-31T23:59:59.000Z", "last_used_at": "2026-07-08T03:12:45.000Z", "created_at": "2026-07-01T10:00:00.000Z", "delivery_policy": null } ], "pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 } } ``` ## Important fields #### object Always `list`. #### data Array of API key objects. #### pagination Pagination metadata with `page`, `limit`, `total`, and `totalPages`. #### data[].id API key identifier used in follow-up Management API calls. #### data[].name Display name for the API key. #### data[].key_prefix Non-secret key prefix for display and support. #### data[].status One of `active`, `inactive`, `suspended`, or `revoked`. #### data[].limit_amount Spending cap in USD as a JSON number for display. `null` means unlimited. #### data[].limit_amount_decimal Exact spending cap in USD as a decimal string. `null` means unlimited. #### data[].used_amount Accumulated usage in USD as a JSON number for display. #### data[].used_amount_decimal Exact accumulated usage in USD as a decimal string. #### data[].models Per-key model allowlist. Empty array means no additional key-level model restriction. #### data[].delivery_policy `auto`, `verified`, `official`, `null`. `null` inherits the Workspace delivery policy. #### data[].expires_at ISO timestamp when the key expires, or `null` for no expiry. #### data[].last_used_at ISO timestamp for the most recent use, or `null` when unused. #### data[].created_at ISO creation timestamp. --- ### Create API Key > Create an API key for the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/management/create-api-key ## Overview Create an API key in the Workspace associated with the management token. The full secret appears once, in this response. ## Request Body | Field | Type | Default / Limits | Notes | |-------|------|------------------|-------| | `name` | string | optional, default `Default Key`, length `1-50` | Display name, trimmed server-side | | `limitAmount` | number \| string \| null | `0–100000` USD | `null` means unlimited; `0` prevents spend. Decimal strings support up to 6 decimal places. Omitting this field when creating a key means unlimited. | | `limitCurrency` | enum | default `USD` | USD only. Sending `CNY` returns `400 currency_retired`. | | `models` | string[] | default `[]` | Optional model allowlist | | `deliveryPolicy` | string \| null | `auto`, `verified`, `official`, `null` | `null` inherits the Workspace delivery policy. | | `expiresAt` | string \| null | RFC3339 datetime | `null` means no expiry | ## Notes - Monetary fields are USD-only. `CNY` inputs are retired and return `400 currency_retired`. - `models: []` adds no key-level model restriction. - `limitAmount: 0` creates a key that is valid but cannot spend quota until updated. ## Example ```bash cURL curl -X POST "https://api.tokenlab.sh/v1/management/api-keys" \ -H "Authorization: Bearer mt-your-management-token" \ -H "Content-Type: application/json" \ -d '{ "name": "Backend Worker", "limitAmount": 500, "models": ["veo3.1", "kling-3.0-video"], "expiresAt": "2026-12-31T23:59:59Z" }' ``` ## Request / Response This page provides the OpenAPI schema and copyable request examples. Send management requests from your terminal or client with a Management Token; direct sending is not available on this page. ## Response example ```json 201 Created { "id": "key_abc123def456", "name": "Backend Worker", "key": "sk-live-redacted", "key_prefix": "sk-live...", "status": "active", "limit_amount": 500, "limit_amount_decimal": "500", "used_amount": 0, "used_amount_decimal": "0", "models": [ "veo3.1", "kling-3.0-video" ], "expires_at": "2026-12-31T23:59:59.000Z", "last_used_at": null, "created_at": "2026-07-08T03:00:00.000Z", "delivery_policy": null } ``` ## Important fields #### key Full secret API key value. It is returned only once on creation. #### id API key identifier used in follow-up Management API calls. #### name Display name for the API key. #### key_prefix Non-secret key prefix for display and support. #### status One of `active`, `inactive`, `suspended`, or `revoked`. #### limit_amount Spending cap in USD as a JSON number for display. `null` means unlimited. #### limit_amount_decimal Exact spending cap in USD as a decimal string. `null` means unlimited. #### used_amount Accumulated usage in USD as a JSON number for display. #### used_amount_decimal Exact accumulated usage in USD as a decimal string. #### models Per-key model allowlist. Empty array means no additional key-level model restriction. #### delivery_policy `auto`, `verified`, `official`, `null`. `null` inherits the Workspace delivery policy. #### expires_at ISO timestamp when the key expires, or `null` for no expiry. #### last_used_at ISO timestamp for the most recent use, or `null` when unused. #### created_at ISO creation timestamp. --- ### Update API Key > Update an API key in the current Workspace. URL: https://tokenlab.sh/docs/en/api-reference/management/update-api-key ## Overview Use this endpoint to update the name, usage limit, allowed models, expiry, or status of an existing user API key. ## Request Body At least one field must be provided. | Field | Type | Default / Limits | Notes | |-------|------|------------------|-------| | `status` | enum | - | `active`, `inactive`, `revoked` | | `name` | string | length `1-50` | Updated display name | | `limitAmount` | number \| string \| null | `0–100000` USD | `null` means unlimited; `0` prevents spend. Decimal strings support up to 6 decimal places. | | `limitCurrency` | enum | default `USD` | USD only. Sending `CNY` returns `400 currency_retired`. When set, `limitAmount` is required. | | `models` | string[] | - | Updated model allowlist | | `deliveryPolicy` | string \| null | `auto`, `verified`, `official`, `null` | `null` inherits the Workspace delivery policy. | | `expiresAt` | string \| null | RFC3339 datetime | `null` clears the expiry | ## Notes - Monetary fields are USD-only. `CNY` inputs are retired and return `400 currency_retired`. - The Management API does not provide deletion. Use `inactive` for a temporary pause and `revoked` to retire a key permanently. - Replace a revoked key with a new one. ## Example ```bash cURL curl -X PATCH "https://api.tokenlab.sh/v1/management/api-keys/key_abc123def456" \ -H "Authorization: Bearer mt-your-management-token" \ -H "Content-Type: application/json" \ -d '{ "status": "inactive", "limitAmount": 0 }' ``` ## Request / Response This page provides the OpenAPI schema and copyable request examples. Send management requests from your terminal or client with a Management Token; direct sending is not available on this page. ## Response example ```json 200 OK { "id": "key_abc123def456", "name": "Backend Worker", "key_prefix": "sk-live...", "status": "inactive", "limit_amount": 0, "limit_amount_decimal": "0", "used_amount": 148.25, "used_amount_decimal": "148.25", "models": [ "gpt-5.6-luna", "claude-sonnet-4-6" ], "expires_at": "2026-12-31T23:59:59.000Z", "last_used_at": "2026-07-08T03:12:45.000Z", "created_at": "2026-07-01T10:00:00.000Z", "delivery_policy": null } ``` ## Important fields #### id API key identifier used in follow-up Management API calls. #### name Display name for the API key. #### key_prefix Non-secret key prefix for display and support. #### status One of `active`, `inactive`, `suspended`, or `revoked`. #### limit_amount Spending cap in USD as a JSON number for display. `null` means unlimited. #### limit_amount_decimal Exact spending cap in USD as a decimal string. `null` means unlimited. #### used_amount Accumulated usage in USD as a JSON number for display. #### used_amount_decimal Exact accumulated usage in USD as a decimal string. #### models Per-key model allowlist. Empty array means no additional key-level model restriction. #### delivery_policy `auto`, `verified`, `official`, `null`. `null` inherits the Workspace delivery policy. #### expires_at ISO timestamp when the key expires, or `null` for no expiry. #### last_used_at ISO timestamp for the most recent use, or `null` when unused. #### created_at ISO creation timestamp. --- ### Get API Key Usage > Returns paginated usage details for a user API key. URL: https://tokenlab.sh/docs/en/api-reference/management/get-api-key-usage ## Overview Return paginated usage records for one API key. ## Query Parameters | Parameter | Type | Default / Limits | Notes | |-----------|------|------------------|-------| | `page` | integer | default `1`, min `1` | 1-based page number | | `limit` | integer | default `50`, min `1`, max `100` | Page size | | `model` | string | max length `100` | Requested model name | | `modelVendor` | string | max length `100` | Public model vendor | | `scene` | enum | - | `chat`, `image`, `audio`, `video`, `embedding`, `rerank`, `translation`, `music`, `3d`, `realtime` | | `startDate` | string | - | Inclusive lower bound; accepts RFC3339 with timezone or `YYYY-MM-DD` | | `endDate` | string | - | Inclusive upper bound; accepts RFC3339 with timezone or `YYYY-MM-DD` | If both `startDate` and `endDate` are present, `startDate` must be earlier than or equal to `endDate`. ## Notes - Monetary fields are USD-only. - The response is paginated. - Settled records can include `billing_transaction_id`. Save it with `request_id` when you need to match a charge to a request. ## Example ```bash cURL curl "https://api.tokenlab.sh/v1/management/api-keys/key_abc123def456/usage?page=1&limit=20&scene=chat&startDate=2026-07-01&endDate=2026-07-31" \ -H "Authorization: Bearer mt-your-management-token" ``` ## Request / Response This page provides the OpenAPI schema and copyable request examples. Send management requests from your terminal or client with a Management Token; direct sending is not available on this page. ## Response example ```json 200 OK { "api_key": { "id": "key_abc123def456", "name": "Backend Worker", "key_prefix": "sk-live...", "status": "active", "limit_amount": 500, "limit_amount_decimal": "500", "used_amount": 148.25, "used_amount_decimal": "148.25", "models": [ "gpt-5.6-luna", "claude-sonnet-4-6" ], "expires_at": "2026-12-31T23:59:59.000Z", "last_used_at": "2026-07-08T03:12:45.000Z", "created_at": "2026-07-01T10:00:00.000Z", "delivery_policy": null }, "items": [ { "request_id": "req_abc123", "billing_transaction_id": "billtxn_abc123", "model": "gpt-5.6-luna", "logical_model": "gpt-5.6-luna", "model_vendor": "OpenAI", "scene": "chat", "usage_unit": "per_token", "usage_quantity": 1500, "usage_quantity_decimal": "1500", "prompt_tokens": 1000, "completion_tokens": 500, "total_tokens": 1500, "cache_read_tokens": 0, "cache_write_tokens": 0, "cache_write_tokens_5m": 0, "cache_write_tokens_1h": 0, "cost": 0.0012, "cost_decimal": "0.0012", "created_at": "2026-07-08T03:15:00.000Z", "requested_policy": null, "resolved_tier": null, "charged_price_basis": null } ], "pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }, "summary": { "total_requests": 1, "total_cost": 0.0012, "total_cost_decimal": "0.0012", "total_prompt_tokens": 1000, "total_completion_tokens": 500, "total_tokens": 1500, "usage_unit_breakdown": [ { "usage_unit": "per_token", "usage_quantity": 1500 } ] }, "breakdowns": { "by_model": [ { "model": "gpt-5.6-luna", "logical_model": "gpt-5.6-luna", "model_vendor": "OpenAI", "requests": 1, "cost": 0.0012, "cost_decimal": "0.0012", "usage_quantity": 1500, "usage_quantity_decimal": "1500" } ], "by_logical_model": [ { "model": "gpt-5.6-luna", "logical_model": "gpt-5.6-luna", "model_vendor": "OpenAI", "requests": 1, "cost": 0.0012, "cost_decimal": "0.0012", "usage_quantity": 1500, "usage_quantity_decimal": "1500" } ], "by_model_vendor": [ { "model_vendor": "OpenAI", "requests": 1, "cost": 0.0012 } ], "by_scene": [ { "scene": "chat", "requests": 1, "cost": 0.0012, "cost_decimal": "0.0012" } ], "daily_cost": [ { "date": "2026-07-08T00:00:00.000Z", "cost": 0.0012, "cost_decimal": "0.0012" } ] } } ``` ## Important fields #### api_key API key summary using the same fields as list/create/update responses. #### items Paginated usage rows. #### pagination Pagination metadata with `page`, `limit`, `total`, and `totalPages`. #### summary Aggregated totals for the selected filters. #### breakdowns Aggregated breakdowns for the selected filters. #### items[].request_id Public request identifier for support and reconciliation. #### items[].billing_transaction_id Settled billing transaction id when available. #### items[].model Requested public model name. #### items[].logical_model Logical public model used for reporting. #### items[].model_vendor Public model vendor. #### items[].scene Public request scene. #### items[].usage_unit Billing usage unit, such as `per_token`. #### items[].usage_quantity Usage quantity as a JSON number for display. #### items[].usage_quantity_decimal Exact usage quantity as a decimal string. #### items[].prompt_tokens Prompt token count. #### items[].completion_tokens Completion token count. #### items[].total_tokens Total token count. #### items[].cache_read_tokens Cache read token count. #### items[].cache_write_tokens Total cache write token count. #### items[].cache_write_tokens_5m Five-minute cache write token count. #### items[].cache_write_tokens_1h One-hour cache write token count. #### items[].cost Cost in USD as a JSON number for display. #### items[].cost_decimal Exact cost in USD as a decimal string. #### items[].requested_policy `auto`, `verified`, `official`, `null`. Requested delivery policy, when recorded. #### items[].resolved_tier `verified`, `official`, `null`. Delivery tier used, when recorded. #### items[].charged_price_basis Applied price and billable quantity, when recorded. For a request with tool fees, `measure.dimensions` includes `webSearchCalls` or `groundedPrompts`, and `effectiveUnitPrices` includes the price of each call as `webSearch` or `groundedPrompt`. #### items[].created_at ISO usage row timestamp. #### summary.total_requests Total request count. #### summary.total_cost Total cost in USD as a JSON number for display. #### summary.total_cost_decimal Exact total cost in USD as a decimal string. #### summary.total_prompt_tokens Total prompt tokens. #### summary.total_completion_tokens Total completion tokens. #### summary.total_tokens Total tokens. #### summary.usage_unit_breakdown Usage quantity grouped by usage unit. #### breakdowns.by_model Breakdown by requested public model. #### breakdowns.by_logical_model Breakdown by logical public model. #### breakdowns.by_model_vendor Breakdown by public model vendor. #### breakdowns.by_scene Breakdown by public request scene. #### breakdowns.daily_cost Daily cost trend. --- ### Get API Key Billing > Returns aggregated billing breakdowns for a user API key. URL: https://tokenlab.sh/docs/en/api-reference/management/get-api-key-billing ## Overview Return total spend, request count, billable usage, and cost grouped by model, model maker, scene, and date. ## Notes - Monetary fields are USD-only. - Workspace top-ups and invoices are not included. ## Example Supports the same model, model vendor, scene, and date filters as [API key usage](/api-reference/management/get-api-key-usage). Returns totals without pagination. ```bash cURL curl -X GET "https://api.tokenlab.sh/v1/management/api-keys/key_abc123def456/billing?startDate=2026-07-01&endDate=2026-07-31" \ -H "Authorization: Bearer mt-your-management-token" ``` ## Request / Response This page provides the OpenAPI schema and copyable request examples. Send management requests from your terminal or client with a Management Token; direct sending is not available on this page. ## Response example ```json 200 OK { "api_key": { "id": "key_abc123def456", "name": "Backend Worker", "key_prefix": "sk-live...", "status": "active", "limit_amount": 500, "limit_amount_decimal": "500", "used_amount": 148.25, "used_amount_decimal": "148.25", "models": [ "gpt-5.6-luna", "claude-sonnet-4-6" ], "expires_at": "2026-12-31T23:59:59.000Z", "last_used_at": "2026-07-08T03:12:45.000Z", "created_at": "2026-07-01T10:00:00.000Z", "delivery_policy": null }, "summary": { "total_requests": 1, "total_cost": 0.0012, "total_cost_decimal": "0.0012", "total_prompt_tokens": 1000, "total_completion_tokens": 500, "total_tokens": 1500, "usage_unit_breakdown": [ { "usage_unit": "per_token", "usage_quantity": 1500 } ] }, "breakdowns": { "by_model": [ { "model": "gpt-5.6-luna", "logical_model": "gpt-5.6-luna", "model_vendor": "OpenAI", "requests": 1, "cost": 0.0012, "cost_decimal": "0.0012", "usage_quantity": 1500, "usage_quantity_decimal": "1500" } ], "by_logical_model": [ { "model": "gpt-5.6-luna", "logical_model": "gpt-5.6-luna", "model_vendor": "OpenAI", "requests": 1, "cost": 0.0012, "cost_decimal": "0.0012", "usage_quantity": 1500, "usage_quantity_decimal": "1500" } ], "by_model_vendor": [ { "model_vendor": "OpenAI", "requests": 1, "cost": 0.0012 } ], "by_scene": [ { "scene": "chat", "requests": 1, "cost": 0.0012, "cost_decimal": "0.0012" } ], "daily_cost": [ { "date": "2026-07-08T00:00:00.000Z", "cost": 0.0012, "cost_decimal": "0.0012" } ] } } ``` ## Important fields #### api_key API key summary using the same fields as list/create/update responses. #### summary Aggregated totals for the selected filters. #### breakdowns Aggregated breakdowns for the selected filters. #### summary.total_requests Total request count. #### summary.total_cost Total cost in USD as a JSON number for display. #### summary.total_cost_decimal Exact total cost in USD as a decimal string. #### summary.total_prompt_tokens Total prompt tokens. #### summary.total_completion_tokens Total completion tokens. #### summary.total_tokens Total tokens. #### summary.usage_unit_breakdown Usage quantity grouped by usage unit. #### breakdowns.by_model Breakdown by requested public model. #### breakdowns.by_logical_model Breakdown by logical public model. #### breakdowns.by_model_vendor Breakdown by public model vendor. #### breakdowns.by_scene Breakdown by public request scene. #### breakdowns.daily_cost Daily cost trend. --- ### List webhook endpoints URL: https://tokenlab.sh/docs/en/api-reference/management/list-webhooks Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- ### Create webhook endpoint URL: https://tokenlab.sh/docs/en/api-reference/management/create-webhook Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- ### Retrieve webhook endpoint URL: https://tokenlab.sh/docs/en/api-reference/management/get-webhook Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- ### Update webhook endpoint URL: https://tokenlab.sh/docs/en/api-reference/management/update-webhook Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- ### Delete webhook endpoint URL: https://tokenlab.sh/docs/en/api-reference/management/delete-webhook Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- ### Rotate webhook signing secret URL: https://tokenlab.sh/docs/en/api-reference/management/rotate-webhook-secret Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- ### Send a test webhook URL: https://tokenlab.sh/docs/en/api-reference/management/test-webhook Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- ### List webhook deliveries URL: https://tokenlab.sh/docs/en/api-reference/management/list-webhook-deliveries Use a workspace **Management Token (`mt-...`)**, not an inference API key (`sk-...`) or signing secret (`whsec_...`). Create one at [Dashboard → API → Management Tokens](https://tokenlab.sh/dashboard/api?tab=tokens) and send `Authorization: Bearer mt-...`. Access is scoped to that workspace. See [Webhook management API guide](/guides/webhooks) for events, verification, retries and troubleshooting. --- # Integrations ## Compatibility Matrix ### Tool and SDK compatibility > Find the right TokenLab setup for coding tools, chat apps, SDKs, and frameworks URL: https://tokenlab.sh/docs/en/guides/ide-sdk-compatibility Most tools connect to TokenLab by changing an API key and base URL. The exact setup depends on which API format the tool can use. | API format | Base URL | Common clients | | --- | --- | --- | | Chat Completions (OpenAI-compatible) | `https://api.tokenlab.sh/v1` | OpenAI SDKs, many chat apps and frameworks | | OpenAI Responses | `https://api.tokenlab.sh/v1` | Responses clients and tools built around Responses events | | Anthropic Messages | `https://api.tokenlab.sh` | Anthropic SDKs and Claude-oriented tools | | Gemini | `https://api.tokenlab.sh` | Gemini REST and SDK clients that allow a custom host | The selected model must list the same format in `accepted_request_formats`. Streaming and tool calling also depend on the model’s capabilities and the client. See [API formats](/guides/api-formats) for examples. ## Compatibility labels | Label | Meaning | | --- | --- | | **Works** | The linked TokenLab setup uses the client’s documented configuration options | | **Works with limits** | The configuration applies to specific features or authentication modes; other vendor services keep their own requirements | These labels describe the configuration on the linked page. Feature availability also depends on the selected model and client version. ## Coding tools | Tool | Status | API format | Notes | | --- | --- | --- | --- | | [Claude Code](/integrations/claude-code) | Works | Anthropic Messages | Uses `ANTHROPIC_BASE_URL` and a TokenLab API key | | [Codex CLI](/integrations/codex-cli) | Works with limits | Responses | Uses Responses; available features depend on the selected model | | [Cursor](/integrations/cursor) | Works with limits | OpenAI-compatible | Applies to Cursor features that accept a custom API key and URL | | [Cline](/integrations/cline) | Works | OpenAI-compatible | Select **OpenAI Compatible** and enter a base URL and model ID | | [Kilo Code](/integrations/kilo-code) | Works | OpenAI-compatible | Uses a custom provider and model ID | | [OpenCode](/integrations/opencode) | Works | OpenAI-compatible | Load a separate provider configuration with `OPENCODE_CONFIG` | | [OpenHands](/integrations/openhands) | Works | OpenAI-compatible | LiteLLM-backed UI: **Advanced → Custom Model** (`openai/` prefix) and **Base URL** | | [Gemini CLI](/integrations/gemini-cli) | Works with limits | Gemini | Use `GOOGLE_GEMINI_BASE_URL` with API-key authentication; a separate `GEMINI_CLI_HOME` preserves the existing setup | ## Chat applications | App | Status | Notes | | --- | --- | --- | | [Chatbox](/integrations/chatbox) | Works | Add an **OpenAI API Compatible** provider | | [Cherry Studio](/integrations/cherry-studio) | Works | Add a provider and complete **Verify and Enable** | | [DeepChat](/integrations/deepchat) | Works | Choose **OpenAI Completions** for Chat Completions | | [Jan](/integrations/jan) | Works | Add an OpenAI-compatible service | | [LibreChat](/integrations/librechat) | Works | Configure a custom OpenAI-compatible endpoint | | [Open WebUI](/integrations/open-webui) | Works | Add an OpenAI-compatible connection with API Type **Chat** | ## SDKs and frameworks | Client | Status | Setup | | --- | --- | --- | | [OpenAI SDK](/integrations/openai-sdk) | Works | Set `base_url` / `baseURL` to `https://api.tokenlab.sh/v1` | | [Anthropic SDK](/integrations/anthropic-sdk) | Works | Set the base URL to `https://api.tokenlab.sh` | | [Vercel AI SDK](/integrations/vercel-ai-sdk) | Works | Create a custom OpenAI-compatible provider | | [LangChain](/integrations/langchain) | Works | Configure `ChatOpenAI` or `OpenAIEmbeddings` with the TokenLab URL | | [LlamaIndex](/integrations/llamaindex) | Works | Use its OpenAI-compatible client options | | [OpenAI Agents SDK](/integrations/openai-agents-sdk) | Works with limits | The guide uses `OpenAIChatCompletionsModel`; check model and SDK feature support before using Responses | | [AutoGen](/integrations/autogen) | Works | Use an OpenAI-compatible model client | | [CrewAI](/integrations/crewai) | Works | Configure its OpenAI-compatible LLM client | | [PydanticAI](/integrations/pydantic-ai) | Works | Supply an OpenAI-compatible provider | More frameworks, observability tools, gateways, and evaluation products are listed in the Integrations navigation. ## Usage in streamed responses Chat Completions streams accept `stream_options: { include_usage: true }`, which appends a final usage chunk before the stream closes. The Responses API does not expose `include_usage`; Responses streams report usage on their terminal event instead. ## What a custom API key does not replace Some products mix model calls with features hosted by the product vendor. A TokenLab key can power the model requests exposed through BYOK settings, but it does not replace unrelated subscriptions, autocomplete services, account features, or proprietary extensions. When a tool update changes its configuration, use the current integration page rather than copying an old environment-variable snippet from elsewhere. ## Before depending on a feature - Confirm the tool allows a custom base URL for the format you need. - Confirm the model lists that format in `accepted_request_formats`. - Test streaming and tool calls if your workflow requires them. - Keep the API key on a trusted machine or server. - Record the Request ID when reporting a failure. --- ### SDKs & Libraries > Use TokenLab with the SDK already in your application URL: https://tokenlab.sh/docs/en/guides/sdks ## Overview You do not need a TokenLab-specific SDK. Keep the client your application already uses and change its API key and base URL: - **OpenAI SDKs** work with Chat Completions and Responses. - **Anthropic SDKs** work with Claude Messages. - **Gemini / Google AI SDKs** work with Gemini requests. Open the matching integration page for client configuration and model-specific limits. For a complete first request, use the [Quickstart](/quickstart). ## Choose an SDK #### [OpenAI Python](https://github.com/openai/openai-python) `pip install openai` #### [OpenAI Node](https://github.com/openai/openai-node) `npm install openai` #### [OpenAI Go](https://github.com/openai/openai-go) `go get github.com/openai/openai-go/v3` #### [Anthropic SDK](https://github.com/anthropics/anthropic-sdk-python) Native Claude Messages API support ## OpenAI SDK Example Use this for Chat Completions or Responses. The selected model must support the API method you call. ```python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh/v1" ) response = client.responses.create( model="gpt-5.6-terra", input="Reply with: Connected to TokenLab." ) print(response.output_text) ``` ## OpenAI Go Example ```go package main import ( openai "github.com/openai/openai-go/v3" "github.com/openai/openai-go/v3/option" ) func main() { client := openai.NewClient( option.WithAPIKey("sk-your-api-key"), option.WithBaseURL("https://api.tokenlab.sh/v1"), ) _ = client } ``` ## Anthropic SDK Example ```python from anthropic import Anthropic client = Anthropic( api_key="sk-your-api-key", base_url="https://api.tokenlab.sh" ) message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Which SDK should you use? | Need | SDK | |------|------------------| | Existing OpenAI-compatible client | OpenAI SDK on TokenLab `/v1` | | Chat Completions, Responses, or embeddings | OpenAI SDK | | Claude Messages fields | Anthropic SDK | | Gemini contents, files, cache, or tools | Gemini / Google AI SDK | | LangChain / LlamaIndex / Vercel AI SDK | Use the dedicated integration pages | ## Configuration notes #### Keep model-specific fields in their API format Claude Messages and Gemini represent tools, streaming, and content differently. Use the matching SDK when your application depends on those fields. #### Keep existing OpenAI clients Existing OpenAI-compatible chat and embedding clients can usually connect by changing the API key and base URL. #### Keep base URLs explicit Pass TokenLab base URLs directly in your client configuration instead of relying on older environment-variable aliases. ## System One decisions Jev uses the native synchronous System One interface with `model`, `state`, and named `questions`. TokenLab JavaScript SDK 0.1.2+ provides `evaluateDecisions(body)`; Python SDK 0.1.2+ provides `evaluate_decisions(body)`. Both preserve typed answers and usage and do not automatically replay a failed request. Use the System One guide for a complete payload. [System One](/api-reference/systemone/create-decision) · [JavaScript SDK](https://github.com/hedging8563/tokenlab-js-sdk) · [Python SDK](https://github.com/hedging8563/tokenlab-python-sdk) --- ## Coding Tools ### Control coding agent costs > Use real usage data to spend less without weakening every task URL: https://tokenlab.sh/docs/en/guides/coding-agent-cost Coding agents repeatedly send instructions, conversation history, code, and tool results. Long sessions can therefore cost much more than a single chat request. The useful levers are model choice, context size, output length, and caching when the selected model supports it. ## Start with your own usage Open [Usage](https://tokenlab.sh/dashboard/usage) in the workspace that owns your API key and compare requests by model or key. Compare similar tasks rather than relying on a generic hourly estimate. Record at least: - input, output, and cached tokens - cost per completed task - failed or repeated requests - the model and API key used ## Match the model to the work The most expensive model does not need to handle every step. | Work | What to optimize | |---|---| | Architecture or difficult debugging | Reasoning quality and enough context | | Focused edits and routine review | Reliable instruction following at a lower price | | Search, classification, and boilerplate | Speed and low input cost | | Final review | Quality on a smaller, carefully selected context | Use the [Models page](https://tokenlab.sh/en/models) for current prices and capabilities. Test candidate models on the same representative task before changing a production tool. ## Keep context intentional - Include the files and logs needed for the current task, not the whole repository by default. - Start a new conversation when old history no longer helps. - Let the agent search for code instead of pasting large directories into every prompt. - Keep reusable instructions short and remove duplicated rules. ## Limit unnecessary output Set the output limit supported by your selected [API format](/guides/api-formats) when the task has a predictable answer length. Chat Completions and Messages use different request shapes from Responses; use the endpoint’s documented field. A limit prevents accidental long responses, but it should still leave enough room for the task to finish. ## Use caching only when it is documented Some models charge less for cached input. Check the model's price details before counting on a cache discount, and confirm cached-token usage in the response or Usage. Cache behavior and prices differ by model. ## Separate projects with API keys Use a different API key for each project or coding tool when you need separate limits and usage history. A key-level limit can stop one tool from consuming the full account balance. ## Compare changes with real tasks 1. Choose a small set of tasks that represents your normal work. 2. Run the same tasks with each candidate model and the same context. 3. Compare completion quality, retries, total tokens, and final cost in Usage. The lowest price per token is not always the lowest cost per completed task. A cheaper request that needs several retries may cost more. ## Tool setup #### [Claude Code](/integrations/claude-code) Connect Claude Code with the Anthropic-compatible API. #### [Codex CLI](/integrations/codex-cli) Configure the TokenLab base URL and API key. #### [Cursor](/integrations/cursor) Review the supported BYOK boundary before setup. #### [OpenCode](/integrations/opencode) Add TokenLab as a custom provider. --- ### Choose a model for coding agents > Compare coding models with the work, context, speed, and budget that matter to you URL: https://tokenlab.sh/docs/en/guides/coding-agent-models There is no single best coding model. A model that is excellent at a small patch may be a poor fit for a repository-wide change, and leaderboard results do not predict how well it will follow your own instructions and tools. Use the [Models page](https://tokenlab.sh/en/models) for current model IDs, context limits, API formats, and TokenLab prices. Do not copy a permanent recommendation list into your configuration. ## Match the model to the job | Work | What matters most | | --- | --- | | Small edits and autocomplete | Fast responses, low cost, reliable instruction following | | Debugging | Reading logs and code together, testing hypotheses, staying within scope | | Code review | Finding concrete defects without flooding the review with speculation | | Large refactors | Enough context, consistent changes across files, and strong tool use | | Architecture | Clear tradeoffs, evidence from the repository, and good long-form reasoning | Use a smaller context for focused work even when the model supports a very large window. More context costs more and can bury the relevant code. ## Compare candidates on your own tasks Choose a few real tasks that represent the work you do. Give each candidate the same repository state, instructions, tools, and time limit. Compare: - whether the change is correct and complete - tests passed and regressions introduced - unnecessary files or code changed - total tokens and final TokenLab cost - time to a usable result - how often a person had to correct the agent Keep results by task type. One model can be your best reviewer while another is better for implementation or quick edits. ## Keep model changes visible Set the model explicitly in the coding tool. If your application offers a fallback, tell the user when the model changes. A fallback may have a different price, context limit, tool format, or output style. The API format also matters: | Tool or model need | Format | Base URL | | --- | --- | --- | | OpenAI-compatible chat | Chat Completions | `https://api.tokenlab.sh/v1` | | Responses events or tools | Responses | `https://api.tokenlab.sh/v1` | | Claude Messages features | Anthropic Messages | `https://api.tokenlab.sh` | | Gemini content and tools | Gemini | `https://api.tokenlab.sh` | Check `accepted_request_formats` for the selected model instead of inferring the format from its name. ## Coding tool guides #### [Claude Code](/integrations/claude-code) Use the Anthropic Messages API and a TokenLab API key. #### [Codex CLI](/integrations/codex-cli) Configure the TokenLab base URL and review Responses support. #### [Cursor](/integrations/cursor) Use Cursor's custom OpenAI-compatible settings where BYOK is supported. #### [OpenCode](/integrations/opencode) Add TokenLab as a custom model provider. For cost controls, separate API keys, and usage comparison, continue with [Control coding agent costs](/guides/coding-agent-cost). --- ### TokenLab Provider Setup > Connect a self-hosted OpenClaw instance to TokenLab URL: https://tokenlab.sh/docs/en/guides/tokenlab-provider ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and help me connect OpenClaw to TokenLab: https://tokenlab.sh/docs/en/guides/tokenlab-provider Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` > Note: This guide is for **self-hosted OpenClaw**. The TokenLab plugin is the shortest setup; manual configuration is available when you need a specific API format. ## Install the plugin Optional: install the published provider plugin **0.1.1**. Supply `TOKENLAB_API_KEY` through the same Gateway environment described below and use session-only model selection. The plugin onboarding wizard changes the default model, so this guide does not run `openclaw onboard`. Keep existing plugin entries and permissions. Check the installed plugin and model list before making a request. Review the capabilities requested by the installer and follow the client’s confirmation prompts. Do not grant unrelated permissions or change the permissions of existing plugins. ```bash openclaw plugins install @tokenlabai/openclaw-provider@0.1.1 openclaw plugins list openclaw models list --provider tokenlab ``` ```text /model tokenlab/claude-sonnet-5 -s /model default -s ``` [npm](https://www.npmjs.com/package/@tokenlabai/openclaw-provider/v/0.1.1) · [GitHub](https://github.com/hedging8563/tokenlab-openclaw-provider) ## Configure providers manually Use `models.providers` only when OpenClaw needs Responses, Claude Messages, Gemini, or MiniMax formats separately. For ordinary chat, the `tokenlab` entry is enough. | Provider | OpenClaw `api` | Best for | baseUrl | |----------|----------------|----------|---------| | `tokenlab` | `openai-completions` | GPT, DeepSeek, Qwen, and most OpenAI-compatible calls | `https://api.tokenlab.sh/v1` | | `tokenlab-responses` | `openai-responses` | OpenAI Responses workflows that expect `/v1/responses` semantics | `https://api.tokenlab.sh/v1` | | `tokenlab-claude` | `anthropic-messages` | Native Claude Messages API | `https://api.tokenlab.sh` | | `tokenlab-gemini` | `google-generative-ai` | Native Gemini API format | `https://api.tokenlab.sh` | | `tokenlab-minimax` | `anthropic-messages` | Native MiniMax routing | `https://api.tokenlab.sh` | > Warning: Use the `/v1` suffix only for `openai-completions` and `openai-responses`. > > Native providers such as `anthropic-messages` and `google-generative-ai` should use `https://api.tokenlab.sh` without `/v1`, otherwise OpenClaw may construct the wrong provider path. ## Prerequisites - A self-hosted OpenClaw instance - For OpenClaw **2026.9.4**, use Node.js `>=24.16.0 <25 || >=26.1.0` ([version requirements](https://github.com/openclaw/openclaw/blob/v2026.9.4/package.json)). For other OpenClaw releases, check that release's requirements. - A TokenLab API Key — [Get one here](https://tokenlab.sh/dashboard/api?tab=keys) ## Configuration Edit your OpenClaw config: - **Self-hosted**: `~/.openclaw/openclaw.json` Add TokenLab providers under `models.providers`: Use the same profile, `OPENCLAW_STATE_DIR` and `OPENCLAW_CONFIG_PATH` as the running Gateway. Back up the active configuration before merging. Put `TOKENLAB_API_KEY` in that instance’s trusted global `.env` or service environment; a terminal export or project `.env` alone may not reach the background Gateway. See [environment handling](https://docs.openclaw.ai/help/environment). Back up the active configuration. Add only the TokenLab entry and preserve existing providers, accounts, default-model selection and permissions. If the name is already used, choose another name and update the commands. To undo setup, remove only your added entry or restore its previous backup. ```json5 { models: { mode: "merge", providers: { tokenlab: { api: "openai-completions", baseUrl: "https://api.tokenlab.sh/v1", apiKey: "${TOKENLAB_API_KEY}", models: [ { id: "gpt-5.6-terra", name: "GPT-5.6 Terra" }, { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro" }, { id: "qwen3-coder-flash", name: "Qwen 3 Coder Flash" } ] }, "tokenlab-responses": { api: "openai-responses", baseUrl: "https://api.tokenlab.sh/v1", apiKey: "${TOKENLAB_API_KEY}", models: [ { id: "gpt-5.6-terra", name: "GPT-5.6 Terra (Responses)" }, { id: "gpt-5.2", name: "GPT-5.2 (Responses)" } ] }, "tokenlab-claude": { api: "anthropic-messages", baseUrl: "https://api.tokenlab.sh", apiKey: "${TOKENLAB_API_KEY}", models: [ { id: "claude-sonnet-5", name: "Claude Sonnet 5" }, { id: "claude-opus-5", name: "Claude Opus 5" } ] }, "tokenlab-gemini": { api: "google-generative-ai", baseUrl: "https://api.tokenlab.sh", apiKey: "${TOKENLAB_API_KEY}", models: [ { id: "gemini-3.5-flash", name: "Gemini 3.5 Flash" }, { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro" } ] }, "tokenlab-minimax": { api: "anthropic-messages", baseUrl: "https://api.tokenlab.sh", apiKey: "${TOKENLAB_API_KEY}", models: [ { id: "minimax-m3", name: "MiniMax M3" } ] } } } } ``` > Info: All 5 providers use the **same API Key**. You only need one TokenLab account. > Info: The `models` arrays above only show common examples. Add more model IDs to each provider as needed. ## Using Models OpenClaw still references models with the `provider/model` format: For **OpenClaw 2026.9.4**, select the model only for the current session with `-s`. Keep `agents.defaults.model`, agent defaults and permissions unchanged. Use `/model default -s` to return this session to its configured default. ```text /model tokenlab-claude/claude-sonnet-5 -s /model default -s ``` ### Model Examples | Provider | Model reference | Description | |----------|-----------------|-------------| | `tokenlab` | `tokenlab/gpt-5.6-terra` | OpenAI-compatible route | | `tokenlab-responses` | `tokenlab-responses/gpt-5.6-terra` | Responses API route | | `tokenlab-claude` | `tokenlab-claude/claude-sonnet-5` | Native Claude Messages route | | `tokenlab-gemini` | `tokenlab-gemini/gemini-3.5-flash` | Native Gemini route | | `tokenlab-minimax` | `tokenlab-minimax/minimax-m3` | Native MiniMax route | Browse all available models at [tokenlab.sh/models](https://tokenlab.sh/en/models). ## When to Use Which Provider - `tokenlab`: default choice for most general-purpose agent and chat use cases. - `tokenlab-responses`: use when your OpenClaw workflow explicitly depends on OpenAI Responses semantics. - `tokenlab-claude`: use when you want Claude's native Messages behavior. - `tokenlab-gemini`: use when you want Gemini-native request/response formatting or existing Gemini-style integrations. - `tokenlab-minimax`: use when you want MiniMax on its native route. If you do not need Gemini-native behavior, you can still call Gemini models through `tokenlab/gemini-*` on the OpenAI-compatible route. ## Common Mistakes #### Still using the old top-level providers array Current OpenClaw docs use `models.providers`. If you keep the older top-level `providers` array format, OpenClaw may ignore the config or fail to resolve the provider prefixes as expected. #### Forgetting /v1 on tokenlab-responses `openai-responses` maps to TokenLab's `/v1/responses` path, so `tokenlab-responses` must use `https://api.tokenlab.sh/v1`. #### Adding /v1 to tokenlab-claude, tokenlab-gemini, or tokenlab-minimax `anthropic-messages` and `google-generative-ai` should use `https://api.tokenlab.sh` without `/v1`. Adding `/v1` can produce incorrect request paths. #### Does OpenClaw still support native Gemini? Yes. Current OpenClaw documentation still includes the built-in `google` provider and also supports custom providers using `api: "google-generative-ai"`. So `tokenlab-gemini` remains a valid native Gemini route for OpenClaw users. ## Verify Setup Restart that instance, then inspect its selected model and provider. `models status` without `--probe` does not make a model-call test. A live `--probe` or a chat message can consume tokens. Check a real reply against the matching TokenLab request before declaring the setup working. To restore, choose the previous model and remove only the TokenLab entries you added. ```bash openclaw gateway restart openclaw models status ``` ## Next Steps Once OpenClaw is connected, these guides help you use TokenLab more effectively: - [API Formats](/guides/api-formats) — understand the differences between OpenAI, Responses, Anthropic, and Gemini routes - [IDE / SDK Compatibility](/guides/ide-sdk-compatibility) — see when `/v1/responses` is the better fit - [Error Handling](/guides/error-handling) — learn common failure modes and recovery patterns - [Models Overview](https://tokenlab.sh/en/models) — browse model IDs before wiring them into agents --- ### TokenLab MCP Server > Give Claude Code, Cursor, VS Code, Codex, and other MCP clients access to TokenLab models and APIs URL: https://tokenlab.sh/docs/en/integrations/tokenlab-mcp-server ## Choose what your agent needs - MCP adds TokenLab API tools to a compatible client. Start with the key-free `catalog` setup below. - The [TokenLab Skill](/integrations/coding-agent-skill) installs integration instructions with `npx skills add`; it does not start an MCP server. Both extend an existing agent. Changing its main model provider uses that client’s own setup guide. ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and choose MCP catalog tools or a Skill for my task: https://tokenlab.sh/docs/en/integrations/tokenlab-mcp-server Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` The [TokenLab MCP Server](https://github.com/hedging8563/tokenlab-mcp-server) lets an MCP client browse current models and prices, send model requests, create media, work with files, and check async tasks. Use the `catalog` profile to browse models and prices without an API key. Add `TOKENLAB_API_KEY` when the client should make paid model or media requests. > Warning: Keep your TokenLab API key in the MCP server environment. Never paste it into a prompt or tool argument. ## Requirements Install Node.js 18.17 or newer and make sure `npx` is available: ```bash node --version npx --version ``` The npm package runs locally over stdio. You do not need a global installation or a source checkout. ## Choose what the client can use | Profile | API key | Includes | | --- | --- | --- | | `catalog` | Not required | Model list, model details, prices, comparisons, and API overview | | `core` | Required for paid calls | Common chat, decision, media, audio, file, task, embedding, rerank, and translation tools | | `full` | Required for paid calls | `core` plus additional developer APIs | Start with `catalog` if you only want better model selection. Use `core` when the client should create content or call a model. `full` is for clients that genuinely need the larger tool set. ## Add the server Back up the active configuration. Add only the TokenLab entry and preserve existing providers, accounts, default-model selection and permissions. If the name is already used, choose another name and update the commands. To undo setup, remove only your added entry or restore its previous backup. #### Claude Code Add the public catalog for your user account: ```bash claude mcp add \ --env TOKENLAB_MCP_TOOL_PROFILE=catalog \ --scope user \ tokenlab -- \ npx -y @tokenlabai/mcp-server@0.6.26 ``` To use paid tools, replace the profile environment variable with `TOKENLAB_API_KEY` and store the key through your normal secret-management method. Use `--scope local` for one project. Never commit a real key in a shared `.mcp.json`. #### Codex ```bash codex mcp add \ --env TOKENLAB_MCP_TOOL_PROFILE=catalog \ tokenlab -- \ npx -y @tokenlabai/mcp-server@0.6.26 ``` For paid tools, configure `TOKENLAB_API_KEY` in the server environment. #### Cursor Save this as `~/.cursor/mcp.json` for all projects or `.cursor/mcp.json` for one project: ```json { "mcpServers": { "tokenlab": { "command": "npx", "args": ["-y", "@tokenlabai/mcp-server@0.6.26"], "env": { "TOKENLAB_MCP_TOOL_PROFILE": "catalog" } } } } ``` #### VS Code Save this as `.vscode/mcp.json`, or add the same server through **MCP: Add Server**: ```json { "servers": { "tokenlab": { "type": "stdio", "command": "npx", "args": ["-y", "@tokenlabai/mcp-server@0.6.26"], "env": { "TOKENLAB_MCP_TOOL_PROFILE": "catalog" } } } } ``` #### Claude Desktop Add this to `claude_desktop_config.json`: ```json { "mcpServers": { "tokenlab": { "command": "npx", "args": ["-y", "@tokenlabai/mcp-server@0.6.26"], "env": { "TOKENLAB_MCP_TOOL_PROFILE": "catalog" } } } } ``` #### Other clients Any local stdio MCP client can use these values: | Field | Value | | --- | --- | | Command | `npx` | | Arguments | `-y`, `@tokenlabai/mcp-server@0.6.26` | | Environment | `TOKENLAB_MCP_TOOL_PROFILE=catalog` | ## Enable paid tools Create an API key in [Console → API keys](https://tokenlab.sh/dashboard/api), then set both variables in the MCP server environment: ```json { "env": { "TOKENLAB_API_KEY": "", "TOKENLAB_MCP_TOOL_PROFILE": "core" } } ``` Use your client's secret input when it has one. If a real key appears in a shared file, screenshot, log, or shell history, revoke it and create a new key. ## Check the connection Restart or reload the MCP client, approve the local server if prompted, and confirm that `tokenlab` is connected. ```bash # Claude Code claude mcp list # Codex codex mcp list ``` Ask the client to call `list_models`. A non-empty list confirms the package started and reached TokenLab. The `catalog` profile does not need an API key. ## Useful tools Tool availability depends on the selected profile. Common tasks include: - list models and read one model's capabilities - read current TokenLab prices or compare several models - send Chat Completions, Responses, Anthropic Messages, or Gemini requests - evaluate typed decisions with `evaluate_decisions` - create or edit images - create video, music, 3D, speech, transcription, or translation - upload and retrieve files - create embeddings or rerank documents - check and cancel supported async tasks The client should ask for approval before a paid call when price or model choice has not already been confirmed. The MCP file tools use the OpenAI-compatible `/v1/files` API. Gemini Files, resumable uploads, and `cachedContents` currently require HTTP calls and are not included in the MCP profiles. See [Gemini Files and cache](/guides/gemini-files-cache). ## Decision models Use `core` or `full` to call a [System One decision model](/api-reference/systemone/create-decision). First call `list_models` with `{"category":"decision"}` and `get_model` with the selected model ID. Keep the agent's main chat model configured separately. For Jev 1.13, call the tool with native `state` and `questions`: ```json { "name": "evaluate_decisions", "arguments": { "model": "jev-1.13", "state": { "ticket": "Please refund the duplicate payment." }, "questions": { "refund_requested": { "type": "noul", "instructions": "Does the customer explicitly request a refund?" } } } } ``` Check `isError`, then read `structuredContent.answers` and `structuredContent.usage`. A Noul answer is a probability number, not a Boolean. Preserve the request ID from `_meta` when available. The tool returns a synchronous result; it does not use the async task polling flow. With the default 120,000 ms server request timeout, allow at least 150,000 ms for the client tool call. If you change `TOKENLAB_REQUEST_TIMEOUT_MS`, keep the client timeout longer. A timeout leaves the outcome uncertain; inspect the request before resubmitting a paid call. Validate decisions against your own labeled cases before using them to drive actions. ## Async media Video, music, and 3D tools return a task instead of a finished file. Image tools can return either a completed result or a task, depending on the model. When a result includes an async `delivery`, call `get_task_status` with its task ID until the status is complete or failed. Do not create a second task because one status check timed out. ## Optional settings | Variable | Default | Use | | --- | --- | --- | | `TOKENLAB_API_BASE` | `https://api.tokenlab.sh` | Custom TokenLab API host; omit the trailing slash | | `TOKENLAB_MCP_TOOL_PROFILE` | `core` | `catalog`, `core`, or `full` | | `TOKENLAB_REQUEST_TIMEOUT_MS` | `120000` | Request timeout in milliseconds | | `TOKENLAB_MCP_MAX_FILE_BYTES` | `104857600` | Maximum local upload size per file | | `TOKENLAB_ARTIFACT_DIR` | OS temporary directory | Where large downloaded files are saved | Use the defaults unless your client or deployment has a specific requirement. ## Troubleshooting #### The client cannot start the server Run `node --version`, `npx --version`, and `npx -y @tokenlabai/mcp-server@0.6.26` in a terminal. A successful stdio server waits silently for MCP messages; press **Ctrl+C** after confirming it starts. #### The server is connected but no tools appear Restart the client and approve the local server. Confirm that the server and its tools are enabled in the client's MCP settings. #### The model list is empty Confirm that the machine can open `https://api.tokenlab.sh/v1/models`. If `TOKENLAB_API_BASE` is set, remove any trailing slash. #### A paid tool returns an authentication error Make sure `TOKENLAB_API_KEY` is in the MCP server environment, not in the prompt or tool arguments. Restart the client after changing it. #### The client loads too many tools Use `catalog` for model and price lookup, or `core` for common TokenLab calls. Choose `full` only when you need its additional APIs. ## Hosted model explorer Clients that support Streamable HTTP can use the public model explorer at: ```text https://exposure-tokenlab-openai-apps-model.vercel.app/mcp ``` Use the local npm server for paid API calls, local file uploads, or the `core` and `full` profiles. ## Links - [TokenLab MCP page](https://tokenlab.sh/mcp) - [GitHub repository](https://github.com/hedging8563/tokenlab-mcp-server) - [TokenLab API skill](/integrations/coding-agent-skill) - [Model catalog](https://tokenlab.sh/en/models) - [API quickstart](/quickstart) Configure task notifications with the [Webhook management API](/guides/webhooks), authenticated with an `mt-…` Management Token. MCP `full` uses `TOKENLAB_MANAGEMENT_TOKEN`. Stop polling terminal tasks and on 401/403/404 or non-retryable errors. --- ### DeepSeek Harness > Configure a TokenLab model in Harness, verify a request, and choose MCP, Skills, or the provider bundle URL: https://tokenlab.sh/docs/en/integrations/deepseek-harness ## Choose the connection you need To use TokenLab as the agent's main model, add a **custom provider** in the Harness Web UI. This works independently of the optional TokenLab provider bundle. Add [MCP tools](/integrations/tokenlab-mcp-server) for media and other APIs, or a [Skill](/integrations/coding-agent-skill) for API instructions; neither changes the main model by itself. Harness is a developer preview. These Web UI steps were checked against the official documentation and the published `@deepseek-ai/dsh` `0.1.5-rc.3` package on September 27, 2026. The bundle instructions below target this version; check compatibility with other Harness releases separately. ## Give this task to your agent ```text Read https://tokenlab.sh/docs/en/integrations/deepseek-harness and check my installed version and operating system. Confirm whether I need TokenLab as the main model, MCP tools, or API Skills. Preserve existing accounts, provider configuration, and permissions; explain how to restore changed settings. Keep API keys local; do not ask me to paste them into chat. I will enter the key locally and perform required UI steps. Explain the cost of a small verification request. Only after I explicitly choose that test, help run it and match the TokenLab request record. ``` ## Start Harness Use the Node CLI on macOS, Linux, or Windows with a supported Node release; Node 24 LTS is a suitable starting point. From your project directory, run this in a terminal or PowerShell: ```bash node --version npx @deepseek-ai/dsh@0.1.5-rc.3 web ``` Open the local URL printed by the command. In a fresh Web UI, use **Choose workspace** to add and select your project directory before sending a message. These commands use the `web` profile; the Electron application's `desktop` profile is not managed by this CLI workflow. See the official [startup guide](https://github.com/deepseek-ai/deepseek-harness) and [Web UI guide](https://deepseek-harness.github.io/deepseek-harness/en/guide/quickstart). ## Configure one TokenLab provider 1. Open **Settings → Models → Add a custom provider**. Preserve any existing providers and permissions. 2. Enter a lowercase Provider ID, such as `tokenlab-chat`, and choose one protocol and its matching Base URL below. Each provider uses one protocol. 3. Enter your TokenLab API key in the local form. Harness stores UI-managed keys in `$DSH_HOME/.credentials.yaml`; the settings document holds a reference. Do not send the key in chat or commit it. 4. Add one current model ID from [TokenLab Models](https://tokenlab.sh/en/models). Check its `tokenlab.accepted_request_formats` in the [model detail API](/api-reference/models/get-model), rather than guessing support from its name. 5. Save the provider, select its model, and start a **new session**. An existing session that has already sent a request retains its recorded model. | Provider ID example | Harness API protocol | Base URL | Required public request format | | --- | --- | --- | --- | | `tokenlab-chat` | `openai-completions` | `https://api.tokenlab.sh/v1` | `openai_chat_completions` | | `tokenlab-responses` | `openai-responses` | `https://api.tokenlab.sh/v1` | `openai_responses` | | `tokenlab-messages` | `anthropic-messages` | `https://api.tokenlab.sh` | `anthropic_messages` | For a first text request, a currently available `gpt-4.1-mini` entry can use the Chat row. **Fetch available models → Add selected** can help populate a custom provider, but listing success does not verify protocol support or a billable request; save the provider afterward. Enter the ID manually if discovery is unavailable. Harness does not offer a Gemini-native custom-provider protocol here. Use Chat only when that model's details list Chat Completions as an accepted request format. Image input and reasoning controls can need additional `settings.yaml` fields; follow [Harness provider configuration](https://deepseek-harness.github.io/deepseek-harness/en/guide/providers) and the selected model's supported fields before enabling them. ## Verify one small request In the new session, send: ```text Reply only with TOKENLAB_CONNECTION_OK. Do not use tools or modify files. ``` This request is billable. Check the reply and the matching model, time, and status in [TokenLab Requests](https://tokenlab.sh/dashboard/runs?section=requests). Stop after this text check; testing three protocols or generating paid media is not required to establish the first connection. A model listing or MCP discovery alone does not validate the key's generation access. ## Optional: the TokenLab bundle [`@tokenlabai/dsh-provider@0.1.6`](https://www.npmjs.com/package/@tokenlabai/dsh-provider/v/0.1.6) targets Harness `0.1.5-rc.3`. Use the native custom-provider steps above to configure one model, or install the bundle for its preset model routes and tools. The bundle contains a **fixed snapshot of 145 public chat models checked on October 3, 2026**: Responses 28, Messages 11, and Chat 106. Each model appears on exactly one route. It pins `@tokenlabai/mcp-server@0.6.25` and includes the separate `tokenlab_wait_task` tool. Installing this version does not refresh the model catalog. Check IDs against the live catalog and add newer models through a custom provider when needed. For an existing compatible Harness installation, first ensure `pnpm` is on `PATH`. Use the same `dsh` executable/version and profile for installation and startup; if you launch through `npx`, replace `dsh` below with that same versioned launcher: ```bash dsh --version pnpm --version dsh plugin --profile web add --workspace-root @tokenlabai/dsh-provider@0.1.6 ``` Before starting that profile, set the key in the launch environment or its `.env` file: ```dotenv TOKENLAB_API_KEY=sk-your-tokenlab-key ``` Harness reads `.env` in the directory where you launch it and in `$DSH_HOME` (normally `~/.dsh`); an inherited environment variable takes precedence. Selecting a workspace later does not select a different `.env`. Keep that file out of git and restart after changing it. A key saved for a custom provider in the UI does not automatically supply this bundle's `TOKENLAB_API_KEY`. Restart the same profile, then inspect its model and tool lists. For `headless`, install into and launch `headless` instead of `web`; installing into one profile does not configure the other. Harness `0.1.5-rc.3` merges saved `llm-pi-ai.providers` by provider key. Providers with different keys coexist. Saved `tokenlab-responses`, `tokenlab-messages`, or `tokenlab-chat` entries override the corresponding bundled route; review those entries when upgrading an older catalog. Preserve the other providers and models in `$DSH_HOME/settings.yaml`; do not replace the whole settings document with the Cordis patch. Public model details identify reasoning capability but do not enumerate supported effort values per model. The bundle therefore leaves `reasoningEfforts` undeclared. Harness does not offer effort levels for these custom routes; this does not disable server-side reasoning. If you configure `reasoningEfforts` yourself, use only separately verified values on the model entry in its provider’s `models` list and preserve the other models. Reasoning capability alone does not establish support for `xhigh` or `max`. The bundle defaults to `TOKENLAB_MCP_TOOL_PROFILE=core` with 32 MCP tools and `TOKENLAB_MCP_SCHEMA_MODE=portable`. Choose `catalog` for discovery only (6 tools), or `full` for all 89 tools, including additional response lifecycle, batch, Seedance asset/group, and worlds operations. The separate `tokenlab_wait_task` poller remains available in every profile and is not included in these MCP counts. `TOKENLAB_API_BASE` and `TOKENLAB_ANTHROPIC_BASE_URL` default to `https://api.tokenlab.sh`; `TOKENLAB_OPENAI_BASE_URL` defaults to `https://api.tokenlab.sh/v1`. For bundle media tools, inspect `delivery.mode`: consume `complete` output directly; pass an `async` result's `delivery.task_id` to `tokenlab_wait_task` and read its terminal `status`, `response`, and `result_urls`. A wait timeout is not a completed task. Keep approvals for billable or destructive tools. See [async tasks](/guides/async-jobs-polling). To remove the bundle, use the same launcher and profile, then restart: ```bash dsh plugin --profile web remove --workspace-root @tokenlabai/dsh-provider ``` ## If it fails - **The composer is disabled:** select both a workspace and a model. - **`MISSING_CREDENTIAL` or 401:** check the selected provider's credential. For bundle tools, check `TOKENLAB_API_KEY` in the launch environment; a UI-saved model key is a separate setting. - **`UNKNOWN_MODEL` or a retired ID:** check the live catalog, configure the current exact ID, then create a new session. Reinstalling bundle `0.1.6` does not update its snapshot. - **The URL is reachable but generation fails:** compare the protocol, Base URL, and model's accepted formats. Do not remove history, tools, or image input merely to turn an error into a successful check. - **`dsh` or `pnpm` is missing:** use the versioned `npx` launcher above for Harness; install [pnpm](https://pnpm.io/installation) before using plugin commands. A plugin installation failure is not an API-key failure. ## MCP and Skills are separate choices A manual custom provider does not install tools. Follow the [TokenLab MCP guide](/integrations/tokenlab-mcp-server) if you need callable API tools without the bundle, and check discovery before any generation. For [TokenLab Skills](/integrations/coding-agent-skill), keep the complete skill folder at `.dsh/skills/tokenlab-api-integration/` under the project root, including `SKILL.md` and referenced files. Harness also discovers `.agents/skills/`. These instructions do not install a provider, MCP server, or key. The official [filesystem Skills reference](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/skill/skill-filesystem/README.md) defines the supported locations. ## Jev / System One & Webhooks Use `mcp__tokenlab__evaluate_decisions` in `core` or `full` for Jev (`POST /v1/systemone`). Discover `category=decision` and inspect the model first. These are synchronous decisions, not chat models or asynchronous tasks; do not use the model picker or `tokenlab_wait_task`. Webhook management requires `full` and a separate `TOKENLAB_MANAGEMENT_TOKEN=mt-...` in the launch environment, explicitly passed to MCP by the bundle. An inference key cannot substitute for it. Management tokens have workspace-level authority beyond webhooks; registering a webhook does not make Harness a receiver. --- ### GitHub Copilot CLI > Connect GitHub Copilot CLI to TokenLab with BYOK URL: https://tokenlab.sh/docs/en/integrations/github-copilot-cli ## Overview [GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/use-byok-models) can connect to any OpenAI Chat Completions-compatible endpoint. TokenLab works through Copilot CLI's BYOK environment variables. ## Install With Node.js 22 or later, install through npm: ```bash npm install -g @github/copilot@1.0.85 ``` On macOS or Linux, you can alternatively use Homebrew: ```bash brew install --cask copilot-cli ``` Verify the installation: ```bash copilot --version ``` ## Configure TokenLab Set the provider variables before starting Copilot CLI: Use the [Quickstart](/quickstart) terminal instructions for macOS, Linux, WSL, or Windows PowerShell to set `TOKENLAB_API_KEY` without putting the key in command history. Launch Copilot CLI from that terminal. For persistent use, use your local secret manager; do not append a plaintext key to your shell profile. These variables apply only to this launch; your saved account, default model and permission settings stay in place. Launch normally without the variables to use your previous setup. Do not persist them in your shell profile. #### macOS / Linux / WSL ```bash macOS / Linux / WSL COPILOT_PROVIDER_TYPE=openai COPILOT_PROVIDER_BASE_URL=https://api.tokenlab.sh/v1 COPILOT_PROVIDER_API_KEY="${TOKENLAB_API_KEY:?Set TOKENLAB_API_KEY first}" copilot --model kimi-k3 ``` #### Windows PowerShell ```powershell Windows PowerShell if (-not $env:TOKENLAB_API_KEY) { throw 'Set TOKENLAB_API_KEY first' } $previous = @{} $previous['COPILOT_PROVIDER_TYPE'] = [Environment]::GetEnvironmentVariable('COPILOT_PROVIDER_TYPE', 'Process') $previous['COPILOT_PROVIDER_BASE_URL'] = [Environment]::GetEnvironmentVariable('COPILOT_PROVIDER_BASE_URL', 'Process') $previous['COPILOT_PROVIDER_API_KEY'] = [Environment]::GetEnvironmentVariable('COPILOT_PROVIDER_API_KEY', 'Process') try { $env:COPILOT_PROVIDER_TYPE = 'openai' $env:COPILOT_PROVIDER_BASE_URL = 'https://api.tokenlab.sh/v1' $env:COPILOT_PROVIDER_API_KEY = $env:TOKENLAB_API_KEY copilot --model kimi-k3 } finally { foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name, $previous[$name], 'Process') } } ``` | Variable | TokenLab value | |---|---| | `COPILOT_PROVIDER_TYPE` | `openai` | | `COPILOT_PROVIDER_BASE_URL` | `https://api.tokenlab.sh/v1` | | `COPILOT_PROVIDER_API_KEY` | Your TokenLab API key | | `COPILOT_MODEL` | A live model ID, for example `kimi-k3` | GitHub requires the selected model to support both streaming and tool calling. `kimi-k3` exposes tool use and a context window larger than GitHub's recommended 128K minimum. ## Verify Inside Copilot CLI, ask for a read-only action first, such as: ```text Inspect this repository and summarize its top-level structure. Do not modify files. ``` You can also inspect provider help or switch the model for the current session: ```bash copilot help providers ``` ## Troubleshooting #### Authentication failed Confirm `COPILOT_PROVIDER_API_KEY` contains an active TokenLab key and restart `copilot` after changing environment variables. #### Model not found Use the exact ID from the [live TokenLab model catalog](https://tokenlab.sh/en/models). Do not add a provider prefix to `kimi-k3`. #### Tool calling or streaming error Copilot CLI rejects models without both capabilities. Choose a tool-capable chat model such as `kimi-k3`. > Warning: Do not commit API keys to shell scripts or dotfiles tracked by Git. Rotate any key that was exposed in terminal history, screenshots, or logs. Saving settings or listing models does not verify a key or a completed request. A prompt uses TokenLab balance. Check the reply and its matching [request record](https://tokenlab.sh/dashboard/runs?section=requests) before trying tools; auxiliary requests may also be billed. --- ### Cursor > Configure TokenLab for supported Cursor chat models, verify the connection, and add MCP or Skills separately URL: https://tokenlab.sh/docs/en/integrations/cursor ## What this connection does Use TokenLab for supported chat requests in the **Cursor desktop editor** on macOS, Windows, or Linux. Cursor documents OpenAI BYOK support for standard, non-reasoning chat models; Tab completion still uses Cursor's models. An OpenAI Base URL override does not turn Cursor into a client for every TokenLab model or protocol. See [Cursor's API-key limits](https://cursor.com/help/models-and-usage/api-keys). If you only need TokenLab image, video, or other API tools, keep your current chat provider and add [MCP](/integrations/tokenlab-mcp-server) instead. A [Skill](/integrations/coding-agent-skill) teaches the agent how to use the APIs; it does not change the chat provider. ## Give this task to your agent ```text Read https://tokenlab.sh/docs/en/integrations/cursor and check my installed version and operating system. Confirm whether I need TokenLab as the main model, MCP tools, or API Skills. Preserve existing accounts, provider configuration, and permissions; explain how to restore changed settings. Keep API keys local; do not ask me to paste them into chat. I will enter the key locally and perform required UI steps. Explain the cost of a small verification request. Only after I explicitly choose that test, help run it and match the TokenLab request record. ``` ## Open the correct settings 1. Install or update the desktop editor from [Cursor Downloads](https://cursor.com/downloads). This guide does not configure Cursor CLI or Cloud Agents. 2. Open **Cursor Settings** from the command palette, or press `Cmd/Ctrl + Shift + J`. `Cmd/Ctrl + ,` opens the general editor settings instead. 3. Open **Models** and note your existing OpenAI key and Base URL configuration so you can restore it. Keep other provider settings intact. 4. In **OpenAI API Key**, enter your TokenLab key locally and save it. Do not paste the key into an agent conversation or commit it to a project. 5. Enable **Override OpenAI Base URL** and enter: ```text https://api.tokenlab.sh/v1 ``` Cursor sends BYOK requests through its backend for prompt assembly; this is not a direct connection from your editor to TokenLab. ## Select a supported model Copy the exact model ID from the current [TokenLab catalog](https://tokenlab.sh/en/models), and check that Cursor accepts it through its OpenAI BYOK path. A model being listed as chat on TokenLab does not establish Cursor compatibility. For an initial text check, `gpt-4.1-mini` is an example of a non-reasoning OpenAI chat model; confirm it is still available before using it. Enable the existing entry in **Models**, or use **Add model** if it is absent, then select that exact model in the chat picker. Do not duplicate a built-in model under a different spelling, and do not assume Claude or Gemini entries use the OpenAI override. [Cursor's custom-model guidance](https://forum.cursor.com/t/add-custom-model-fail-no-models-available/163488) explains these routing limits. ## Verify one small request Start a new chat with the selected model and send: ```text Reply only with TOKENLAB_CONNECTION_OK. Do not use tools or modify files. ``` This is a billable model request. Check both the reply and the matching model, time, and status in [TokenLab Requests](https://tokenlab.sh/dashboard/runs?section=requests). A saved key, a visible model, or a reply without a matching TokenLab request does not confirm this route. ## If it fails - **The model is missing or rejected:** check the exact ID and Cursor's current BYOK support. Use an existing built-in entry when available; restarting alone cannot make an unsupported model compatible. - **Authentication fails:** re-enter the TokenLab key in the OpenAI field and check the Base URL. A balance or quota error is separate from an invalid key; follow the returned error and the corresponding TokenLab request. - **No TokenLab request appears:** confirm the selected model and override, then inspect Cursor's error. Tab completion and other Cursor-managed features are not a connection test. - **Another provider stops working:** restore the previous OpenAI settings or disable the override, then start a new chat. Avoid changing unrelated provider keys to repair this route. ## Add tools or instructions separately [TokenLab MCP](/integrations/tokenlab-mcp-server) adds callable tools. Configure it through Cursor's MCP settings or `.cursor/mcp.json`; first check tool discovery, then deliberately run any billable generation you need. Installing MCP does not replace the main chat model. [TokenLab Skills](/integrations/coding-agent-skill) supply API instructions. For a project-local installation, keep the complete skill directory at `.cursor/skills/tokenlab-api-integration/`, including `SKILL.md` and its referenced files. Check **Customize → Skills** for discovery. Skills do not supply a key or install an MCP server. See the official [MCP](https://cursor.com/docs/mcp) and [Skills](https://cursor.com/docs/skills) guides for your Cursor version. --- ### Cline > Configure Cline to use TokenLab models URL: https://tokenlab.sh/docs/en/integrations/cline ## Overview [Cline](https://docs.cline.bot/provider-config/openai-compatible) can use TokenLab through its OpenAI-compatible settings. ## Install Install **Cline** from your editor's extension marketplace, then open the Cline panel. Cline supports VS Code, Cursor, VSCodium, Windsurf, Antigravity, and JetBrains IDEs. ## Configure TokenLab Save the current provider, model and connection settings locally before editing. These controls change the active model selection; restore your previous values when finished. Keep signed-in accounts and tool permissions unchanged. Do not overwrite another OpenAI-compatible connection without a restorable backup. 1. Click the gear icon in the Cline panel. 2. Set **API Provider** to **OpenAI Compatible**. 3. Enter the following values: | Field | Value | |---|---| | Base URL | `https://api.tokenlab.sh/v1` | | API Key | Your TokenLab API key | | Model ID | `kimi-k3` | 4. Click **Verify**. For the `kimi-k3` example, Cline's optional model configuration can use: | Capability | Value | |---|---| | Context / input limit | `1048576` | | Max output tokens | `131072` | | Image support | Enabled | > Note: The model catalog changes over time. Use the [live TokenLab model catalog](https://tokenlab.sh/en/models) when selecting another model instead of copying a stale list. ## Verify After **Verify** succeeds, send a small read-only task before allowing edits: ```text List the files in this workspace and explain which one is the application entry point. Do not edit anything. ``` Saving settings or listing models does not verify a key or a completed request. A prompt uses TokenLab balance. Check the reply and its matching [request record](https://tokenlab.sh/dashboard/runs?section=requests) before trying tools; auxiliary requests may also be billed. ## Troubleshooting #### Invalid API key Re-copy the key from the TokenLab dashboard and confirm it is active. #### Model not found Enter the model ID exactly as shown by TokenLab, for example `kimi-k3`, without `kimi/` or another provider prefix. #### Connection error Confirm the Base URL ends in `/v1`. Do not enter `/chat/completions` in Cline's Base URL field. --- ### Claude Code > Use TokenLab with Anthropic's Claude Code CLI URL: https://tokenlab.sh/docs/en/integrations/claude-code ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and help me connect Claude Code to TokenLab: https://tokenlab.sh/docs/en/integrations/claude-code Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` ## Overview [Claude Code](https://code.claude.com/docs/en/setup) can use a TokenLab API key and the TokenLab Messages API for Claude models. ## Installation Anthropic now recommends the native installer. The npm package still works, but Anthropic has deprecated that installation method. #### macOS / Linux / WSL ```bash macOS / Linux / WSL curl -fsSL https://claude.ai/install.sh | bash ``` #### Windows ```powershell Windows irm https://claude.ai/install.ps1 | iex ``` If you specifically need the npm package, use: ```bash npm install -g @anthropic-ai/claude-code@2.1.263 ``` > Warning: Do not use `sudo npm install -g` for Claude Code. ## Configuration Use the [Quickstart](/quickstart) terminal instructions for macOS, Linux, WSL, or Windows PowerShell to set `TOKENLAB_API_KEY` without putting the key in command history. Launch Claude Code from that terminal. For persistent use, use your local secret manager; do not append a plaintext key to your shell profile. These variables apply only to this launch; your saved account, default model and permission settings stay in place. Launch normally without the variables to use your previous setup. Do not persist them in your shell profile. This session path uses Claude Code **2.1.263** and Bearer authentication through `ANTHROPIC_AUTH_TOKEN`. Keep the Base URL without `/v1`. Existing settings-file `env` values, credential helpers, cloud-provider switches or managed policies can affect authentication; inspect them before launch. If they conflict, use the setup helper below instead of deleting account data or weakening policies. Check `/status` for the selected model and permissions. #### macOS / Linux / WSL ```bash macOS / Linux / WSL ANTHROPIC_BASE_URL=https://api.tokenlab.sh ANTHROPIC_AUTH_TOKEN="${TOKENLAB_API_KEY:?Set TOKENLAB_API_KEY first}" ANTHROPIC_API_KEY= claude --model claude-sonnet-4-6 ``` #### Windows PowerShell ```powershell Windows PowerShell if (-not $env:TOKENLAB_API_KEY) { throw 'Set TOKENLAB_API_KEY first' } $previous = @{} $previous['ANTHROPIC_BASE_URL'] = [Environment]::GetEnvironmentVariable('ANTHROPIC_BASE_URL', 'Process') $previous['ANTHROPIC_AUTH_TOKEN'] = [Environment]::GetEnvironmentVariable('ANTHROPIC_AUTH_TOKEN', 'Process') $previous['ANTHROPIC_API_KEY'] = [Environment]::GetEnvironmentVariable('ANTHROPIC_API_KEY', 'Process') try { $env:ANTHROPIC_BASE_URL = 'https://api.tokenlab.sh' $env:ANTHROPIC_AUTH_TOKEN = $env:TOKENLAB_API_KEY $env:ANTHROPIC_API_KEY = '' claude --model claude-sonnet-4-6 } finally { foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name, $previous[$name], 'Process') } } ``` Optional: the [reversible Claude Code setup helper](https://github.com/hedging8563/tokenlab-skills/blob/247bb17c53754c8288d2d5cb00824fbd9f32899b/skills/tokenlab-api-integration/references/claude_setup.md) provides preview, apply and original-file restoration with Python 3.11+. TokenLab Skills plugin **0.1.2** includes these scripts; a standalone Skill installation can also be used after updating to a version that contains them. For an existing plugin or standalone Skill installation, follow the [README update instructions](https://github.com/hedging8563/tokenlab-skills/blob/247bb17c53754c8288d2d5cb00824fbd9f32899b/README.md#update-an-existing-installation) and confirm the script is present. The manual configuration here remains usable without the helper. Saving settings or listing models does not verify a key or a completed request. A prompt uses TokenLab balance. Check the reply and its matching [request record](https://tokenlab.sh/dashboard/runs?section=requests) before trying tools; auxiliary requests may also be billed. ## Usage After configuration, use Claude Code normally: ```bash # Start an interactive session ANTHROPIC_BASE_URL=https://api.tokenlab.sh ANTHROPIC_AUTH_TOKEN="${TOKENLAB_API_KEY:?Set TOKENLAB_API_KEY first}" ANTHROPIC_API_KEY= claude --model claude-sonnet-4-6 # Run a single command ANTHROPIC_BASE_URL=https://api.tokenlab.sh ANTHROPIC_AUTH_TOKEN="${TOKENLAB_API_KEY:?Set TOKENLAB_API_KEY first}" ANTHROPIC_API_KEY= claude --model claude-sonnet-4-6 "Explain this code" < main.py ``` Verify the installation if needed: ```bash claude --version claude doctor ``` ## Choose a model Copy a current Claude model ID from the [Models page](https://tokenlab.sh/en/models), then pass it with `--model`: ```bash ANTHROPIC_BASE_URL=https://api.tokenlab.sh ANTHROPIC_AUTH_TOKEN="${TOKENLAB_API_KEY:?Set TOKENLAB_API_KEY first}" ANTHROPIC_API_KEY= claude --model claude-sonnet-4-6 "Review this PR" ``` ## Extended Thinking For complex reasoning tasks, use models with extended thinking: ```bash ANTHROPIC_BASE_URL=https://api.tokenlab.sh ANTHROPIC_AUTH_TOKEN="${TOKENLAB_API_KEY:?Set TOKENLAB_API_KEY first}" ANTHROPIC_API_KEY= claude --model claude-opus-4-6 "Design a microservices architecture for..." ``` ## Troubleshooting #### Connection errors Verify the base URL is exactly `https://api.tokenlab.sh` (no `/v1` suffix for Anthropic-style requests). #### Invalid API key Ensure your TokenLab API key starts with `sk-` and has sufficient balance. #### Model not found Check the model name is correct. Use `claude-sonnet-4-6` format. --- ### Codex CLI > Configure OpenAI Codex CLI to use TokenLab API URL: https://tokenlab.sh/docs/en/integrations/codex-cli ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and help me connect Codex CLI to TokenLab: https://tokenlab.sh/docs/en/integrations/codex-cli Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` ## Overview Codex CLI can connect to TokenLab through `/v1/responses`. The selected model must list `openai_responses` in `tokenlab.accepted_request_formats`; features can vary by model. Remote compaction uses `POST /v1/responses/compact`. Codex sends the current session model in `body.model`, so that model must also support Responses. Do not configure `/v1/compact`. ## System Requirements Use the [official CLI installation guide](https://learn.chatgpt.com/docs/codex/cli) for macOS, Linux or Windows. Native Windows and WSL are separate environments; follow the [Windows guide](https://learn.chatgpt.com/docs/windows/windows-sandbox). The npm command below is one installation option, not a requirement for native binaries. ## Installation ```bash npm install -g @openai/codex@0.149.0 ``` Verify installation: ```bash codex --version ``` ## Configuration ### Step 1: Set API Key Use the [Quickstart](/quickstart) terminal instructions for macOS, Linux, WSL, or Windows PowerShell to set `TOKENLAB_API_KEY` without putting the key in command history. Launch Codex from that terminal. For persistent use, use your local secret manager; do not append a plaintext key to your shell profile. ### Step 2: Configure tokenlab.config.toml Use a separate `tokenlab.config.toml` in your active `CODEX_HOME` (normally `~/.codex`) with Codex **0.149.0**. Create it only if the name is unused, or back it up before editing an existing TokenLab profile. The profile below is loaded explicitly with `codex --profile tokenlab`; it leaves the default `config.toml`, `auth.json`, accounts and permission policies unchanged. Do not use a legacy `[profiles.tokenlab]` table. Start without the profile to return to your ordinary setup. See the [official profile configuration](https://learn.chatgpt.com/docs/config-file/config-advanced#profiles). ```toml model_provider = "tokenlab" model = "gpt-5.6-sol" model_reasoning_effort = "xhigh" [model_providers.tokenlab] env_key = "TOKENLAB_API_KEY" name = "TokenLab" base_url = "https://api.tokenlab.sh/v1" wire_api = "responses" supports_websockets = true websocket_connect_timeout_ms = 15000 ``` This WebSocket mode is a Responses-over-WebSocket bridge for Codex clients. It accepts only official `response.create` events; `stream` is implicit, and `background` and `response.cancel` are not available on this transport. It is not the OpenAI Realtime API and does not accept `session.update`, `conversation.item.*`, `input_audio_buffer.*`, binary audio, or nested Realtime `response.create.response` envelopes. > Note: Create the chosen configuration file if it does not exist. After editing it, start a new session with the matching command below. Current Codex uses `wire_api = "responses"`. Do not configure `chat/completions`. Keep `requires_openai_auth` unset or false for this custom provider so `env_key` is used. > Note: Responses requests are not converted to Chat Completions. Optional and future fields depend on the selected model. ## Basic Usage **Start interactive mode:** ```bash codex --profile tokenlab ``` **Direct command:** ```bash codex --profile tokenlab "Fix the bug in main.py line 42" ``` **Specify model:** ```bash codex --profile tokenlab -m gpt-5.6-sol "Build a REST API server" ``` ## Choose a model Use a model that currently lists `openai_responses` in `tokenlab.accepted_request_formats`. Compare candidates on your own coding tasks and check current prices on the [Models page](https://tokenlab.sh/en/models). ## Interactive Commands | Command | Description | |---------|-------------| | `/help` | Display help | | `/exit` or `Ctrl+C` | Exit | | `/clear` | Clear conversation | | `/status` | Show the active model, permissions, and token usage | | `/model ` | Switch model | ## Verify Configuration Check whether the environment variable is set without printing its value. This check does not validate the key: ```bash if [ -n "${TOKENLAB_API_KEY:-}" ]; then printf '%s\n' 'TOKENLAB_API_KEY is set' else printf '%s\n' 'TOKENLAB_API_KEY is not set' fi ``` Start Codex and run `/status` to check the active model and permissions. If you use a profile, start that same profile. See the [official status command](https://learn.chatgpt.com/docs/developer-commands?surface=cli#inspect-the-session-with-status). When you are ready to make a real request, run the command below. It uses your TokenLab balance. Check the reply and the matching record in [Requests](https://tokenlab.sh/dashboard/runs?section=requests); an environment check or successful startup alone does not prove a request succeeded. ```bash codex --profile tokenlab "Reply only with OK. Do not use tools or modify files." ``` ## Common Use Cases **Code review:** ```bash git diff | codex --profile tokenlab "Review these code changes" ``` **Generate commit messages:** ```bash git diff --staged | codex --profile tokenlab "Generate a commit message for these changes" ``` **Fix errors:** ```bash codex --profile tokenlab "Fix the TypeScript errors in src/components/" ``` **Explain code:** ```bash cat main.py | codex --profile tokenlab "Explain what this code does" ``` ## Troubleshooting #### Connection Error - Verify `base_url` in tokenlab.config.toml is exactly `https://api.tokenlab.sh/v1` - Check network connectivity - Ensure no proxy interference #### Authentication Failed After upgrading to Codex 0.149.0 or later, `API_KEY_REQUIRED` or `401 Unauthorized` can occur if a custom provider relies only on `auth.json` while `requires_openai_auth` is unset or `false`. This guide reads the key from `TOKENLAB_API_KEY` through `env_key` and does not rely on `auth.json`. Keep `requires_openai_auth` unset or `false`; this setup does not require changing it to `true` or overwriting `auth.json`. - Verify that the active configuration or profile contains `env_key = "TOKENLAB_API_KEY"`. If you saved the settings in `tokenlab.config.toml`, launch with `codex --profile tokenlab`. - Verify that `TOKENLAB_API_KEY` is set in the terminal used to launch Codex, then restart Codex from that terminal. - Check that the key starts with `sk-` - Ensure the key is active in TokenLab dashboard #### Model Not Found - Check model name matches exactly - Verify model availability at [tokenlab.sh/en/models](https://tokenlab.sh/en/models) #### Responses-native field rejected - Optional `/v1/responses` fields depend on the selected model - If you see `unsupported_request_field`, remove that field or switch to a workflow that does not depend on it #### /compact or auto-compact fails - Codex CLI calls `POST /v1/responses/compact`, not `/v1/compact` - The compaction request uses the current session `model`, so that model must be available on the Responses path - Keep `wire_api = "responses"` and `base_url = "https://api.tokenlab.sh/v1"` --- ### OpenCodex > Connect Codex to TokenLab through OpenCodex with model-specific API routing URL: https://tokenlab.sh/docs/en/integrations/opencodex ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read https://tokenlab.sh/docs/en/integrations/opencodex and help me connect OpenCodex to TokenLab. Check my installed OpenCodex and Codex versions, active configuration, and running proxy first. Preserve existing accounts, providers, models, and permissions. Back up files before changing them. Have me enter the API key locally; never ask for, print, or paste it in chat. Use the TokenLab preset and the selected model's documented API format. Check configuration loading first. Explain the cost before running a small real request. Match the reply with its TokenLab request record. ``` ## How the connection works [OpenCodex](https://github.com/lidge-jun/opencodex) is a local proxy between Codex and model APIs. This guide uses **OpenCodex 2.73.0** and **Codex CLI 0.149.0**. The TokenLab preset's Chat Completions integration was released and verified end to end in 2.72.0. [Version 2.73.0](https://github.com/lidge-jun/opencodex/releases/tag/v2.73.0) keeps that integration and adds model-specific Responses and Anthropic Messages routing. The routes listed below have been checked against TokenLab with streamed text and function-call/result round trips. Codex sends Responses requests to the **local OpenCodex proxy**. OpenCodex then sends the selected model's request to **TokenLab**. A Responses request on the local connection does not mean that the model is called through TokenLab's Responses API. | Models selected in Codex | OpenCodex → TokenLab | | --- | --- | | `gpt-6-astra`, `gpt-6.1-sol`, `gpt-6-sol`, `gpt-6-luna`, `grok-4.7`, `deepseek-v4.1-flash`, `deepseek-v4-pro`, `kimi-k3`, `glm-5.3` | Responses: `POST /v1/responses` | | `claude-opus-5`, `claude-opus-5-5`, `claude-sonnet-5`, `claude-sonnet-5-5`, `claude-fable-5`, `claude-fable-5-1` | Anthropic Messages: `POST /v1/messages` | | `gemini-3.8-flash` | Chat Completions: `POST /v1/chat/completions` | The preset's Base URL is `https://api.tokenlab.sh/v1`. Keep it unchanged: OpenCodex builds the matching Messages or Responses URL itself. Models outside the listed Responses defaults and Claude routing use the preset's Chat adapter. TokenLab supports Gemini's native API for models that declare it, but **the OpenCodex 2.73.0 TokenLab preset calls Gemini through Chat Completions**. Do not change the entire provider to Responses or Gemini; that would also change requests for models that do not accept that format. ## Install or update Use Node.js 18 or later for the npm installation: ```bash npm install -g @bitkyc08/opencodex@2.73.0 ocx --version codex --version ``` Codex must also be installed. OpenCodex provides [installation](https://opencodex.me/getting-started/installation/) and [Codex connection instructions](https://opencodex.me/getting-started/quickstart/). Native Windows and WSL have separate configurations; run setup and Codex in the same environment. ## Add TokenLab Create a key in [TokenLab API Keys](https://tokenlab.sh/dashboard/api?tab=keys). For terminal setup, follow [Quickstart](/quickstart) to set `TOKENLAB_API_KEY` without putting its value in command history. Start OpenCodex from that terminal; a background service needs the variable in its own launch environment. For a **fresh OpenCodex installation**, run `ocx init`, select TokenLab, and enter the key locally or use the literal environment reference `${TOKENLAB_API_KEY}`. Review the wizard's Codex connection and autostart choices before applying them. For an **existing installation**, add the preset without replacing other providers: ```bash ocx provider add tokenlab --api-key '${TOKENLAB_API_KEY}' ``` The single quotes save an environment-variable reference, not the key's value. This command also works in PowerShell. If `tokenlab` already exists, edit that provider in the dashboard instead of overwriting it with `--force`. You can also choose **TokenLab** in the dashboard's **Add provider** list and enter the key there. OpenCodex saves its configuration under `$OPENCODEX_HOME/config.json`, normally `~/.opencodex/config.json`. Start the proxy if it is not already running, then open its dashboard: ```bash ocx start ocx status ocx gui ``` In the provider page, check the Base URL and discovered models. The preset discovers `GET /v1/models?category=chat` and keeps models with `tool-use` capability. Images, video, audio, embeddings, and decision models are excluded. Discovery with a key reflects that key's model permissions and delivery policy. Run `ocx sync` to connect Codex and refresh its model catalog, then start a new Codex session. This changes Codex's proxy connection and catalog; review existing custom-provider settings and keep a backup before syncing. It does not require replacing your account or permission policy. ## Select a model and verify a request Choose the `tokenlab/` entry in Codex's model picker, or select it for one CLI launch: ```bash codex -m "tokenlab/gpt-6.1-sol" ``` OpenCodex uses `tokenlab/` to select the provider; the model ID sent to TokenLab is `gpt-6.1-sol`. Choose an exact currently available ID from [Models](https://tokenlab.sh/en/models). For a small connection check, send: ```text Reply only with TOKENLAB_CONNECTION_OK. Do not use tools or modify files. ``` This request uses your TokenLab balance. Check the reply and its matching model, time, and status in [Requests](https://tokenlab.sh/dashboard/runs?section=requests). Provider discovery and successful startup alone do not verify inference access. Streaming and function calling work on the routes in the table. Image input and thinking controls depend on the selected model: check its capabilities, and use only the effort choices OpenCodex offers for that model. Image input and thinking requests were checked on representative models for the Responses and Messages routes; Gemini image input was checked on its Chat route. A model that supports thinking does not necessarily expose thinking text or support every effort level. ## Keep a Responses model on Chat Completions To use the Chat path for one of the listed Responses models, merge a `modelAdapters` entry into the **existing** `providers.tokenlab` object in OpenCodex's configuration. This example keeps `gpt-6-astra` on Chat for Codex: ```json { "modelAdapters": { "gpt-6-astra": "openai-chat" } } ``` This is a provider-field example, not a replacement for the full configuration. Preserve other model overrides, credentials, and providers, then restart the proxy and open a new Codex session. Removing only this model entry restores its Responses default. Claude's Messages routing is tied to the canonical TokenLab endpoint. The Chat override above applies to the Responses defaults; it does not switch Claude to Chat. For a Chat-native client, the listed Responses models already use Chat without this override. See [OpenCodex provider routing](https://opencodex.me/guides/providers/#3-api-key-catalog). ## Delivery policy and other TokenLab tools The preset does not force an `X-TokenLab-Delivery-Policy` header. TokenLab uses your API key's delivery-policy default. Choosing Chat, Responses, or Messages is separate from choosing a delivery policy; see [TokenLab provider settings](/guides/tokenlab-provider). Add the [TokenLab MCP server](/integrations/tokenlab-mcp-server) for other API tools, or [TokenLab Skills](/integrations/coding-agent-skill) for integration instructions. These do not change the main model's provider or API format. **JEV Auto in OpenCodex 2.73.0 uses TypeSafe's decision backend.** It does not offer TokenLab as that backend. Calling TokenLab's [System One API](/api-reference/systemone/create-decision) through MCP is a separate operation; do not put a TokenLab key in the TypeSafe credential field. ## Troubleshooting and restoring your setup - **TokenLab is missing from the picker:** check `ocx --version`; this guide targets 2.73.0. Refresh the catalog with `ocx sync` and start a new Codex session. - **401 or missing credentials:** check the key is active and available to the process running OpenCodex. An environment variable in a different terminal does not update an already running service. - **Model is missing:** check the exact ID, your key's permissions, and current availability. Non-chat models and chat models without `tool-use` are not included in this preset. - **Unsupported request or wrong endpoint:** compare the selected model with the routing table and inspect saved adapter overrides. The required format is `tokenlab.accepted_request_formats` in [model details](/api-reference/models/get-model); the model list is not a substitute for that detail field. Claude and Gemini must not be sent to Responses merely because Codex uses Responses locally. - **Tools or image input fail:** keep the original error and Request ID. Check the model's capabilities and the active OpenCodex route before changing settings; do not remove conversation history or tool results to hide an error. To stop routing Codex through the proxy, use `ocx stop`; OpenCodex stops the proxy and restores the native Codex connection. `ocx restore` restores the native connection while keeping the proxy running for other clients. Review the [OpenCodex CLI reference](https://opencodex.me/reference/cli/) before changing an installation shared by other clients. --- ### WorkBuddy > Add TokenLab as a custom model provider in WorkBuddy URL: https://tokenlab.sh/docs/en/integrations/workbuddy ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and help me connect WorkBuddy to TokenLab: https://tokenlab.sh/docs/en/integrations/workbuddy Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` ## Overview [WorkBuddy](https://www.codebuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/Model) can add TokenLab through its visual custom-model editor. ## Configure TokenLab 1. Open **Settings → Model**. 2. Add a model and select **Custom** as the provider. 3. Enter the following values: | Field | Value | |---|---| | Endpoint URL | `https://api.tokenlab.sh/v1` | | API Key | Your TokenLab API key | | Model name | A current TokenLab chat model ID | | Custom Protocol | **Off** | > Warning: Keep **Custom Protocol** off. In standard mode WorkBuddy validates the base URL and appends `/chat/completions`. If you turn it on while entering only `https://api.tokenlab.sh/v1`, requests are sent to the wrong path. ## Advanced model settings Choose capability flags and context/output limits from the selected model’s current TokenLab details. Do not enable image input or reasoning for every model, and do not copy a 1M/128K limit to a different model. Use the model ID exactly as shown by TokenLab. Saving and selecting a model confirms only local configuration. A text or tool task uses TokenLab balance; compare its reply with the matching [request record](https://tokenlab.sh/dashboard/runs?section=requests). To undo setup, select your previous model and remove only the TokenLab entry in the editor. ## Verify Start with a small task that exercises both text generation and a safe tool read: ```text Inspect this folder, list its top-level files, and summarize what the project does. Do not modify files. ``` ## Troubleshooting #### 404 or route not found Confirm the endpoint is `https://api.tokenlab.sh/v1` and **Custom Protocol** is off. Do not paste `/chat/completions` when standard mode is selected. #### Model not found Copy the exact model ID from TokenLab and check that it supports Chat Completions and tools. #### Agent replies but cannot use tools Enable **Tool calling** and make sure the selected TokenLab model advertises tool-use support. > Warning: Screenshots of the model dialog can reveal endpoints or API keys. Mask credentials before sharing them and rotate any exposed key. The official desktop app is available for macOS and Windows. Use the [official model editor](https://www.codebuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/Model); this guide does not assume a Linux build or a writable configuration-file schema. MCP and Skills have separate installation paths: [API tools](/integrations/tokenlab-mcp-server) and [integration instructions](/integrations/coding-agent-skill). They do not change WorkBuddy’s main model. --- ### Hermes Agent > Connect Hermes Agent to TokenLab URL: https://tokenlab.sh/docs/en/integrations/hermes-agent Use TokenLab as a named model provider in Hermes while keeping your existing accounts and tool permissions. ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and help me connect Hermes to TokenLab: https://tokenlab.sh/docs/en/integrations/hermes-agent Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` ## Install or check Hermes Follow the [official installation guide](https://hermes-agent.nousresearch.com/docs/getting-started/installation). It offers desktop installers for Apple Silicon macOS and Windows, and a CLI path for Linux and native Windows. Check the requirements for your system; skip installation if Hermes is already installed. ## Optional: reversible setup Use the Hermes helper included in TokenLab Skills **0.1.3** for official Hermes **0.21.3**. It previews and backs up the change, adds only a named provider, and preserves YAML comments, accounts, the default model and permissions. Read the [setup reference](https://github.com/hedging8563/tokenlab-skills/blob/900de0c4e296c0d2ed00b5d842815ed89c403503/skills/tokenlab-api-integration/references/hermes_setup.md) first. Set `SKILL_DIR` to the installed `tokenlab-api-integration` folder and `HERMES_PYTHON` to the Python in the Hermes installation shown by `hermes --version`: `venv/bin/python` on macOS/Linux or `venv\Scripts\python.exe` on Windows. PowerShell uses `$SkillDir` and `$HermesPython`. Use that existing environment; the helper does not install global Python packages. ```bash "$HERMES_PYTHON" "$SKILL_DIR/scripts/configure_hermes.py" --model gpt-5.6-luna "$HERMES_PYTHON" "$SKILL_DIR/scripts/configure_hermes.py" --model gpt-5.6-luna --apply ``` ```powershell & $HermesPython "$SkillDir/scripts/configure_hermes.py" --model gpt-5.6-luna & $HermesPython "$SkillDir/scripts/configure_hermes.py" --model gpt-5.6-luna --apply ``` After applying, skip the manual merge below. Follow the helper’s printed home and loading commands; it detects `active_profile`. Configuration inspection does not validate the key. Preview restoration with `--restore`, then add `--apply` to restore the original bytes. Conflicts, later edits, managed settings and WSL stop automatic setup; retain those settings and use the manual route. ## Configure a named provider Find the active Hermes directory: `HERMES_HOME` when set, otherwise `~/.hermes` (Windows: `%LOCALAPPDATA%\hermes`). Back up `config.yaml`, then merge the following entries without replacing other settings. The example uses Chat Completions; choose a current model that supports this format and tool calling. Back up the active configuration. Add only the TokenLab entry and preserve existing providers, accounts, default-model selection and permissions. If the name is already used, choose another name and update the commands. To undo setup, remove only your added entry or restore its previous backup. ```yaml providers: tokenlab: api: https://api.tokenlab.sh/v1 key_env: TOKENLAB_API_KEY transport: chat_completions default_model: gpt-5.6-luna ``` Create a key in [API keys](https://tokenlab.sh/dashboard/api?tab=keys). Enter it locally as `TOKENLAB_API_KEY` in the Hermes directory’s `.env` or in the environment of the process that starts Hermes. Never paste the key into a conversation or put it in the provider YAML. ## Check configuration, then make a request Check the added provider with `hermes config get providers.tokenlab.api`; it should show `https://api.tokenlab.sh/v1`. Keep the existing `model` section unchanged. Start a new session with the explicit provider and model below; launch normally without these flags to use your previous default. Configuration inspection does not validate the key. ```bash hermes chat --provider custom:tokenlab --model gpt-5.6-luna ``` When ready to spend TokenLab balance, send a short text request. Check the response and its matching record in [Requests](https://tokenlab.sh/dashboard/runs?section=requests). Only then try a read-only tool task. Auxiliary models and optional tools can have separate providers or keys. ## Restore or troubleshoot If the provider is unknown, check the installed version and the [current provider format](https://hermes-agent.nousresearch.com/docs/integrations/providers). For authentication errors, check the local environment and key status without printing the key. To undo setup, restore the previous model selection and remove only the TokenLab entries you added. MCP and Skills are separate extensions: use the [MCP guide](/integrations/tokenlab-mcp-server) for API tools or the [Skill guide](/integrations/coding-agent-skill) for integration guidance. Neither is required to select this provider. --- ### Kilo Code > Connect Kilo Code and Kilo CLI to TokenLab URL: https://tokenlab.sh/docs/en/integrations/kilo-code ## Overview [Kilo Code](https://kilo.ai/docs/ai-providers/openai-compatible) supports custom OpenAI-compatible providers in its VS Code extension and Kilo CLI. Both clients share the same provider and model configuration. ## Install For VS Code, search for **Kilo Code** in Extensions and install the pre-release channel. Kilo documents that channel as the current stable, recommended extension. For the CLI, run: ```bash npm install -g @kilocode/cli@7.7.2 kilo --version ``` ## Configure in the UI Save the current provider, model and connection settings locally before editing. These controls change the active model selection; restore your previous values when finished. Keep signed-in accounts and tool permissions unchanged. Do not overwrite another OpenAI-compatible connection without a restorable backup. 1. Open Kilo Code **Settings → Providers**. 2. Scroll down and click **Custom provider**. 3. Use these values: | Field | Value | |---|---| | Provider ID | `tokenlab` | | Display name | `TokenLab` | | Provider API | **OpenAI Compatible** | | Base URL | `https://api.tokenlab.sh/v1` | | API key | Your TokenLab API key | | Model | Select or add `kimi-k3` | Kilo automatically queries TokenLab's `/v1/models` endpoint after the Base URL and key are entered. Click **Submit**, then choose `tokenlab/kimi-k3` in the model picker. ## Configure the CLI Use the [Quickstart](/quickstart) terminal instructions for macOS, Linux, WSL, or Windows PowerShell to set `TOKENLAB_API_KEY` without putting the key in command history. Launch Kilo from that terminal. For persistent use, use your local secret manager; do not append a plaintext key to your shell profile. Before starting Kilo **7.7.2**, back up the active global configuration and check `permission.bash`. This version migrates an existing configuration without that explicit rule to `bash: "allow"`, even when `permission["*"]` is `deny`. Preserve your current shell rule explicitly in the global file: if the existing wildcard rule is `deny`, merge `"bash": "deny"` into the existing `permission` object. Keep other rules intact. The separate provider file below does not prevent this client migration. Create a separate `~/.config/kilo/tokenlab.jsonc` file with the configuration below. If `XDG_CONFIG_HOME` is set, use `kilo/tokenlab.jsonc` under that directory. Keep the normal `kilo.json` / `kilo.jsonc`, accounts, default model and permissions unchanged: this separate file is loaded only for an invocation that selects it with `KILO_CONFIG`. If the file or provider name already belongs to another setup, choose a different file/name and adjust the commands. ```jsonc { "$schema": "https://app.kilo.ai/config.json", "provider": { "tokenlab": { "options": { "apiKey": "{env:TOKENLAB_API_KEY}", "baseURL": "https://api.tokenlab.sh/v1" }, "models": { "kimi-k3": { "name": "Kimi K3", "tool_call": true, "reasoning": true, "modalities": { "input": [ "text", "image" ], "output": [ "text" ] }, "limit": { "context": 1048576, "output": 131072 } } }, "npm": "@ai-sdk/openai-compatible", "name": "TokenLab" } } } ``` #### macOS / Linux / WSL ```bash macOS / Linux / WSL KILO_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/kilo/tokenlab.jsonc" kilo models tokenlab KILO_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/kilo/tokenlab.jsonc" kilo --model tokenlab/kimi-k3 ``` #### Windows PowerShell ```powershell Windows PowerShell $previous = @{} $previous['KILO_CONFIG'] = [Environment]::GetEnvironmentVariable('KILO_CONFIG', 'Process') try { $env:KILO_CONFIG = (Join-Path $(if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { Join-Path $HOME '.config' }) 'kilo/tokenlab.jsonc') kilo models tokenlab kilo --model tokenlab/kimi-k3 } finally { foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name, $previous[$name], 'Process') } } ``` Saving settings or listing models does not verify a key or a completed request. A prompt uses TokenLab balance. Check the reply and its matching [request record](https://tokenlab.sh/dashboard/runs?section=requests) before trying tools; auxiliary requests may also be billed. ## Troubleshooting #### Models do not appear Confirm the Base URL ends in `/v1`, the key is active, and `kilo models` lists the configured provider. Add `kimi-k3` manually if model discovery is unavailable. #### Context compaction does not run Custom models need explicit `limit.context` and `limit.output` values. Keep the limits aligned with TokenLab's current model metadata. #### The model cannot edit or run tools Confirm `tool_call` is `true`, then review Kilo's permission settings for the current project. --- ### Gemini CLI > Configure Google Gemini CLI to use TokenLab API URL: https://tokenlab.sh/docs/en/integrations/gemini-cli ## Overview Gemini CLI **0.60.0** documents `GOOGLE_GEMINI_BASE_URL` for API-key authentication. Use TokenLab’s Gemini-native endpoint with a current model that supports `gemini_generate_content`. See the [official configuration reference](https://geminicli.com/docs/reference/configuration/) and [authentication guide](https://geminicli.com/docs/get-started/authentication/). This setup does not route a Google subscription through TokenLab. ## Installation ```bash npm install -g @google/gemini-cli@0.60.0 gemini --version ``` ## Configuration Use the [Quickstart](/quickstart) terminal instructions for macOS, Linux, WSL, or Windows PowerShell to set `TOKENLAB_API_KEY` without putting the key in command history. Launch Gemini CLI from that terminal. For persistent use, use your local secret manager; do not append a plaintext key to your shell profile. Keep your current Google login and configuration. For a separate TokenLab launch, choose an unused configuration root such as `~/.gemini-tokenlab` (`$HOME` on PowerShell) and create its `.gemini` directory. Copy your existing `.gemini/settings.json` and any policy files needed by your setup into the corresponding locations, preserving permissions, sandbox and unrelated settings. In that copy only, merge the following authentication field; create a settings file if none exists. Do not copy OAuth caches or clear your original credentials. Follow managed authentication policy instead of overriding it. ```json { "security": { "auth": { "selectedType": "gemini-api-key" } } } ``` ## Usage The launch below uses `~/.gemini-tokenlab/.gemini/settings.json`. Adjust the path if you chose another root. Approve the working directory through Gemini’s normal trust prompt; keep existing tool-approval policies. The model is selected explicitly for this session. #### macOS / Linux / WSL ```bash macOS / Linux / WSL GEMINI_CLI_HOME="$HOME/.gemini-tokenlab" GEMINI_API_KEY="${TOKENLAB_API_KEY:?Set TOKENLAB_API_KEY first}" GOOGLE_GEMINI_BASE_URL=https://api.tokenlab.sh gemini --model gemini-3.5-flash ``` #### Windows PowerShell ```powershell Windows PowerShell if (-not $env:TOKENLAB_API_KEY) { throw 'Set TOKENLAB_API_KEY first' } $previous = @{} $previous['GEMINI_CLI_HOME'] = [Environment]::GetEnvironmentVariable('GEMINI_CLI_HOME', 'Process') $previous['GEMINI_API_KEY'] = [Environment]::GetEnvironmentVariable('GEMINI_API_KEY', 'Process') $previous['GOOGLE_GEMINI_BASE_URL'] = [Environment]::GetEnvironmentVariable('GOOGLE_GEMINI_BASE_URL', 'Process') try { $env:GEMINI_CLI_HOME = (Join-Path $HOME '.gemini-tokenlab') $env:GEMINI_API_KEY = $env:TOKENLAB_API_KEY $env:GOOGLE_GEMINI_BASE_URL = 'https://api.tokenlab.sh' gemini --model gemini-3.5-flash } finally { foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name, $previous[$name], 'Process') } } ``` These variables apply only to this launch; your saved account, default model and permission settings stay in place. Launch normally without the variables to use your previous setup. Do not persist them in your shell profile. ## Verify Saving settings or listing models does not verify a key or a completed request. A prompt uses TokenLab balance. Check the reply and its matching [request record](https://tokenlab.sh/dashboard/runs?section=requests) before trying tools; auxiliary requests may also be billed. ```text Reply only with TOKENLAB_CONNECTION_OK. Do not use tools or modify files. ``` --- ### Pi Coding Agent > Connect Pi Coding Agent to TokenLab URL: https://tokenlab.sh/docs/en/integrations/pi-coding-agent ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and help me connect Pi to TokenLab: https://tokenlab.sh/docs/en/integrations/pi-coding-agent Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` ## Install and configure Pi Use the current [Pi installation instructions](https://pi.dev/). The npm package is `@earendil-works/pi-coding-agent`. On Windows, follow the [shell setup](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/windows.md); the CLI uses Git Bash by default. Skip installation when Pi is already available. ```bash npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.85.1 pi --version ``` The configuration below was checked with **Pi 0.85.1**. Other versions need their own verification against the official documentation. Back up `~/.pi/agent/models.json`, then append the `tokenlab` entry after existing entries inside `providers`; create the file only if it is absent. A nonempty `PI_CODING_AGENT_DIR` selects the directory containing `models.json`. Keep existing provider order, `settings.json`, accounts, default-model fields and permissions unchanged. If `tokenlab` already exists in the configuration or saved accounts, use another provider name and update the launch command. Set `TOKENLAB_API_KEY` in the launch environment using [Quickstart](/quickstart) for macOS, Linux, WSL or Windows PowerShell; never put the key in chat. ```json { "providers": { "tokenlab": { "baseUrl": "https://api.tokenlab.sh/v1", "api": "openai-completions", "apiKey": "$TOKENLAB_API_KEY", "models": [{ "id": "gpt-5.6-luna" }] } } } ``` The leading `$` in `"$TOKENLAB_API_KEY"` is required: a bare `"TOKENLAB_API_KEY"` is a literal string. Choose a current model with Chat Completions and tool support. Set context, output, image and reasoning options from that model’s details instead of copying another model’s limits. Optional: the [reversible Pi setup helper](https://github.com/hedging8563/tokenlab-skills/blob/247bb17c53754c8288d2d5cb00824fbd9f32899b/skills/tokenlab-api-integration/references/pi_setup.md) provides preview, apply and original-file restoration with Python 3.11+. TokenLab Skills plugin **0.1.2** includes these scripts; a standalone Skill installation can also be used after updating to a version that contains them. For an existing plugin or standalone Skill installation, follow the [README update instructions](https://github.com/hedging8563/tokenlab-skills/blob/247bb17c53754c8288d2d5cb00824fbd9f32899b/README.md#update-an-existing-installation) and confirm the script is present. The manual configuration here remains usable without the helper. ## Check and restore Use the same terminal and agent directory to check loading, then select TokenLab explicitly for this session. An unset `TOKENLAB_API_KEY` can hide the provider from the model list. Discovery proves registration and credential presence, not key validity or a completed API request. ```bash pi --list-models tokenlab pi --provider tokenlab --model gpt-5.6-luna ``` A prompt uses TokenLab balance. Check the reply and its [request record](https://tokenlab.sh/dashboard/runs?section=requests) before testing tools. To undo the manual configuration, remove only the provider entry you added, preserving all other settings and accounts. ## Skills and MCP Pi loads [Skills natively](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/skills.md). Install the [TokenLab Skill](/integrations/coding-agent-skill) for API integration guidance. MCP requires a separate Pi extension; do not paste an MCP client configuration into `models.json`. --- ### OpenCode > Configure OpenCode to use TokenLab API URL: https://tokenlab.sh/docs/en/integrations/opencode ## Let my agent set this up Copy this task to an agent already running on your computer: ```text Read this guide and help me connect OpenCode to TokenLab: https://tokenlab.sh/docs/en/integrations/opencode Check my installed version and active configuration first. Preserve existing accounts, providers, permissions and other settings. Back up local files and show proposed changes. Have me enter any API key locally; never ask for, print or paste it in chat. Check configuration loading first. Explain any paid request test separately before running it. ``` ## Overview OpenCode can use TokenLab through a custom OpenAI-compatible provider. Add the TokenLab API key, Base URL, and a current model ID. ## Installation [OpenCode](https://opencode.ai/docs/) ```bash npm install -g opencode-ai@1.18.31 ``` Verify installation: ```bash opencode --version ``` ## Configuration ### Step 1: Set Environment Variables Use the [Quickstart](/quickstart) terminal instructions for macOS, Linux, WSL, or Windows PowerShell to set `TOKENLAB_API_KEY` without putting the key in command history. Launch OpenCode from that terminal. For persistent use, use your local secret manager; do not append a plaintext key to your shell profile. ### Step 2: Configure OpenCode The configuration below was checked with **OpenCode 1.18.31**. Other versions need their own verification against the official documentation. Create a separate `~/.config/opencode/tokenlab.jsonc` file with the configuration below. If `XDG_CONFIG_HOME` is set, use `opencode/tokenlab.jsonc` under that directory. Keep the normal `opencode.json` / `opencode.jsonc`, accounts, default model and permissions unchanged: this separate file is loaded only for an invocation that selects it with `OPENCODE_CONFIG`. If the file or provider name already belongs to another setup, choose a different file/name and adjust the commands. ```jsonc { "$schema": "https://opencode.ai/config.json", "provider": { "tokenlab": { "npm": "@ai-sdk/openai-compatible", "name": "TokenLab", "options": { "baseURL": "https://api.tokenlab.sh/v1", "apiKey": "{env:TOKENLAB_API_KEY}" }, "models": { "gpt-5.6-terra": { "name": "GPT-5.6 Terra" }, "gpt-5.6-luna": { "name": "GPT-5.6 Luna" } } } } } ``` > Note: This configuration uses `@ai-sdk/openai-compatible` for Chat Completions. Choose a current model whose details support Chat Completions and the tools you need; do not infer support for another protocol or version from this example. Optional: the [reversible OpenCode setup helper](https://github.com/hedging8563/tokenlab-skills/blob/247bb17c53754c8288d2d5cb00824fbd9f32899b/skills/tokenlab-api-integration/references/opencode_setup.md) provides preview, apply and original-file restoration with Python 3.11+. TokenLab Skills plugin **0.1.2** includes these scripts; a standalone Skill installation can also be used after updating to a version that contains them. For an existing plugin or standalone Skill installation, follow the [README update instructions](https://github.com/hedging8563/tokenlab-skills/blob/247bb17c53754c8288d2d5cb00824fbd9f32899b/README.md#update-an-existing-installation) and confirm the script is present. The manual configuration here remains usable without the helper. ## Basic Usage Set the key using Quickstart, then load this file and select the model explicitly for this invocation. Substitute your actual path and provider name if customized. Keep any existing `OPENCODE_CONFIG` or inline `OPENCODE_CONFIG_CONTENT` setup in a separate launch environment; project and managed settings may still take precedence. #### macOS / Linux / WSL ```bash macOS / Linux / WSL OPENCODE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/tokenlab.jsonc" opencode models tokenlab OPENCODE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/tokenlab.jsonc" opencode --model tokenlab/gpt-5.6-terra ``` #### Windows PowerShell ```powershell Windows PowerShell $tokenlabConfigRoot = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { Join-Path $HOME '.config' } $previousConfig = $env:OPENCODE_CONFIG try { $env:OPENCODE_CONFIG = Join-Path $tokenlabConfigRoot 'opencode/tokenlab.jsonc' opencode models tokenlab opencode --model tokenlab/gpt-5.6-terra } finally { if ($null -eq $previousConfig) { Remove-Item Env:OPENCODE_CONFIG -ErrorAction SilentlyContinue } else { $env:OPENCODE_CONFIG = $previousConfig } } ``` Model discovery checks local loading, not key validity or a successful live request. A prompt uses TokenLab balance and OpenCode may send separately billed auxiliary requests, such as title generation. Check the reply and the matching [request records](https://tokenlab.sh/dashboard/runs?section=requests). To use your ordinary setup, launch without this layer. Do not persist `OPENCODE_CONFIG` in your shell profile for this guide. To undo the manual setup, remove only the separate file you created, or restore its backup if it existed before. ## Choose a Model Copy a current coding model ID from the [Models page](https://tokenlab.sh/en/models). Compare models on your own repository before setting a default. ## Common Commands **Analyze code:** ```bash OPENCODE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/tokenlab.jsonc" opencode run --model tokenlab/gpt-5.6-terra "What does this function do?" < src/utils.ts ``` **Generate code:** ```bash OPENCODE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/tokenlab.jsonc" opencode run --model tokenlab/gpt-5.6-terra "Create a REST API with Express" ``` **Review changes:** ```bash git diff | OPENCODE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/tokenlab.jsonc" opencode run --model tokenlab/gpt-5.6-terra "Review these changes" ``` **Fix errors:** ```bash OPENCODE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/tokenlab.jsonc" opencode run --model tokenlab/gpt-5.6-terra "Fix the TypeScript errors in this project" ``` ## Interactive Commands | Command | Description | |---------|-------------| | `/help` | Show available commands | | `/models` | Switch to a different model | | `/clear` | Clear conversation history | | `/exit` | Exit OpenCode | ## Troubleshooting #### Connection Error - Verify `options.baseURL` is set to `https://api.tokenlab.sh/v1` - Check network connectivity - Try `curl https://api.tokenlab.sh/v1/models` to test #### Authentication Failed - Verify `TOKENLAB_API_KEY` environment variable is set - Verify `options.apiKey` references `{env:TOKENLAB_API_KEY}` or another valid secret source - Check that the key starts with `sk-` - Ensure the key is active in TokenLab dashboard #### Model Not Found - Check model name in config matches exactly - Verify model availability at [tokenlab.sh/en/models](https://tokenlab.sh/en/models) ## Tips #### Use project context Run OpenCode from your project root for better understanding of your codebase. #### Choose a model with your own tasks Test the same edit or review task across a few current models and compare the result, time, and cost. #### Review generated code Always review AI-generated code before applying changes to your project. --- ### OpenHands > Connect OpenHands to TokenLab URL: https://tokenlab.sh/docs/en/integrations/openhands ## Overview Use this page for the OpenHands UI with LiteLLM-backed **Settings → LLM → Advanced** options. Select **OpenAI** and enter a custom model with the `openai/` prefix, a TokenLab key in the local API-key field, and the Base URL below. Record the previous selection first and restore it when finished. Keep existing accounts and tool permissions. See the [official OpenAI-compatible configuration](https://docs.openhands.dev/openhands/usage/llms/openai-llms). CLI, cloud and source-checkout installations can use different setup flows; follow the instructions for your installed edition. `make setup-config` is not a general installed-CLI command. ## Model Settings Save the current provider, model and connection settings locally before editing. These controls change the active model selection; restore your previous values when finished. Keep signed-in accounts and tool permissions unchanged. Do not overwrite another OpenAI-compatible connection without a restorable backup. ```text Custom Model: openai/kimi-k3 Base URL: https://api.tokenlab.sh/v1 ``` Saving settings or listing models does not verify a key or a completed request. A prompt uses TokenLab balance. Check the reply and its matching [request record](https://tokenlab.sh/dashboard/runs?section=requests) before trying tools; auxiliary requests may also be billed. ```text Reply only with TOKENLAB_CONNECTION_OK. Do not use tools or modify files. ``` --- ## Native SDKs ### OpenAI SDK > Use TokenLab with official OpenAI SDKs and OpenAI-compatible clients URL: https://tokenlab.sh/docs/en/integrations/openai-sdk ## Overview Point an OpenAI SDK client to `https://api.tokenlab.sh/v1` and use a TokenLab API key. Keep the API method your application already uses, and confirm the selected model supports it through `accepted_request_formats`. > Note: Python, JavaScript, and Go have official OpenAI SDKs. PHP works well with OpenAI-compatible community clients, but it is not an official OpenAI SDK. ## Installation #### Python ```bash Python pip install openai ``` #### JavaScript ```bash JavaScript npm install openai ``` #### Go ```bash Go go get github.com/openai/openai-go/v3 ``` > Note: Use `POST /v1/responses` for Responses input items, tools, or continuation. Optional fields depend on the selected model. ## Configure the Client #### Python ```python Python from openai import OpenAI client = OpenAI( api_key="sk-your-tokenlab-key", base_url="https://api.tokenlab.sh/v1", ) ``` #### JavaScript ```javascript JavaScript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-tokenlab-key', baseURL: 'https://api.tokenlab.sh/v1', }); ``` #### Go ```go Go package main import ( openai "github.com/openai/openai-go/v3" "github.com/openai/openai-go/v3/option" ) func main() { client := openai.NewClient( option.WithAPIKey("sk-your-tokenlab-key"), option.WithBaseURL("https://api.tokenlab.sh/v1"), ) _ = client } ``` ## Recommended: Chat Completions #### Python ```python Python response = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Reply with: Connected to TokenLab."}] ) print(response.choices[0].message.content) ``` #### JavaScript ```javascript JavaScript const response = await client.chat.completions.create({ model: 'gpt-5.6-terra', messages: [{ role: 'user', content: 'Reply with: Connected to TokenLab.' }], }); console.log(response.choices[0].message.content); ``` #### cURL ```bash cURL curl https://api.tokenlab.sh/v1/chat/completions \ -H "Authorization: Bearer sk-your-tokenlab-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "messages": [ {"role": "user", "content": "Reply with: Connected to TokenLab."} ] }' ``` ## Advanced: Responses API Use this path only when your tool or workflow explicitly depends on OpenAI Responses semantics. ### Streaming with Responses #### Python ```python Python stream = client.responses.create( model="gpt-5.6-terra", input="Write a short poem about coding.", stream=True, ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="") ``` #### JavaScript ```javascript JavaScript const stream = await client.responses.create({ model: 'gpt-5.6-terra', input: 'Write a short poem about coding.', stream: true, }); for await (const event of stream) { if (event.type === 'response.output_text.delta') { process.stdout.write(event.delta); } } ``` ## Tools / Function Calling ```python response = client.responses.create( model="gpt-5.6-terra", input="What's the weather in Tokyo?", tools=[{ "type": "function", "name": "get_weather", "description": "Get weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string"} }, "required": ["location"] } }] ) for item in response.output: if item.type == "function_call": print(item.name) print(item.arguments) ``` ## Vision with Responses ```python response = client.responses.create( model="gpt-5.6-terra", input=[{ "role": "user", "content": [ {"type": "input_text", "text": "What's in this image?"}, {"type": "input_image", "image_url": "https://example.com/image.jpg"} ] }] ) print(response.output_text) ``` ## Embeddings ```python response = client.embeddings.create( model="text-embedding-3-small", input="Hello world" ) print(response.data[0].embedding[:5]) ``` ## Chat Completions Chat Completions is the default OpenAI-compatible path for TokenLab: ```python response = client.chat.completions.create( model="gpt-5.6-terra", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"} ] ) print(response.choices[0].message.content) ``` ## Troubleshooting #### Connection Error - Verify the base URL is exactly `https://api.tokenlab.sh/v1` - Check for proxy interference or custom HTTP client overrides - Confirm your SDK version supports the method and fields you are using #### Authentication Failed - Check that your API key starts with `sk-` - Verify the key is active in TokenLab dashboard - Confirm the SDK is sending `Authorization: Bearer ...` #### Wrong API Path - `responses.create(...)` sends requests to `/v1/responses` - `chat.completions.create(...)` sends requests to `/v1/chat/completions` - Use Chat Completions by default unless you explicitly need Responses-specific behavior --- ### Anthropic SDK > Use Claude Messages through TokenLab with the Anthropic SDK URL: https://tokenlab.sh/docs/en/integrations/anthropic-sdk ## Overview The Anthropic SDK can call Claude models through TokenLab's **Messages API**. Change the API key and base URL; the SDK methods stay the same. > Note: For the Anthropic SDK, use `https://api.tokenlab.sh` as the base URL, without appending `/v1` yourself. ## Installation #### Python ```bash Python pip install anthropic ``` #### JavaScript ```bash JavaScript npm install @anthropic-ai/sdk ``` ## Configure the Client #### Python ```python Python from anthropic import Anthropic client = Anthropic( api_key="sk-your-tokenlab-key", base_url="https://api.tokenlab.sh", ) ``` #### JavaScript ```javascript JavaScript import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic({ apiKey: 'sk-your-tokenlab-key', baseURL: 'https://api.tokenlab.sh', }); ``` ## Basic Usage ```python message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[ {"role": "user", "content": "Reply with: Connected to TokenLab."} ] ) print(message.content[0].text) ``` ## Streaming ```python with client.messages.stream( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Write a short poem about coding."}] ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` ## Vision ```python import base64 message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{ "role": "user", "content": [ {"type": "text", "text": "What's in this image?"}, { "type": "image", "source": { "type": "url", "url": "https://example.com/image.jpg" } } ] }] ) with open("image.png", "rb") as f: image_data = base64.b64encode(f.read()).decode() message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{ "role": "user", "content": [ {"type": "text", "text": "Describe this image"}, { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": image_data } } ] }] ) ``` ## Tool Use ```python message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, tools=[{ "name": "get_weather", "description": "Get the weather for a location", "input_schema": { "type": "object", "properties": { "location": {"type": "string"} }, "required": ["location"] } }], messages=[{"role": "user", "content": "What's the weather in Tokyo?"}] ) for block in message.content: if block.type == "tool_use": print(block.name) print(block.input) ``` ## Extended Thinking ```python message = client.messages.create( model="claude-opus-5", max_tokens=16000, thinking={ "type": "enabled", "budget_tokens": 10000 }, messages=[{"role": "user", "content": "Solve this complex problem step by step."}] ) for block in message.content: if block.type == "thinking": print(block.thinking) elif block.type == "text": print(block.text) ``` ## Choose a Model Copy a current Claude model ID from the [Models page](https://tokenlab.sh/en/models). Compare models on your own prompts before setting a default. ## Troubleshooting #### Wrong Base URL - Use `https://api.tokenlab.sh` - Do not manually append `/v1` when configuring the Anthropic SDK #### Authentication Failed - Check that your TokenLab API key starts with `sk-` - Confirm the key is active in TokenLab dashboard - Let the Anthropic SDK manage the auth header instead of adding custom headers manually #### Model Not Found - Verify the Claude model name exactly - Check current availability in the TokenLab model catalog --- ## Frameworks & Platforms ### LiteLLM > Connect LiteLLM to TokenLab URL: https://tokenlab.sh/docs/en/integrations/litellm ## Overview LiteLLM can use TokenLab in two ways: - use TokenLab as an **OpenAI-compatible endpoint** behind LiteLLM - use LiteLLM's gateway when your team also needs its virtual keys, aliases, or logs For Chat Completions, configure LiteLLM's **custom OpenAI / OpenAI-compatible** client with `https://api.tokenlab.sh/v1`. > Note: If your application needs Claude Messages or Gemini fields, use an integration that keeps that API format. ## Install ```bash pip install 'litellm[proxy]' ``` ## Proxy Configuration Create a `litellm-config.yaml` like this: ```yaml model_list: - model_name: tokenlab-gpt-5.4 litellm_params: model: custom_openai/gpt-5.4 api_base: https://api.tokenlab.sh/v1 api_key: os.environ/OPENAI_API_KEY - model_name: tokenlab-claude-sonnet litellm_params: model: custom_openai/claude-sonnet-4-6 api_base: https://api.tokenlab.sh/v1 api_key: os.environ/OPENAI_API_KEY ``` Start the proxy: ```bash export OPENAI_API_KEY="sk-your-tokenlab-key" litellm --config litellm-config.yaml --port 4000 ``` ## Call LiteLLM Through OpenAI SDK ```python from openai import OpenAI client = OpenAI( api_key="anything", base_url="http://127.0.0.1:4000" ) response = client.chat.completions.create( model="tokenlab-gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ## Direct Python Usage If you are using LiteLLM as a Python library instead of the proxy, keep the same TokenLab base URL: ```python import litellm response = litellm.completion( model="custom_openai/gpt-5.4", api_base="https://api.tokenlab.sh/v1", api_key="sk-your-tokenlab-key", messages=[{"role": "user", "content": "Summarize this repo."}] ) ``` ## Best Practices #### Prefer custom_openai for TokenLab Treat TokenLab as an OpenAI-compatible endpoint unless you have a very specific reason to build a more complex provider mapping. #### Use LiteLLM when you need one more gateway layer LiteLLM makes sense when your own platform wants virtual keys, extra model-selection policy, or centralized logs in front of TokenLab. #### Keep native-provider expectations realistic OpenAI-compatible translation layers are great for broad compatibility, but they are not the right place to promise every provider-native feature. ## Troubleshooting #### Connection errors - Verify `api_base` is exactly `https://api.tokenlab.sh/v1` - Make sure LiteLLM can reach TokenLab over the public internet - If you run the proxy locally, verify the OpenAI client points to your LiteLLM port instead of TokenLab directly #### Authentication errors - Check that LiteLLM is reading the right `OPENAI_API_KEY` - Confirm the TokenLab key starts with `sk-` - Confirm the key is active in TokenLab dashboard #### Model not found - Verify the TokenLab model name in `custom_openai/` - Keep your LiteLLM `model_name` alias separate from the real TokenLab model id --- ### Vercel AI SDK > Use TokenLab models with Vercel AI SDK URL: https://tokenlab.sh/docs/en/integrations/vercel-ai-sdk ## Overview Use the **OpenAI-compatible provider** for Chat Completions. If the selected model and your application need Responses, use the OpenAI provider with the same TokenLab Base URL. ## TokenLab Provider Package TokenLab also provides a small Vercel AI SDK wrapper: ```bash npm install ai @tokenlabai/ai-sdk-provider ``` ```typescript import { generateText } from 'ai'; import { tokenlab } from '@tokenlabai/ai-sdk-provider'; const { text } = await generateText({ model: tokenlab.chatModel('gpt-5.6-terra'), prompt: 'Explain TokenLab in one sentence.', }); console.log(text); ``` ## Recommended Default: OpenAI-Compatible Provider ```bash npm install ai @ai-sdk/openai-compatible zod ``` ```typescript import { createOpenAICompatible } from '@ai-sdk/openai-compatible'; export const tokenlab = createOpenAICompatible({ name: 'tokenlab', apiKey: process.env.TOKENLAB_API_KEY, baseURL: 'https://api.tokenlab.sh/v1', }); ``` ## Generate Text ```typescript import { generateText } from 'ai'; import { tokenlab } from './tokenlab'; const { text } = await generateText({ model: tokenlab.chatModel('gpt-5.6-terra'), prompt: 'Explain TokenLab in one sentence.', }); console.log(text); ``` ## Stream Text ```typescript import { streamText } from 'ai'; import { tokenlab } from './tokenlab'; const result = await streamText({ model: tokenlab.chatModel('gpt-5.6-terra'), prompt: 'Write a short poem about coding.', }); for await (const textPart of result.textStream) { process.stdout.write(textPart); } ``` ## Tool Calling ```typescript import { generateText, stepCountIs, tool } from 'ai'; import { z } from 'zod'; import { tokenlab } from './tokenlab'; const result = await generateText({ model: tokenlab.chatModel('gpt-5.6-terra'), prompt: 'What is the weather in San Francisco?', stopWhen: stepCountIs(2), tools: { weather: tool({ description: 'Get weather in a location', inputSchema: z.object({ location: z.string(), }), execute: async ({ location }) => ({ location, temperature: 72, condition: 'sunny', }), }), }, }); console.log(result.text); ``` ## Structured Output ```typescript import { generateObject } from 'ai'; import { z } from 'zod'; import { tokenlab } from './tokenlab'; const { object } = await generateObject({ model: tokenlab.chatModel('gpt-5.6-terra'), schema: z.object({ name: z.string(), role: z.string(), }), prompt: 'Generate a fake developer profile.', }); console.log(object); ``` ## If You Explicitly Need Responses-Native Behavior ```bash npm install ai @ai-sdk/openai ``` ```typescript import { createOpenAI } from '@ai-sdk/openai'; export const tokenlabResponses = createOpenAI({ apiKey: process.env.TOKENLAB_API_KEY, baseURL: 'https://api.tokenlab.sh/v1', }); ``` ```typescript import { generateText } from 'ai'; import { tokenlabResponses } from './tokenlab-responses'; const { text } = await generateText({ model: tokenlabResponses('gpt-5.6-terra'), prompt: 'Explain TokenLab in one sentence.', }); ``` > Note: Use `@ai-sdk/openai-compatible` as the safe default for proxy-style integrations. Switch to `@ai-sdk/openai` only when you explicitly want a provider path built on `/v1/responses`. ## Environment Variables ```bash # .env.local TOKENLAB_API_KEY=sk-your-tokenlab-key ``` ## Best Practices #### Prefer openai-compatible first For third-party gateways and proxy backends, `@ai-sdk/openai-compatible` is usually the least surprising starting point. #### Move to Responses only when needed If you need provider behavior tied to `/v1/responses`, switch the provider package deliberately instead of mixing both patterns in one client. #### Keep secrets on the server Never expose your TokenLab API key in client-side code. Put provider setup in server files or API routes. --- ### LangChain > Use TokenLab chat and embeddings with LangChain URL: https://tokenlab.sh/docs/en/integrations/langchain ## Overview LangChain's `ChatOpenAI` and `OpenAIEmbeddings` can use TokenLab through the OpenAI-compatible API. > Note: `ChatOpenAI` handles OpenAI-compatible fields. If your application needs Claude Messages or Gemini-specific fields, use the matching LangChain integration instead. ## Installation ```bash pip install langchain langchain-openai langchain-community faiss-cpu ``` ## Basic Configuration ```python from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-5.6-terra", api_key="sk-your-tokenlab-key", base_url="https://api.tokenlab.sh/v1", ) response = llm.invoke("Explain TokenLab in one sentence.") print(response.content) ``` ## Using Different Models ```python from langchain_openai import ChatOpenAI gpt = ChatOpenAI( model="gpt-5.6-terra", api_key="sk-your-key", base_url="https://api.tokenlab.sh/v1", ) claude = ChatOpenAI( model="claude-sonnet-5", api_key="sk-your-key", base_url="https://api.tokenlab.sh/v1", ) gemini = ChatOpenAI( model="gemini-3.5-flash", api_key="sk-your-key", base_url="https://api.tokenlab.sh/v1", ) deepseek = ChatOpenAI( model="deepseek-v4-pro", api_key="sk-your-key", base_url="https://api.tokenlab.sh/v1", ) ``` ## Message History ```python from langchain_core.messages import HumanMessage, SystemMessage messages = [ SystemMessage(content="You are a helpful assistant."), HumanMessage(content="What is the capital of France?") ] response = llm.invoke(messages) print(response.content) ``` ## Streaming ```python for chunk in llm.stream("Write a short poem about coding."): if chunk.content: print(chunk.content, end="", flush=True) ``` ## Embeddings ```python from langchain_openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings( model="text-embedding-3-small", api_key="sk-your-key", base_url="https://api.tokenlab.sh/v1", ) vector = embeddings.embed_query("Hello world") print(vector[:5]) ``` ## Simple RAG Example ```python from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import FAISS from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough embeddings = OpenAIEmbeddings( model="text-embedding-3-small", api_key="sk-your-key", base_url="https://api.tokenlab.sh/v1", ) texts = [ "TokenLab provides one API for many AI models.", "TokenLab supports OpenAI-compatible integrations." ] vectorstore = FAISS.from_texts(texts, embeddings) retriever = vectorstore.as_retriever() prompt = ChatPromptTemplate.from_template( "Answer using the context below.\\n\\nContext:\\n{context}\\n\\nQuestion:\\n{question}" ) rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm ) response = rag_chain.invoke("What does TokenLab provide?") print(response.content) ``` ## Agents > Note: For new agentic projects, LangChain recommends considering LangGraph for more explicit control over long-running and tool-using workflows. ```python from langchain.agents import create_agent from langchain_core.tools import tool @tool def search(query: str) -> str: """Search for information.""" return f"Search results for: {query}" tools = [search] agent = create_agent( model=llm, tools=tools, system_prompt="You are a helpful assistant with access to tools.", ) result = agent.invoke({ "messages": [{"role": "user", "content": "Search for TokenLab pricing"}] }) print(result["messages"][-1].content) ``` ## Best Practices #### Pass base_url explicitly The most reliable TokenLab setup is to pass `base_url="https://api.tokenlab.sh/v1"` directly to `ChatOpenAI` and `OpenAIEmbeddings` instead of depending on older environment-variable aliases. #### Use standard features here Stick to standard chat, tool calling, streaming, and embeddings on `ChatOpenAI`. If you need vendor-native extras, switch to the vendor's own LangChain integration. #### Use cheaper models for retrieval Use embedding models like `text-embedding-3-small` for retrieval and reserve stronger chat models for the final answer step. --- ### LlamaIndex > Integrate TokenLab with LlamaIndex using OpenAI-compatible integrations URL: https://tokenlab.sh/docs/en/integrations/llamaindex ## Overview Use LlamaIndex `OpenAILike` for TokenLab. It accepts a custom Base URL and does not infer model metadata from OpenAI model names. ## Installation ```bash pip install llama-index-core \ llama-index-readers-file \ llama-index-llms-openai-like \ llama-index-embeddings-openai-like ``` ## Basic Configuration ```python from llama_index.core import Settings from llama_index.llms.openai_like import OpenAILike from llama_index.embeddings.openai_like import OpenAILikeEmbedding llm = OpenAILike( model="gpt-5.6-terra", api_base="https://api.tokenlab.sh/v1", api_key="sk-your-tokenlab-key", is_chat_model=True, ) embed_model = OpenAILikeEmbedding( model_name="text-embedding-3-small", api_base="https://api.tokenlab.sh/v1", api_key="sk-your-tokenlab-key", ) Settings.llm = llm Settings.embed_model = embed_model ``` ## Basic Usage ```python response = llm.complete("Explain TokenLab in one sentence.") print(response.text) ``` ## Minimal OpenAILike LLM ```python from llama_index.llms.openai_like import OpenAILike llm = OpenAILike( model="claude-sonnet-5", api_base="https://api.tokenlab.sh/v1", api_key="sk-your-tokenlab-key", context_window=200000, is_chat_model=True, is_function_calling_model=True, ) ``` ## Chat ```python from llama_index.core.llms import ChatMessage messages = [ ChatMessage(role="system", content="You are a helpful assistant."), ChatMessage(role="user", content="What is the capital of France?") ] response = llm.chat(messages) print(response.message.content) ``` ## Streaming ```python for chunk in llm.stream_complete("Write a short poem about AI."): print(chunk.delta, end="", flush=True) ``` ## Embeddings ```python vector = embed_model.get_text_embedding("Hello, world!") print(vector[:5]) ``` ## RAG with Documents ```python from llama_index.core import SimpleDirectoryReader, VectorStoreIndex documents = SimpleDirectoryReader("./data").load_data() index = VectorStoreIndex.from_documents(documents) query_engine = index.as_query_engine() response = query_engine.query("What is in my documents?") print(response) ``` ## Chat Engine ```python chat_engine = index.as_chat_engine(chat_mode="condense_question") response = chat_engine.chat("What is TokenLab?") print(response) response = chat_engine.chat("How many models does it support?") print(response) ``` ## Async Usage ```python import asyncio async def main(): response = await llm.acomplete("Hello!") print(response.text) asyncio.run(main()) ``` ## Best Practices #### Use OpenAILike for TokenLab Prefer `llama_index.llms.openai_like.OpenAILike` and `llama_index.embeddings.openai_like.OpenAILikeEmbedding` for TokenLab and other third-party OpenAI-compatible gateways. #### Set api_base explicitly Pass `api_base="https://api.tokenlab.sh/v1"` directly in code instead of relying on older OpenAI environment-variable names. #### Keep model roles separated Use chat/reasoning models for synthesis and `text-embedding-3-small` or `text-embedding-3-large` for retrieval. --- ### OpenAI Agents SDK > Use TokenLab models with the OpenAI Agents SDK URL: https://tokenlab.sh/docs/en/integrations/openai-agents-sdk ## Overview Create an OpenAI-compatible TokenLab client and pass it to `OpenAIChatCompletionsModel`. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" export TOKENLAB_BASE_URL="https://api.tokenlab.sh/v1" export TOKENLAB_MODEL="gpt-5.6-luna" ``` ## Example ```python import asyncio import os from openai import AsyncOpenAI from agents import Agent, OpenAIChatCompletionsModel, Runner, set_tracing_disabled client = AsyncOpenAI( base_url=os.getenv("TOKENLAB_BASE_URL", "https://api.tokenlab.sh/v1"), api_key=os.environ["TOKENLAB_API_KEY"], ) set_tracing_disabled(disabled=True) async def main(): agent = Agent( name="Assistant", instructions="You are concise and practical.", model=OpenAIChatCompletionsModel( model=os.getenv("TOKENLAB_MODEL", "gpt-5.6-luna"), openai_client=client, ), ) result = await Runner.run(agent, "Explain custom model providers in one paragraph.") print(result.final_output) asyncio.run(main()) ``` ## Scope This path uses Chat Completions compatibility. TokenLab's native Responses endpoint is available at `https://api.tokenlab.sh/v1/responses`, but SDK features that depend on OpenAI-specific Responses semantics should be verified with the exact model and workflow before production use. --- ### AutoGen > Use TokenLab models with Microsoft AutoGen through OpenAI-compatible clients URL: https://tokenlab.sh/docs/en/integrations/autogen ## Overview AutoGen can call TokenLab with `OpenAIChatCompletionClient`. Configure the TokenLab Base URL, API key, and model ID in the client. ## Installation ```bash pip install -U autogen-agentchat "autogen-ext[openai]" pyyaml ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## YAML Model Config Use TokenLab's `/v1` endpoint as `base_url`: Save this configuration as `tokenlab-model.yaml`: ```yaml provider: autogen_ext.models.openai.OpenAIChatCompletionClient config: model: claude-sonnet-5 base_url: https://api.tokenlab.sh/v1 model_info: function_calling: true json_output: true structured_output: false vision: false family: claude ``` Load the YAML and read the API key from the environment. YAML does not expand environment variables automatically: ```python import os import yaml from autogen_agentchat.agents import AssistantAgent from autogen_core.models import ChatCompletionClient with open("tokenlab-model.yaml", encoding="utf-8") as config_file: config = yaml.safe_load(config_file) config["config"]["api_key"] = os.environ["TOKENLAB_API_KEY"] model_client = ChatCompletionClient.load_component(config) agent = AssistantAgent("assistant", model_client=model_client) ``` ## Python Client ```python import os from autogen_agentchat.agents import AssistantAgent from autogen_ext.models.openai import OpenAIChatCompletionClient model_client = OpenAIChatCompletionClient( model="claude-sonnet-5", base_url="https://api.tokenlab.sh/v1", api_key=os.environ["TOKENLAB_API_KEY"], model_info={ "function_calling": True, "json_output": True, "structured_output": False, "vision": False, "family": "claude", }, ) agent = AssistantAgent("assistant", model_client=model_client) ``` ## Run your first request ```python import asyncio async def main(): try: result = await agent.run(task="Reply with: Connected to TokenLab.") print(result.messages[-1].content) finally: await model_client.close() asyncio.run(main()) ``` ## Endpoint Notes AutoGen's OpenAI extension expects the OpenAI-compatible request shape. For provider-native features such as Anthropic Messages or Gemini `generateContent`, use TokenLab's native endpoints from a client that supports those protocols directly. --- ### Semantic Kernel > Connect Microsoft Semantic Kernel to TokenLab with a custom OpenAI-compatible endpoint URL: https://tokenlab.sh/docs/en/integrations/semantic-kernel ## Overview Semantic Kernel can call TokenLab through its OpenAI custom endpoint chat completion service. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## .NET Kernel Builder Semantic Kernel's custom endpoint should point to the root OpenAI-compatible `/v1` endpoint, not `/v1/chat/completions`. ```csharp using Microsoft.SemanticKernel; #pragma warning disable SKEXP0010 var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: "gpt-5.6-luna", endpoint: new Uri("https://api.tokenlab.sh/v1"), apiKey: Environment.GetEnvironmentVariable("TOKENLAB_API_KEY")); var kernel = builder.Build(); ``` ## Basic Prompt ```csharp var result = await kernel.InvokePromptAsync( "Give me a concise checklist for evaluating an LLM gateway."); Console.WriteLine(result); ``` ## Endpoint Notes Use: ```text https://api.tokenlab.sh/v1 ``` Do not use: ```text https://api.tokenlab.sh/v1/chat/completions ``` Semantic Kernel adds the chat completions path automatically for this connector. --- ### CrewAI > Use TokenLab models in CrewAI agents through OpenAI-compatible LLM settings URL: https://tokenlab.sh/docs/en/integrations/crewai ## Overview CrewAI supports OpenAI-compatible LLMs by setting the model, API key, and base URL on environment variables or on the `LLM` class. ## Environment Variables ```python import os os.environ["OPENAI_API_KEY"] = "sk-your-tokenlab-key" os.environ["OPENAI_API_BASE"] = "https://api.tokenlab.sh/v1" os.environ["OPENAI_MODEL_NAME"] = "openai/claude-sonnet-5" ``` ## LLM Class ```python from crewai import Agent, LLM llm = LLM( model="claude-sonnet-5", custom_openai=True, base_url="https://api.tokenlab.sh/v1", api_key="sk-your-tokenlab-key", ) agent = Agent( role="Research analyst", goal="Summarize technical tradeoffs clearly", backstory="You produce concise implementation notes.", llm=llm, ) ``` Model IDs change over time. Choose a current model on the [Models page](https://tokenlab.sh/en/models), then compare quality, speed, and cost on your own CrewAI tasks. ## Endpoint Notes This setup uses Chat Completions. To use Responses, Anthropic Messages, or Gemini, confirm that your CrewAI component supports that API format. --- ### Pydantic AI > Use TokenLab with Pydantic AI through OpenAI-compatible models URL: https://tokenlab.sh/docs/en/integrations/pydantic-ai ## Overview Pydantic AI can use TokenLab with `OpenAIChatModel` and `OpenAIProvider(base_url=...)`. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Basic Agent ```python import os from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIChatModel from pydantic_ai.providers.openai import OpenAIProvider model = OpenAIChatModel( "claude-sonnet-5", provider=OpenAIProvider( base_url="https://api.tokenlab.sh/v1", api_key=os.environ["TOKENLAB_API_KEY"], ), ) agent = Agent(model) result = agent.run_sync("Explain one benefit of OpenAI-compatible gateways.") print(result.output) ``` ## Tools ```python from pydantic_ai import Agent, RunContext agent = Agent(model) @agent.tool def lookup_status(ctx: RunContext[None], service: str) -> str: return f"{service} is online" result = agent.run_sync("Check the TokenLab status.") print(result.output) ``` ## Endpoint Notes This path targets Pydantic AI's OpenAI Chat Completions model class. For Responses-native or provider-native protocol behavior, choose a client path that exposes that protocol directly. --- ### DSPy > Configure TokenLab as an OpenAI-compatible language model in DSPy URL: https://tokenlab.sh/docs/en/integrations/dspy ## Overview DSPy can call TokenLab by using the `openai/` provider prefix with TokenLab's OpenAI-compatible `/v1` endpoint. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Configure DSPy ```python import os import dspy lm = dspy.LM( "openai/claude-sonnet-5", api_key=os.environ["TOKENLAB_API_KEY"], api_base="https://api.tokenlab.sh/v1", ) dspy.configure(lm=lm) ``` ## Use a Module ```python qa = dspy.ChainOfThought("question -> answer") response = qa(question="What should I monitor in an LLM gateway?") print(response.answer) ``` Model IDs change over time. Choose a current chat model on the [Models page](https://tokenlab.sh/en/models), then compare it on your own DSPy program. --- ### Langflow > Use TokenLab models from Langflow's OpenAI component URL: https://tokenlab.sh/docs/en/integrations/langflow ## Overview Langflow's **OpenAI** component includes an **OpenAI API Base** field. Point that field at TokenLab's OpenAI-compatible `/v1` endpoint and use TokenLab model IDs. ## OpenAI Component 1. Add an **OpenAI** language model component. 2. Set **OpenAI API Key** to your TokenLab key. 3. Expand advanced settings. 4. Set **OpenAI API Base** to `https://api.tokenlab.sh/v1`. 5. Set **Model Name** to a TokenLab model ID. 6. Connect the model output to your agent, chain, or workflow. ## Values | Field | Value | |-------|-------| | OpenAI API Base | `https://api.tokenlab.sh/v1` | | OpenAI API Key | `sk-your-tokenlab-key` | | Model Name | `gpt-5.4-mini` | ## Choose a model Copy a current chat model ID from the [Models page](https://tokenlab.sh/en/models). The selected component must use an API format supported by that model. If your application already uses LiteLLM Proxy, connect TokenLab through Langflow's LiteLLM Proxy component instead. --- ### Haystack > Use TokenLab with Haystack OpenAIChatGenerator URL: https://tokenlab.sh/docs/en/integrations/haystack ## Overview Haystack's `OpenAIChatGenerator` supports custom OpenAI-compatible deployments through `api_base_url`. Point it at TokenLab and provide a TokenLab API key. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Generator ```python from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage from haystack.utils import Secret generator = OpenAIChatGenerator( api_key=Secret.from_env_var("TOKENLAB_API_KEY"), api_base_url="https://api.tokenlab.sh/v1", model="claude-sonnet-5", ) response = generator.run( messages=[ChatMessage.from_user("Summarize this document in one sentence.")] ) ``` ## Endpoint Notes Use this path for Haystack chat generator pipelines. For embeddings, configure the matching Haystack embedding component with TokenLab's `/v1` base URL if your pipeline uses TokenLab embeddings. --- ### Graphiti > Use TokenLab as an OpenAI-compatible Graphiti LLM provider URL: https://tokenlab.sh/docs/en/integrations/graphiti ## Overview Graphiti supports OpenAI-compatible `/v1` endpoints through `OpenAIGenericClient`. Point that client at TokenLab and choose a TokenLab model. ## Installation ```bash pip install graphiti-core httpx ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## LLM Client ```python import os from graphiti_core.llm_client.config import LLMConfig from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient llm_config = LLMConfig( api_key=os.environ["TOKENLAB_API_KEY"], model="claude-sonnet-5", small_model="gpt-5.6-luna", base_url="https://api.tokenlab.sh/v1", ) llm_client = OpenAIGenericClient( config=llm_config, structured_output_mode="json_object", ) ``` Pass `llm_client` into your `Graphiti(...)` instance alongside your graph database and embedder configuration. ## Endpoint Notes Graphiti's OpenAI-compatible path is chat-completions oriented. Use models that reliably return structured JSON for extraction and deduplication tasks. --- ### Dify > Integrate TokenLab with Dify using the OpenAI-API-compatible provider and a custom base URL URL: https://tokenlab.sh/docs/en/integrations/dify ## Overview Use the **OpenAI-API-compatible** model-provider plugin to connect Dify to TokenLab. Set the model type to **LLM**, **Completion mode** to **Chat**, and **API Type** to **Chat Completions API**. This guide uses Chat Completions. ## Prerequisites - TokenLab account with API access - Dify Cloud or self-hosted Dify ## Configuration Steps ### Step 1: Get Your API Key 1. Log into [TokenLab Dashboard](https://tokenlab.sh/dashboard) 2. Open [API Keys](https://tokenlab.sh/dashboard/api) 3. Create or copy an API key that starts with `sk-` ### Step 2: Configure the Provider #### Open Model Providers In Dify, go to **Settings** → **Model Providers** #### Select OpenAI-API-compatible Open **OpenAI-API-compatible**. If it is missing, install that model-provider plugin from the Dify Marketplace. Choose **Add model** to enter the TokenLab settings. #### Enter TokenLab Settings Use these values: | Field | Value | |-------|-------| | API Key | `sk-your-tokenlab-key` | | Model type | LLM | | Completion mode | Chat | | API Type | Chat Completions API | | API Base URL | `https://api.tokenlab.sh/v1` | #### Add Models Add the models you want to use, for example: - `gpt-5.4` - `gpt-5.4-mini` - `claude-sonnet-5` - `claude-opus-4-8` - `gemini-3.5-flash` - `gemini-3.1-flash-lite` - `deepseek-v4-flash` ### Step 3: Test Connection 1. Select a model you added, such as `gpt-5.4-mini`. 2. Send a test prompt 3. Confirm Dify receives a valid response ## Embeddings for Knowledge Bases For RAG and knowledge base indexing, add an embedding model such as: - `text-embedding-3-small` - `text-embedding-3-large` Then set it as the default embedding model in the relevant knowledge base or application settings. ## Choose models Copy current chat and embedding model IDs from the [Models page](https://tokenlab.sh/en/models). Dify must configure chat and embeddings separately, and each model must support the API used by that Dify component. ## Best Practices #### OpenAI plugin alternative The separate **OpenAI** plugin can be used for models supported by its catalog. Set its custom base URL to `https://api.tokenlab.sh/v1` and **API Protocol** to **Chat Completions**; its default may be Responses. It is not an older name for OpenAI-API-compatible, and it does not accept every custom model ID. #### Compare models with your own Dify app Test the same representative inputs and compare output quality and final cost in Usage before changing the model. #### This setup uses Chat Completions Keep **Completion mode = Chat** and **API Type = Chat Completions API** for this setup. Responses is a separate API option; this guide does not configure it or WebSocket connections. ## Troubleshooting #### Connection Error - Verify the base URL is exactly `https://api.tokenlab.sh/v1` - Remove trailing slashes if Dify duplicates them - Confirm the Dify server can reach TokenLab over the public internet #### 401 Unauthorized - Double-check the API key - Confirm the key is active in TokenLab dashboard - Make sure the value pasted into Dify does not contain extra spaces or line breaks #### Model Not Found - Verify the model name exactly - Re-add the model entry if the provider UI cached an older value - Check current model availability in TokenLab docs or dashboard --- ### Flowise > Use TokenLab chat models in Flowise workflows URL: https://tokenlab.sh/docs/en/integrations/flowise ## Overview Flowise can call TokenLab through a TokenLab chat node when available, or through an OpenAI-compatible chat model node pointed at TokenLab. ## TokenLab Node If your Flowise build includes a **TokenLab** chat model node: 1. Add **Chat Models** -> **TokenLab** 2. Create a TokenLab API credential 3. Set **Model Name** to `gpt-5.4-mini` 4. Connect it to your chain, agent, or workflow 5. Run a test prompt. ## OpenAI-Compatible Fallback If your build does not include a TokenLab node, add **Chat Models** → **OpenAI Custom Model**. Under **Connect Credential**, create an **OpenAI API** credential with your TokenLab key. Set **Model Name** and **Additional Parameters** → **Base Path** as follows: | Field | Value | |-------|-------| | Base Path | `https://api.tokenlab.sh/v1` | | API Key | `sk-your-tokenlab-key` | | Model Name | `gpt-5.4-mini` | Connect the node to your chain, agent, or workflow, then run a test prompt. ## Choose a model Copy a current chat model ID from the [Models page](https://tokenlab.sh/en/models). The model must support Chat Completions for this node. --- ### Mem0 > Use TokenLab as an OpenAI-compatible LLM provider in Mem0 OSS URL: https://tokenlab.sh/docs/en/integrations/mem0 ## Overview Mem0 OSS can use TokenLab through its OpenAI-compatible LLM configuration by keeping `provider: "openai"` and setting `openai_base_url`. ## Installation ```bash pip install mem0ai ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Python Configuration ```python import os from mem0 import Memory config = { "llm": { "provider": "openai", "config": { "model": "claude-sonnet-5", "api_key": os.environ["TOKENLAB_API_KEY"], "openai_base_url": "https://api.tokenlab.sh/v1", "temperature": 0.1, }, }, "embedder": { "provider": "openai", "config": { "model": "text-embedding-3-small", "api_key": os.environ["TOKENLAB_API_KEY"], "openai_base_url": "https://api.tokenlab.sh/v1", }, }, "vector_store": { "provider": "qdrant", "config": {"path": "./mem0-qdrant"}, }, } memory = Memory.from_config(config) ``` ## Basic Usage ```python memory.add("The user prefers concise engineering summaries.", user_id="demo") results = memory.search("How should I answer?", filters={"user_id": "demo"}) print(results) ``` ## Endpoint Notes This configuration covers Mem0's OpenAI-compatible LLM path. Configure embedders separately according to the vector store and embedding model you use. --- ### Agno > Use TokenLab models in Agno agents URL: https://tokenlab.sh/docs/en/integrations/agno ## Overview Agno agents can call TokenLab through its OpenAI-compatible model client. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## TokenLab Model Wrapper If your Agno version includes the TokenLab model wrapper: ```python from agno.agent import Agent from agno.models.tokenlab import TokenLab agent = Agent(model=TokenLab(id="gpt-5.6-luna")) agent.print_response("Give me a concise project risk checklist.") ``` Some Agno builds also support model strings: ```python agent = Agent(model="tokenlab:gpt-5.4-mini") ``` ## OpenAI-Compatible Setup If your version does not include TokenLab yet, use Agno's OpenAI-compatible model path with: - Base URL: `https://api.tokenlab.sh/v1` - API key: `TOKENLAB_API_KEY` - Model: `gpt-5.4-mini` Model IDs change over time. Choose a current model on the [Models page](https://tokenlab.sh/en/models), then compare quality, speed, and cost on your own agent tasks. --- ### Browser Use > Run Browser Use agents with TokenLab models URL: https://tokenlab.sh/docs/en/integrations/browser-use ## Overview Browser Use can call TokenLab through `ChatOpenAILike`, the OpenAI-compatible chat model wrapper. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Example ```python import os from browser_use import Agent from browser_use.llm.openai.like import ChatOpenAILike llm = ChatOpenAILike( model="claude-sonnet-5", api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", add_schema_to_system_prompt=True, dont_force_structured_output=True, ) agent = Agent( task="Compare the top search results for TokenLab integrations", llm=llm, ) ``` ## Endpoint Notes Use this page for Browser Use workflows that call Chat Completions through an OpenAI-compatible client. If your workflow needs TokenLab native Responses, Anthropic Messages, or Gemini endpoints, use a client path in Browser Use that speaks that protocol directly. --- ### VoltAgent > Use TokenLab models in VoltAgent agents URL: https://tokenlab.sh/docs/en/integrations/voltagent ## Overview VoltAgent accepts AI SDK language models directly. Configure an OpenAI-compatible AI SDK provider with TokenLab's `/v1` base URL and pass the selected model into your agent. ## Installation ```bash npm install @voltagent/core@2 ai@6 @ai-sdk/openai@3 zod ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Direct AI SDK Provider ```ts import { Agent } from "@voltagent/core"; import { createOpenAI } from "@ai-sdk/openai"; const tokenlab = createOpenAI({ apiKey: process.env.TOKENLAB_API_KEY, baseURL: "https://api.tokenlab.sh/v1", }); const agent = new Agent({ name: "tokenlab-agent", instructions: "Answer concisely.", model: tokenlab.chat("claude-sonnet-5"), }); ``` ## Model Router Path If your VoltAgent registry snapshot includes a TokenLab provider entry from `models.dev`, configure: ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" export TOKENLAB_BASE_URL="https://api.tokenlab.sh/v1" ``` Then use the registry model string, for example `tokenlab/claude-sonnet-5`. ## Endpoint Notes The direct AI SDK path is the most explicit setup. Use TokenLab native Responses, Anthropic Messages, or Gemini endpoints only when your selected AI SDK provider/client is configured for that protocol. --- ### Ragas > Evaluate LLM apps with Ragas and TokenLab URL: https://tokenlab.sh/docs/en/integrations/ragas ## Overview Ragas can evaluate TokenLab-backed applications by passing an OpenAI-compatible `AsyncOpenAI` client into `llm_factory`. ## Installation ```bash pip install "ragas==0.4.3" "langchain-community<0.4" openai ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Example Evaluator ```python import os from openai import AsyncOpenAI from ragas.llms import llm_factory from ragas.metrics import DiscreteMetric client = AsyncOpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) llm = llm_factory("claude-sonnet-5", client=client) metric = DiscreteMetric( name="summary_accuracy", allowed_values=["accurate", "inaccurate"], prompt="Evaluate whether the response is accurate. Answer only accurate or inaccurate.\n\nResponse: {response}", ) ``` Use `llm` in Ragas metrics and testsets the same way you would use an OpenAI SDK-backed model. ## Endpoint Notes This Ragas setup uses the OpenAI SDK. Responses, Anthropic Messages, and Gemini require an evaluation client that supports the corresponding format. --- ### Guardrails > Validate TokenLab model outputs with Guardrails URL: https://tokenlab.sh/docs/en/integrations/guardrails ## Overview Configure the OpenAI SDK with the TokenLab Base URL, request JSON, then validate the returned text with `Guard.parse`. ## Installation ```bash pip install guardrails-ai openai ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Example ```python import os from guardrails import Guard from openai import OpenAI from pydantic import BaseModel, Field class Pet(BaseModel): pet_type: str = Field(description="Species of pet") name: str = Field(description="A unique pet name") guard = Guard.for_pydantic(output_class=Pet) client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) response = client.chat.completions.create( model="claude-sonnet-5", response_format={"type": "json_object"}, messages=[{ "role": "user", "content": "Suggest a pet. Return only JSON with string fields pet_type and name.", }], ) outcome = guard.parse(response.choices[0].message.content) print(outcome.validated_output) ``` ## Endpoint Notes Guardrails focuses on validation around the LLM call. Use the OpenAI-compatible path for chat-completions flows, or pass a custom callable if your application needs a native TokenLab endpoint. --- ### Prompt flow > Use TokenLab through Prompt flow OpenAI connections URL: https://tokenlab.sh/docs/en/integrations/promptflow ## Overview Prompt flow's OpenAI connection supports a custom `base_url`. Set it to TokenLab's `/v1` endpoint and use TokenLab model IDs in your flow. ## Installation ```bash pip install promptflow promptflow-tools ``` ## Create A Connection Save the connection below as `openai.yaml`. The command prompts for the API key locally. In an OpenAI flow node, put the TokenLab model ID in `inputs.model`; `deployment_name` is for Azure OpenAI. ```yaml $schema: https://azuremlschemas.azureedge.net/promptflow/latest/OpenAIConnection.schema.json name: open_ai_connection type: open_ai base_url: https://api.tokenlab.sh/v1 api_key: "" ``` ```bash pf connection create --file openai.yaml ``` ## Flow Notes In your `flow.dag.yaml`, keep the connection name aligned with the command above: ```yaml nodes: - name: chat type: llm source: type: code path: chat.jinja2 inputs: model: claude-sonnet-5 api: chat connection: open_ai_connection ``` ## Endpoint Notes This integration uses Prompt flow's OpenAI-compatible connection. Native TokenLab Responses, Anthropic Messages, and Gemini routes require Prompt flow tools or custom Python nodes that call those endpoints directly. --- ### Promptfoo > Run Promptfoo evals against TokenLab models URL: https://tokenlab.sh/docs/en/integrations/promptfoo ## Overview Promptfoo can evaluate TokenLab through its OpenAI-compatible chat and Responses providers. ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Example Config ```yaml description: TokenLab provider example prompts: - 'Answer in one concise paragraph: {{question}}' providers: - id: openai:chat:gpt-5.4-mini label: tokenlab-chat-gpt-5.4-mini config: apiBaseUrl: https://api.tokenlab.sh/v1 apiKey: '{{env.TOKENLAB_API_KEY}}' - id: openai:responses:gpt-5.5 label: tokenlab-responses-gpt-5.5 config: apiBaseUrl: https://api.tokenlab.sh/v1 apiKey: '{{env.TOKENLAB_API_KEY}}' tests: - vars: question: What are two practical ways to evaluate an LLM gateway? assert: - type: contains-any value: - latency - reliability - cost - quality ``` ## Run ```bash promptfoo eval ``` Use chat providers for broad compatibility. Use Responses providers when the eval specifically depends on Responses behavior. --- ### Portkey Gateway > Connect Portkey Gateway to TokenLab URL: https://tokenlab.sh/docs/en/integrations/portkey ## Overview Configure TokenLab as a custom OpenAI-compatible host in Portkey. Use Responses only when your Portkey version supports a custom Responses endpoint. ## Provider Values | Field | Value | |-------|-------| | Provider | `openai` | | Custom Host | `https://api.tokenlab.sh/v1` | | API key | `sk-your-tokenlab-key` | | Model | A current TokenLab model ID | ## Chat Completions Use TokenLab for OpenAI-compatible chat requests: The example requires a running self-hosted Portkey Gateway; replace the gateway URL with your deployment URL. For Portkey Cloud, also send `x-portkey-api-key` with your Portkey API key. See the [Portkey configuration guide](https://portkey.ai/docs/api-reference/inference-api/headers). ```bash curl https://your-portkey-gateway.example/v1/chat/completions \ -H "Content-Type: application/json" \ -H "x-portkey-provider: openai" \ -H "x-portkey-custom-host: https://api.tokenlab.sh/v1" \ -H "Authorization: Bearer sk-your-tokenlab-key" \ -d '{ "model": "gpt-5.6-luna", "messages": [{"role": "user", "content": "Say hello from TokenLab."}] }' ``` ## Responses If your Portkey version supports a custom Responses URL, choose a current Responses-capable model from the [Models page](https://tokenlab.sh/en/models) and use `/v1/responses`. ## Other API Formats Anthropic Messages and Gemini can be used through Portkey only if your Portkey version supports custom URLs for those formats. --- ### Helicone > Observe TokenLab requests through Helicone Gateway URL: https://tokenlab.sh/docs/en/integrations/helicone ## Overview Helicone Gateway can forward OpenAI-compatible requests to TokenLab and record their traces. ## Environment ```bash export HELICONE_API_KEY="your-helicone-key" export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## cURL ```bash curl --request POST \ --url https://gateway.helicone.ai/v1/chat/completions \ --header "Authorization: Bearer $TOKENLAB_API_KEY" \ --header "Helicone-Auth: Bearer $HELICONE_API_KEY" \ --header "Helicone-Target-Url: https://api.tokenlab.sh" \ --header "Helicone-Target-Provider: TokenLab" \ --header "Content-Type: application/json" \ --data '{ "model": "gpt-5.6-luna", "messages": [ { "role": "user", "content": "Say hello from TokenLab through Helicone." } ] }' ``` ## OpenAI SDK ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.TOKENLAB_API_KEY, baseURL: "https://gateway.helicone.ai/v1", defaultHeaders: { "Helicone-Auth": `Bearer ${process.env.HELICONE_API_KEY}`, "Helicone-Target-Url": "https://api.tokenlab.sh", "Helicone-Target-Provider": "TokenLab", }, }); const response = await client.chat.completions.create({ model: "claude-sonnet-5", messages: [{ role: "user", content: "Write one concise product summary." }], }); ``` ## Scope This setup covers OpenAI-compatible `/v1` requests. Send Responses, Anthropic Messages, and Gemini through Helicone only when it supports that format; otherwise call TokenLab directly. --- ### Langfuse > Trace TokenLab OpenAI-compatible requests with Langfuse URL: https://tokenlab.sh/docs/en/integrations/langfuse ## Overview Langfuse can trace OpenAI SDK calls after the SDK Base URL is changed to TokenLab `/v1`. ## Installation ```bash pip install langfuse openai ``` ## Environment ```bash export LANGFUSE_SECRET_KEY="sk-lf-..." export LANGFUSE_PUBLIC_KEY="pk-lf-..." export LANGFUSE_BASE_URL="https://cloud.langfuse.com" export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Example ```python import os from langfuse import get_client, observe from langfuse.openai import OpenAI client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) @observe() def story(): return client.chat.completions.create( model="claude-sonnet-5", messages=[{"role": "user", "content": "Write a short haiku."}], ).choices[0].message.content print(story()) get_client().flush() ``` ## Endpoint Notes This page covers Langfuse tracing for the OpenAI SDK path. If your application uses TokenLab native Responses, Anthropic Messages, or Gemini endpoints, make sure your instrumentation captures that client call path. --- ### OpenLIT > Monitor TokenLab OpenAI-compatible requests with OpenLIT URL: https://tokenlab.sh/docs/en/integrations/openlit ## Overview OpenLIT can record OpenAI SDK calls after the SDK Base URL is changed to TokenLab `/v1`. ## Installation ```bash pip install openlit openai ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Example ```python import os import openlit from openai import OpenAI openlit.init() client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) response = client.chat.completions.create( model="claude-sonnet-5", messages=[{"role": "user", "content": "Hello from OpenLIT"}], ) ``` ## Endpoint Notes OpenLIT follows the SDK instrumentation path. Chat Completions is the safest integration surface; other TokenLab-native calls depend on whether your OpenLIT/OpenTelemetry setup captures that SDK method or HTTP client. --- ### OpenLLMetry > Trace TokenLab OpenAI-compatible requests with OpenLLMetry URL: https://tokenlab.sh/docs/en/integrations/openllmetry ## Overview Configure the OpenAI SDK with TokenLab `/v1`, then use OpenLLMetry to record those SDK calls. ## Installation ```bash pip install opentelemetry-instrumentation-openai opentelemetry-sdk openai ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Example This standalone example writes spans to your terminal. If your application already has an OpenTelemetry tracer provider, reuse that provider and its exporter. ```python import os from openai import OpenAI from opentelemetry import trace from opentelemetry.instrumentation.openai import OpenAIInstrumentor from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor tracer_provider = TracerProvider() tracer_provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter())) trace.set_tracer_provider(tracer_provider) OpenAIInstrumentor().instrument() client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) client.chat.completions.create( model="claude-sonnet-5", messages=[{"role": "user", "content": "Trace this request"}], ) ``` ## Endpoint Notes Use this path for OpenAI SDK-compatible TokenLab calls. For TokenLab native endpoints, instrument the HTTP or SDK client that actually sends those requests. --- ### LangBot > Connect a LangBot bot to TokenLab models URL: https://tokenlab.sh/docs/en/integrations/langbot ## Overview LangBot can call TokenLab through its OpenAI-compatible requester. ## TokenLab Requester If your LangBot build includes a **TokenLab** requester: 1. Open LangBot's model provider settings. 2. Add a provider with requester **TokenLab**. 3. Set the API key to your TokenLab key. 4. Select or create a model such as `gpt-5.4-mini`. 5. Attach the model to the pipeline or bot that should use it. ## Use the OpenAI-compatible requester If TokenLab is not listed yet, use the OpenAI-compatible requester: | Field | Value | |-------|-------| | Requester | OpenAI Chat Completions | | Base URL | `https://api.tokenlab.sh/v1` | | API Key | `sk-your-tokenlab-key` | | Model | `gpt-5.4-mini` | ## Choose a model Copy a current chat model ID from the [Models page](https://tokenlab.sh/en/models). The LangBot component must use an API format accepted by that model. --- ### Phoenix > Trace TokenLab OpenAI-compatible requests with Arize Phoenix URL: https://tokenlab.sh/docs/en/integrations/phoenix ## Overview Phoenix can record calls from an OpenAI Python client whose Base URL points to TokenLab. ## Installation ```bash pip install arize-phoenix openinference-instrumentation-openai openai ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" ``` ## Register Phoenix Start Phoenix locally or use an existing deployment before running this example. By default, `register()` sends traces to the local OTLP/gRPC collector at `http://localhost:4317`. For another deployment, configure the collector endpoint and authentication using the [Phoenix setup guide](https://arize.com/docs/phoenix/sdk-api-reference/python/arize-phoenix-otel). Installing the SDK does not start the collector. ```python from phoenix.otel import register tracer_provider = register( project_name="tokenlab-app", auto_instrument=True, ) ``` ## Call TokenLab ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) response = client.chat.completions.create( model="claude-sonnet-5", messages=[{"role": "user", "content": "Write a short haiku."}], ) print(response.choices[0].message.content) ``` ## Endpoint Notes Phoenix traces the OpenAI SDK call path. Use this page for Chat Completions style TokenLab calls. Other TokenLab native endpoints can still be traced if your application instrumentation captures the client you use for those requests. --- ### Opik > Trace TokenLab OpenAI-compatible requests with Opik URL: https://tokenlab.sh/docs/en/integrations/opik ## Overview Point an OpenAI client to TokenLab, then wrap that client with Opik to record calls. ## Installation ```bash pip install opik openai ``` ## Environment ```bash export TOKENLAB_API_KEY="sk-your-tokenlab-key" export OPIK_PROJECT_NAME="tokenlab-integration-demo" ``` ## Wrap the OpenAI Client Configure Opik before running this example: Opik Cloud requires `OPIK_API_KEY` and, when needed, `OPIK_WORKSPACE`. For an existing self-hosted deployment, set `OPIK_URL_OVERRIDE` to its API URL, such as `http://localhost:5173/api`. See the [Opik configuration guide](https://www.comet.com/docs/opik/tracing/advanced/sdk_configuration). ```python import os from openai import OpenAI from opik.integrations.openai import track_openai client = OpenAI( api_key=os.environ["TOKENLAB_API_KEY"], base_url="https://api.tokenlab.sh/v1", ) openai_client = track_openai(client) ``` ## Log a Chat Completion ```python completion = openai_client.chat.completions.create( model="claude-sonnet-5", messages=[ {"role": "user", "content": "Summarize why LLM observability matters."} ], ) print(completion.choices[0].message.content) ``` ## Endpoint Notes This setup traces the OpenAI-compatible SDK path. If you use TokenLab native endpoints outside the OpenAI SDK, instrument that client or workflow separately. --- ## Chat Apps ### AI Chat Applications > Connect popular chat applications to TokenLab URL: https://tokenlab.sh/docs/en/integrations/ai-chat-apps ## Overview Most chat apps can connect through their OpenAI-compatible settings. Enter `https://api.tokenlab.sh/v1` as the Base URL, paste a TokenLab API key, and choose a current chat model ID. > Note: The app-specific settings below identify the API where a choice is required. Some apps select Responses by model. Use a model that supports the API used by your app; coding tools have separate integration guides. ## Supported Applications | Application | Platform | Notes | |-------------|----------|-------| | Open WebUI | Web, Self-hosted | Supports OpenAI-compatible connections | | LibreChat | Web, Self-hosted | Supports custom OpenAI endpoints | | LobeChat | Web, Desktop, Self-hosted | Supports custom model providers | | Cherry Studio | Windows, macOS, Linux | Supports OpenAI-compatible providers | | Chatbox | Windows, macOS, Linux | Supports OpenAI-compatible settings | | DeepChat | Windows, macOS, Linux | Desktop client with OpenAI-compatible provider settings | | Jan | Desktop, Self-hosted | Local-first app with remote OpenAI-compatible providers | | TypingMind | Web, Desktop | Uses a custom endpoint style | | NextChat | Web, Self-hosted | Lightweight ChatGPT-style client with base-URL overrides | | ChatWise | Windows, macOS | Standard custom provider pattern | ## Default Connection Values | Field | Value | |-------|-------| | Base URL | `https://api.tokenlab.sh/v1` | | API Key | `sk-your-tokenlab-key` | | API Type | OpenAI Compatible | If an app asks for a **full endpoint URL** instead of a base URL, use: - Chat Completions: `https://api.tokenlab.sh/v1/chat/completions` ## Example App Setups #### LobeChat **Self-hosted LobeChat** ```bash OPENAI_API_KEY=sk-your-tokenlab-key OPENAI_PROXY_URL=https://api.tokenlab.sh/v1 ``` **LobeChat Cloud** 1. Open **Settings** → **AI Providers** 2. Select **OpenAI** 3. Enter your TokenLab API key 4. Set the custom endpoint to `https://api.tokenlab.sh/v1` Enable and select a model, then send a test message. LobeChat can automatically use Responses for some GPT models, so choose a TokenLab model that supports the API it uses. #### Cherry Studio Follow the [Cherry Studio setup guide](/integrations/cherry-studio) for connection settings, model selection, and a first test message. #### Chatbox Follow the [Chatbox setup guide](/integrations/chatbox) for connection settings, model selection, and a first test message. #### TypingMind 1. Open **Models** → **Add Custom Models**. 2. Enter: - **Name**: TokenLab - **Endpoint**: `https://api.tokenlab.sh/v1/chat/completions` - **Model ID / Context length**: Copy the exact model ID and context length from [TokenLab Models](https://tokenlab.sh/en/models). - **Custom Headers**: `Authorization: Bearer sk-your-tokenlab-key` 3. Click **Test**, then **Add Model**. Select the new model and send a test message. #### Open WebUI Follow the [Open WebUI setup guide](/integrations/open-webui) for connection settings, model selection, and a first test message. #### DeepChat Follow the [DeepChat setup guide](/integrations/deepchat) for connection settings, model selection, and a first test message. #### Jan Follow the [Jan setup guide](/integrations/jan) for connection settings, model selection, and a first test message. #### LibreChat Follow the [LibreChat setup guide](/integrations/librechat) for connection settings, model selection, and a first test message. ## Choose a Model Copy a current chat model ID from the [Models page](https://tokenlab.sh/en/models). Capabilities, context limits, and prices can change, so do not keep a static model list in setup instructions. ## Tips #### Prefer base URL when the app supports it Most apps are easier to configure and less fragile when you provide `https://api.tokenlab.sh/v1` as a base URL instead of a full endpoint URL. #### Test the model in your app Compare a few current models on the same conversations before choosing a default. #### Keep coding tools separate General chat apps and coding agents often need different endpoint behavior. Keep Codex/Cursor-style tools on their dedicated integration pages. ## Troubleshooting #### Connection Error - Verify the base URL is exactly `https://api.tokenlab.sh/v1` - Remove trailing slashes if the app auto-appends paths - If the app wants a full endpoint URL, use `https://api.tokenlab.sh/v1/chat/completions` #### 401 Unauthorized - Double-check your API key - Confirm the key is active in TokenLab dashboard - Make sure there are no extra spaces or line breaks #### Model Not Available - Verify the model name exactly - Copy the exact model ID from the TokenLab Models page - Check that the model supports Chat Completions --- ### Open WebUI > Use TokenLab as an OpenAI-compatible API connection in Open WebUI URL: https://tokenlab.sh/docs/en/integrations/open-webui ## Overview Open WebUI supports custom OpenAI-compatible API connections. Add TokenLab as a remote OpenAI-compatible endpoint and refresh the model list. ## Admin Connection 1. Open **Admin Panel** -> **Settings** -> **Connections**. 2. In the OpenAI API connection area, add a connection. 3. Set the base URL to `https://api.tokenlab.sh/v1`. 4. Set the API key to your TokenLab key. 5. Save the connection and refresh models. ## Values | Field | Value | |-------|-------| | API Base URL | `https://api.tokenlab.sh/v1` | | API Key | `sk-your-tokenlab-key` | | Example model | `gpt-5.4-mini` | ## Choose a model Refresh the model list or copy a current chat model ID from the [Models page](https://tokenlab.sh/en/models). To specify model IDs manually, edit this connection and open **Advanced** → **Model IDs (Filter)**. Add the exact TokenLab model IDs, then save. --- ### Chatbox > Use TokenLab in Chatbox through the OpenAI-compatible provider flow URL: https://tokenlab.sh/docs/en/integrations/chatbox ## Overview Chatbox can use TokenLab through its OpenAI-compatible API settings. ## Configure Chatbox 1. Open **Settings** -> **Model Provider** -> **Add** -> **Add Custom Provider** 2. Set **Name** to TokenLab and **API Mode** to **OpenAI API Compatible**, then click **Add**. 3. Enter: - **API Host**: `https://api.tokenlab.sh/v1` - **API Path**: `/chat/completions` - **API Key**: `sk-your-tokenlab-key` - **Model**: `gpt-5.4-mini` 4. Save and send a short test message. ## Choose a model Use a current chat model ID from the [Models page](https://tokenlab.sh/en/models). If Chatbox does not list it automatically, add the exact ID manually. ## Notes If your Chatbox build already lists TokenLab, select it directly. Otherwise use the OpenAI-compatible settings above. --- ### DeepChat > Connect DeepChat to TokenLab with an OpenAI-compatible base URL URL: https://tokenlab.sh/docs/en/integrations/deepchat ## Overview DeepChat works with TokenLab through a standard OpenAI-compatible provider configuration. ## Configure DeepChat 1. Open **Settings** -> **Model Provider** 2. Add or edit an OpenAI-compatible provider 3. Enter: - **Provider name**: TokenLab - **API Type**: **OpenAI Completions** (Chat Completions) - **Base URL**: `https://api.tokenlab.sh/v1` - **API Key**: `sk-your-tokenlab-key` 4. Add a model such as `gpt-5.4-mini` 5. Save and test a chat. ## Choose a model Copy a current chat model ID from the [Models page](https://tokenlab.sh/en/models). DeepChat can add an ID manually when it is not shown in the picker. ## Troubleshooting - Use the base URL, not the full `/chat/completions` path, unless DeepChat explicitly asks for a full endpoint URL. - Remove trailing spaces from the API key. - If a model is not listed, add the exact TokenLab model id manually. --- ### Jan > Connect Jan to TokenLab URL: https://tokenlab.sh/docs/en/integrations/jan ## Overview Add TokenLab to Jan as a remote OpenAI-compatible service. ## Configure Jan 1. Open **Settings** -> **Providers** 2. Add a remote OpenAI-compatible provider 3. Enter: - **Name**: TokenLab - **Base URL**: `https://api.tokenlab.sh/v1` - **API Key**: `sk-your-tokenlab-key` 4. Add or select `gpt-5.4-mini` 5. Save and start a new thread. ## Refresh models TokenLab exposes model discovery at: ```text https://api.tokenlab.sh/v1/models ``` If your Jan build supports remote model discovery for TokenLab, refresh the model list. Otherwise, add model ids manually. ## Choose a model Refresh Jan's model list or copy a current chat model ID from the [Models page](https://tokenlab.sh/en/models). --- ### LibreChat > Configure LibreChat to use TokenLab as a custom OpenAI-compatible endpoint URL: https://tokenlab.sh/docs/en/integrations/librechat ## Overview Add TokenLab to LibreChat as a custom OpenAI-compatible endpoint. ## Environment Add this variable to the `.env` file in your LibreChat project root: ```bash TOKENLAB_API_KEY=sk-your-tokenlab-key ``` ## Example Endpoint In the LibreChat project root, create `librechat.yaml` from `librechat.example.yaml` if needed. Keep its required `version` and existing settings, then merge this entry into `endpoints.custom`: ```yaml endpoints: custom: - name: 'TokenLab' apiKey: '${TOKENLAB_API_KEY}' baseURL: 'https://api.tokenlab.sh/v1' models: default: - 'gpt-5.5' - 'gpt-5.6-terra' - 'gpt-5.6-luna' - 'claude-opus-4-8' - 'claude-sonnet-5' - 'gemini-3.5-flash' - 'deepseek-v4-pro' - 'deepseek-v4-flash' fetch: true titleConvo: true titleModel: 'gpt-5.6-luna' modelDisplayLabel: 'TokenLab' ``` For Docker, mount this file as `/app/librechat.yaml` in the API container. Restart the same LibreChat deployment and select TokenLab. See [LibreChat's setup guide](https://www.librechat.ai/docs/configuration/librechat_yaml). ## Notes LibreChat uses the OpenAI-compatible chat path for this setup. TokenLab also exposes native Responses, Anthropic Messages, and Gemini-compatible endpoints for clients that support those request shapes. --- ### Cherry Studio > Connect Cherry Studio to TokenLab URL: https://tokenlab.sh/docs/en/integrations/cherry-studio ## Overview Select TokenLab directly if it appears in Cherry Studio. Otherwise add it as a custom OpenAI-compatible service. ## Configure as a Custom Provider 1. Open **Settings** -> **Model Provider** 2. Add an OpenAI-compatible provider 3. Enter: - **Name**: TokenLab - **API Base URL**: `https://api.tokenlab.sh/v1` - **API Key**: `sk-your-tokenlab-key` 4. Add a model such as `gpt-5.4-mini` 5. Complete **Verify and Enable** in the setup dialog. For manual setup, save and confirm the provider is enabled. 6. Select the added model and send a short test message. ## Built-in TokenLab option When TokenLab appears as a built-in provider in Cherry Studio, prefer that option. It can carry endpoint metadata for: - OpenAI-compatible chat completions - OpenAI Responses - Anthropic Messages - Gemini generateContent Choose current chat models from the [Models page](https://tokenlab.sh/en/models). Add only the models you plan to use. ---