Authentication
Authenticate with the api-secret header, check a secret, and use short-lived tokens.
Every REST call carries your API secret in the api-secret header. The same secret works for the SDKs and the CLI (Your API secret). Create one under Developer → API Secrets; the value is shown once.
| Method | Path | Purpose | Credits |
|---|---|---|---|
POST | /v1/validate | Check an API secret | free |
POST | / | Exchange the secret for a short-lived runtime token (the SDKs do this for you) | free |
POST | / | A one-hour token scoped to one agent: for the LiveKit plugin ("scope":, bound to a room when you send room_name/livekit_url) or, without scope, a Bearer credential for downloading that agent’s model on a device | free |
POST | / | A one-hour token for a browser embed (Embedding) | free |
POST /v1/validate
POST /v1/validate validateApiSecret
Checks the secret in the api-secret header. Always returns 200; read valid. A request with no header, or with the secret in Authorization: Bearer instead of api-secret, also answers 200 {"valid": false}, not 401.
Example
curl
curl -s -X POST https://api.bithuman.ai/v1/validate -H "api-secret: $BITHUMAN_API_SECRET"
Python
import os, requests
r = requests.post("https://api.bithuman.ai/v1/validate", headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]})
print(r.json())
Node
const resp = await fetch("https://api.bithuman.ai/v1/validate", {
method: "POST",
headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
Response
{"valid": true}POST /v1/runtime-tokens/request
POST /v1/runtime-tokens/request requestRuntimeToken
Exchanges the API secret for a short-lived runtime token that authorizes rendering for your account. The Python SDK and the LiveKit plugin call it for you and renew the token while a session runs; call it yourself only when you build your own runtime integration. A runtime token cannot create other tokens or call other endpoints.
Sent with mode, the same endpoint starts a cloud avatar in your LiveKit room instead: Cloud avatar without the plugin.
curl
curl -X POST https://api.bithuman.ai/v1/runtime-tokens/request \
-H "api-secret: $BITHUMAN_API_SECRET" -H "Content-Type: application/json" \
-d '{"agent_code": "'"$BITHUMAN_AGENT_CODE"'"}'
Python
import os, requests
resp = requests.post(
"https://api.bithuman.ai/v1/runtime-tokens/request",
headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
json={"agent_code": os.environ["BITHUMAN_AGENT_CODE"]},
)
print(resp.json())
Node
const resp = await fetch("https://api.bithuman.ai/v1/runtime-tokens/request", {
method: "POST",
headers: {
"api-secret": process.env.BITHUMAN_API_SECRET,
"Content-Type": "application/json",
},
body: JSON.stringify({ agent_code: process.env.BITHUMAN_AGENT_CODE }),
});
console.log(await resp.json());
POST /v1/runtime-tokens/mint
POST /v1/runtime-tokens/mint mintScopedRuntimeToken
Mints a one-hour token for "scope": "livekit-cloud" that can only start one agent’s avatar in one LiveKit room. Pass it to the LiveKit plugin instead of your secret, because the plugin writes its credential into room attributes every participant can read. Send room_name and livekit_url to bind the LiveKit token to one room and server. A complete worker is on LiveKit.
curl
curl -X POST https://api.bithuman.ai/v1/runtime-tokens/mint \
-H "api-secret: $BITHUMAN_API_SECRET" -H "Content-Type: application/json" \
-d '{"agent_code": "'"$BITHUMAN_AGENT_CODE"'", "scope": "livekit-cloud", "room_name": "your-room", "livekit_url": "wss://your-project.livekit.cloud"}'
Python
import os, requests
resp = requests.post(
"https://api.bithuman.ai/v1/runtime-tokens/mint",
headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
json={
"agent_code": os.environ["BITHUMAN_AGENT_CODE"],
"scope": "livekit-cloud",
"room_name": "your-room",
"livekit_url": "wss://your-project.livekit.cloud",
},
)
print(resp.json())
Node
const resp = await fetch("https://api.bithuman.ai/v1/runtime-tokens/mint", {
method: "POST",
headers: {
"api-secret": process.env.BITHUMAN_API_SECRET,
"Content-Type": "application/json",
},
body: JSON.stringify({
agent_code: process.env.BITHUMAN_AGENT_CODE,
scope: "livekit-cloud",
room_name: "your-room",
livekit_url: "wss://your-project.livekit.cloud",
}),
});
console.log(await resp.json());
Keep the secret safe
- Keep it in the environment or a secrets manager, never in source, a command line or an app bundle.
- Browsers get an embed token; LiveKit rooms get a minted token; shipped apps fetch a credential from your backend.
BITHUMAN_API_KEYis a deprecated alias ofBITHUMAN_API_SECRET.
Rotate a secret
Create the new secret, move your services to it, then revoke the old one under API Secrets. Revocation is immediate: sessions still using the old secret stop at their next usage report. The CLI’s per-device secrets (cli@<hostname>) revoke one machine at a time. The API equivalents are on API secrets.
Errors
| Status | Code | Cause | Fix |
|---|---|---|---|
401 | MISSING_AUTH | no api-secret header (/v1/validate answers valid: false instead) | send the header on every request |
401 | UNAUTHORIZED | the secret is invalid, or a revoked secret on a REST endpoint | check it with /v1/validate; create a new one |
403 | RUNTIME_ | a revoked secret on a token endpoint (/, /) | create a new secret and move your services to it |
403 | RUNTIME_ | runtime access is suspended for the account; the message says so | contact support |
403 | PLAN_REQUIRED | from 2026-10-19, any API secret of a Free account | choose a plan; a new secret does not help |
403 | SECRET_ | reading a stored secret’s value with an API secret | reveal secrets in the Console |
All codes: Errors.