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

‹ API reference

Runtime sessions

List live sessions, read a transcript, end a session, or revoke every runtime key.

Overview

See and control the avatar sessions running on your account: list live sessions with the credits they use per minute, read a session’s transcript, terminate one, or (in an emergency) revoke every runtime key at once.

Which one to use:

Base URL https://api.bithuman.ai. Authenticate with your api-secret. The {user_id} in the path is your own account id — get it from GET /v1/me.

Set it once in your shell before the examples below — with $USER_ID unset the paths collapse to /v2//… and the API answers 404 NOT_FOUND:

export USER_ID=$(curl -s https://api.bithuman.ai/v1/me \
  -H "api-secret: $BITHUMAN_API_SECRET" \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['user_id'])")

List sessions

GET /v2/{user_id}/runtime-sessions listRuntimeSessions

GET /v2/{user_id}/runtime-sessions: your sessions, plus account-wide totals.

QueryTypeDefaultDescription
windowstringalllive (only running), recent (idle + ended), or all.
kindstringallconversations (cloud), self_hosted, or all.
limitint50Max sessions to return (1–200).

The totals (active_count, live_burn_rate_cr_per_min, credits_this_hour) are account-wide over the last hour; sessions is the filtered list. billing_type is the pricing code the session bills under; see GET /v1/pricing. Use a session’s id for the calls below.

curl

curl "https://api.bithuman.ai/v2/$USER_ID/runtime-sessions?window=live" \
  -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.get(
    f"https://api.bithuman.ai/v2/{os.environ['USER_ID']}/runtime-sessions?window=live",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch(`https://api.bithuman.ai/v2/${process.env.USER_ID}/runtime-sessions?window=live`, {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{
  "success": true,
  "data": {
    "active_count": 1,
    "live_burn_rate_cr_per_min": 4,
    "credits_this_hour": 52,
    "sessions": [
      {
        "id": "a3f1c8e2-…-9b02",
        "agent_code": "A80HVD8577",
        "agent_name": "Greeter",
        "billing_type": "usage_essence_2_model_cloud",
        "key_alias": "prod-server",
        "room_name": "room_a3f1c8e2",
        "started": "2026-07-15T15:20:00Z",
        "last_seen": "2026-07-15T15:33:00Z",
        "minutes": 13.0,
        "credits": 52,
        "burn_rate_cr_per_min": 4,
        "status": "live",
        "has_transcript": true
      }
    ]
  }
}

Read a transcript

GET /v2/{user_id}/runtime-sessions/{session_id}/messages getRuntimeSessionMessages

GET /v2/{user_id}/runtime-sessions/{session_id}/messages — the conversation, oldest first.

Only cloud voice/chat sessions record a transcript; self-hosted sessions return messages: [].

curl

curl "https://api.bithuman.ai/v2/$USER_ID/runtime-sessions/a3f1c8e2-…/messages" \
  -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.get(
    f"https://api.bithuman.ai/v2/{os.environ['USER_ID']}/runtime-sessions/a3f1c8e2-…/messages",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch(`https://api.bithuman.ai/v2/${process.env.USER_ID}/runtime-sessions/a3f1c8e2-…/messages`, {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{
  "success": true,
  "data": {
    "session_id": "a3f1c8e2-…",
    "agent_code": "A80HVD8577",
    "started": "2026-07-15T15:20:00Z",
    "ended": "2026-07-15T15:33:00Z",
    "live": true,
    "messages": [
      { "timestamp": "2026-07-15T15:20:03Z", "role": "user", "message": "Hi there" },
      { "timestamp": "2026-07-15T15:20:05Z", "role": "assistant", "message": "Hello! How can I help?" }
    ]
  }
}

End a cloud avatar session

POST /v1/runtime-sessions/{session_id}/end endCloudAvatarSession

POST /v1/runtime-sessions/{session_id}/end — end a cloud avatar you started. session_id is the session_id the start returned. No body.

Billing stops at the end call: the session is billed its active time up to that moment, and nothing after. Its concurrent-session slot frees at once.

When ended is true, the avatar has left your room. When it is false, note says when it leaves: within about a minute, or, for an essence-1 avatar, when you remove it or the room closes. Deleting the room removes it at once. Your LiveKit room is never deleted. Calling it again returns the same answer and bills nothing. Errors: 404 no such session (or not yours).

curl

curl -X POST "https://api.bithuman.ai/v1/runtime-sessions/$SESSION_ID/end" \
  -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.post(
    f"https://api.bithuman.ai/v1/runtime-sessions/{os.environ['SESSION_ID']}/end",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch(`https://api.bithuman.ai/v1/runtime-sessions/${process.env.SESSION_ID}/end`, {
  method: "POST",
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{ "session_id": "cs_4f1c…", "status": "ended", "agent_id": "A23KSG5258",
  "model": "essence-2", "room_name": "my-room", "started_at": "…", "ended_at": "…",
  "billed_seconds": 131.2, "credits": 9, "ended": true, "slot_released": true,
  "note": "Session ended: the avatar left the room and billing stopped." }

Read a cloud avatar session

GET /v1/runtime-sessions/{session_id} getCloudAvatarSession

GET /v1/runtime-sessions/{session_id} — status (starting, live or ended), times, billed_seconds and credits so far. Errors: 404 no such session (or not yours).

curl

curl "https://api.bithuman.ai/v1/runtime-sessions/$SESSION_ID" \
  -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.get(
    f"https://api.bithuman.ai/v1/runtime-sessions/{os.environ['SESSION_ID']}",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch(`https://api.bithuman.ai/v1/runtime-sessions/${process.env.SESSION_ID}`, {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());

Terminate a session

POST /v2/{user_id}/runtime-sessions/{session_id}/terminate terminateRuntimeSession

POST /v2/{user_id}/runtime-sessions/{session_id}/terminate — stop one session. Never touches an API secret.

FieldTypeRequiredDescription
reasonstringnoFree-text stop reason. Default user-terminated.

The session stops counting toward your plan’s concurrent sessions at once. What else happens depends on where it runs:

  • A conversation on bitHuman’s servers (the web embed, the Console): its room is closed and ended is true.
  • A cloud avatar in your own LiveKit room: billing stops, ended is false, and the avatar leaves at its next billing check, within about a minute. An essence-1 avatar stays until you remove it or the room closes. Delete the room on your LiveKit server to end it at once.
  • A self-hosted runtime: the activity record is closed; stop the process on your hardware.

To find a cloud avatar’s session_id, list sessions with window=live&kind=conversations and take the id whose room_name is your room. A cloud avatar you started can instead be ended by the session_id its start returned (cs_…). Errors: 404 no such session (or not yours).

curl

curl -X POST "https://api.bithuman.ai/v2/$USER_ID/runtime-sessions/a3f1c8e2-…/terminate" \
  -H "api-secret: $BITHUMAN_API_SECRET" -H "content-type: application/json" -d '{}'

Python

import os, requests

resp = requests.post(
    f"https://api.bithuman.ai/v2/{os.environ['USER_ID']}/runtime-sessions/a3f1c8e2-…/terminate",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={},
)
print(resp.json())

Node

const resp = await fetch(`https://api.bithuman.ai/v2/${process.env.USER_ID}/runtime-sessions/a3f1c8e2-…/terminate`, {
  method: "POST",
  headers: {
    "api-secret": process.env.BITHUMAN_API_SECRET,
    "content-type": "application/json",
  },
  body: JSON.stringify({  }),
});
console.log(await resp.json());
{
  "success": true,
  "data": { "session_id": "a3f1c8e2-…", "closed": true, "ended": true, "self_hosted": false,
            "note": "Session ended — the avatar disconnected and the conversation closed." }
}

Revoke all keys

POST /v2/{user_id}/runtime/revoke-all revokeAllRuntimeKeys

POST /v2/{user_id}/runtime/revoke-all — the emergency stop. Disables every runtime key on the account (e.g. a leak). Live sessions stop within one refresh cycle (~5 min).

FieldTypeRequiredDescription
reasonstringnoFree-text reason.

This does not delete your API secrets — it suspends runtime token issuance. It’s self-recoverable: creating a new API secret clears the suspension.

The two calls below are made for you by the bitHuman SDKs, the CLI and the plugins on self-hosted and on-device sessions; you need them only to build your own runtime.

curl

curl -X POST "https://api.bithuman.ai/v2/$USER_ID/runtime/revoke-all" \
  -H "api-secret: $BITHUMAN_API_SECRET" -H "content-type: application/json" -d '{}'

Python

import os, requests

resp = requests.post(
    f"https://api.bithuman.ai/v2/{os.environ['USER_ID']}/runtime/revoke-all",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={},
)
print(resp.json())

Node

const resp = await fetch(`https://api.bithuman.ai/v2/${process.env.USER_ID}/runtime/revoke-all`, {
  method: "POST",
  headers: {
    "api-secret": process.env.BITHUMAN_API_SECRET,
    "content-type": "application/json",
  },
  body: JSON.stringify({  }),
});
console.log(await resp.json());
{
  "success": true,
  "data": { "revoked": true, "effective_in_seconds": 320, "runtime_suspended": true,
            "note": "All runtime keys disabled. Creating a new API key restores runtime access." }
}

Live key check for a self-hosted runtime

POST /v1/auth/validate validateSelfHostedKey

The bitHuman SDKs, the CLI and the plugins call this for you when a self-hosted session starts and while it runs; you call it only if you build your own runtime. Deleting a key in the dashboard stops a running self-hosted session at its next check.

A runtime treats the three answers differently:

statusmeaningwhat a runtime should do
200validserve
401definite: unknown, revoked or disabledstop
503we cannot tell right nowretry, keep serving

A 503 is never a statement about your key: keep serving and retry.

ParameterInTypeRequiredDescription
billing_typebodystringnoThe self-hosted product this runtime serves. Always send the key for the product you run.

curl

curl -X POST https://api.bithuman.ai/v1/auth/validate \
  -H "Authorization: Bearer $BITHUMAN_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"billing_type": "self-hosted-essence-2-model"}'
# → {"user_id": "...", "key_id": "key_1a2b3c4d5e6f7a8b"}

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/auth/validate",
    headers={"Authorization": f"Bearer {os.environ['BITHUMAN_API_SECRET']}"},
    json={"billing_type": "self-hosted-essence-2-model"},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/auth/validate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BITHUMAN_API_SECRET}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ billing_type: "self-hosted-essence-2-model" }),
});
console.log(await resp.json());
{
  "key_id": "key_1a2b3c4d5e6f7a8b"
}

Report usage from a self-hosted session

POST /v1/meter/beats postMeterBeat

The bitHuman SDKs, the CLI and the plugins call this for you: a self-hosted or on-device session reports its running time here, and that is what bills it (Pricing). You call it only if you build your own runtime.

A report covers the seconds the session ran, idle included, not frames rendered. The product (billing_type) is fixed by the first report of a session.

Read only the status code:

statusmeaningwhat a runtime should do
2xxthe interval is recordedcount it as reported
402balance depleted / account suspendedkeep rendering, report it again later
401definite bad keystop
503we cannot tell right nowretry

A 402 never stops a running session; the next session is refused until you top up. A retried report (same session_id, install_id and seq) is never charged twice.

ParameterInTypeRequiredDescription
session_idbodystringyesStable id for the session being billed.
install_idbodystringnoStable id for this installation.
seqbodyintegeryesBeat sequence number within the session.
served_sbodynumberyesSeconds of session covered by this beat. Clamped server-side; wall-clock, idle included.
billing_typebodystringnoThe self-hosted product wire key. Always send the key for the product you run.

curl

curl -X POST https://api.bithuman.ai/v1/meter/beats \
  -H "Authorization: Bearer $BITHUMAN_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"s-1","install_id":"i-1","seq":1,
       "served_s":60,"billing_type":"self-hosted-essence-2-model"}'

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/meter/beats",
    headers={"Authorization": f"Bearer {os.environ['BITHUMAN_API_SECRET']}"},
    json={
        "session_id": "s-1",
        "install_id": "i-1",
        "seq": 1,
        "served_s": 60,
        "billing_type": "self-hosted-essence-2-model",
    },
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/meter/beats", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BITHUMAN_API_SECRET}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    session_id: "s-1",
    install_id: "i-1",
    seq: 1,
    served_s: 60,
    billing_type: "self-hosted-essence-2-model",
  }),
});
console.log(await resp.json());