Docs index: /llms.txt · every page as Markdown: add .md

‹ API reference

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.

MethodPathPurposeCredits
POST/v1/validateCheck an API secretfree
POST/v1/runtime-tokens/requestExchange the secret for a short-lived runtime token (the SDKs do this for you)free
POST/v1/runtime-tokens/mintA one-hour token scoped to one agent: for the LiveKit plugin ("scope": "livekit-cloud", 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 devicefree
POST/v1/embed-tokens/requestA 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_KEY is a deprecated alias of BITHUMAN_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

StatusCodeCauseFix
401MISSING_AUTHno api-secret header (/v1/validate answers valid: false instead)send the header on every request
401UNAUTHORIZEDthe secret is invalid, or a revoked secret on a REST endpointcheck it with /v1/validate; create a new one
403RUNTIME_SUSPENDEDa revoked secret on a token endpoint (/v1/runtime-tokens/*, /v1/embed-tokens/request)create a new secret and move your services to it
403RUNTIME_SUSPENDEDruntime access is suspended for the account; the message says socontact support
403PLAN_REQUIREDfrom 2026-10-19, any API secret of a Free accountchoose a plan; a new secret does not help
403SECRET_REVEAL_CONSOLE_ONLYreading a stored secret’s value with an API secretreveal secrets in the Console

All codes: Errors.