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

‹ API reference

Agents

Create an avatar agent from a portrait, then manage it and make it speak.

An agent is an avatar (face, voice and persona) identified by a short code such as A23WJF0199. Create one, poll until it is ready, then use it everywhere: the web embed, the SDKs, talking video and live sessions. Creation and model adds cost credits per model (pricing); everything else on this page is free.

The sample avatars (such as A23WJF0199, wise-pup) only download; every other call on this page needs an agent you own, and returns 404 for a sample.

To add knowledge to a live session, see Knowledge; every operation is also in the API reference index.

Generate an agent

POST /v1/agent/generate generateAgent

Starts an asynchronous creation and returns an agent_id at once. Credits are reserved at submit and refunded automatically if creation fails. Pick the model from the subject: a real person → essence-2; a cartoon, animal, robot or any other character → expression-2 (Choosing a model).

Request

ParameterTypeRequiredDescription
modelstringalways send itessence-2 (a real person), expression-2 (any character) or auto (picks from the image). essence-1 and expression-1 are the first generation. Always send it: it becomes required on 2026-12-26 (see Notes).
imagestringnoPortrait URL (publicly fetchable) or base64. Used as a reference; a portrait is generated from prompt when omitted
promptstringnoSystem prompt and personality
audiostringnoVoice sample URL or base64, for voice cloning
aspect_ratiostringno16:9 (default), 9:16 or 1:1
framingstringnoportrait (default) or full_body
transparencybooleannotrue generates on a green-screen background for chroma key
agent_idstringnoLeave it out; a fresh code is generated. If it names an agent you already own, that agent is regenerated in place (it returns to processing); another account’s code returns 404

A model outside your plan gets 403 PLAN_REQUIRED.

Headers: api-secret, and optionally Idempotency-Key: a repeated request with the same key returns the first response and starts no second creation.

Notes

  • Omitted, model still creates an expression-1 agent and warns (MODEL_DEFAULT_DEPRECATED), so a character is refused with 422. From 2026-12-26 model is required, and the bare names essence and expression (and the version field) are refused with a 400 naming the replacement.
  • Creation takes minutes for essence-2, essence-1 and expression-1, and longer for expression-2. Set your polling timeout per model.
  • Every model except expression-2 needs a clear, real human face. For essence-2, essence-1 or expression-1, a cartoon, stylized character, animal, robot or creature in the photo or prompt, or a photo with no face found, returns 422 MODEL_SUBJECT_MISMATCH before anything is charged; the message tells you to use Expression 2. An essence-2 creation or add also checks the photo’s face before charging: a face too small in frame, or several similar-sized faces, returns 422 IMAGE_FACE_UNSUITABLE — upload a closer photo of one person. auto routes people to essence-2 and everything else to expression-2.
  • A 200 does not mean the image was fetched. An unreachable image fails the creation a few seconds later (refunded); poll status to confirm. Creation is image-only: a video field returns 400 VIDEO_INPUT_NOT_SUPPORTED.
  • At most two essence-2 creations run at once per account; a third fails at once with a capacity message and no charge.

Example

curl

curl -X POST https://api.bithuman.ai/v1/agent/generate \
  -H "Content-Type: application/json" \
  -H "api-secret: $BITHUMAN_API_SECRET" \
  -H "Idempotency-Key: museum-guide-1" \
  -d '{"model": "expression-2", "prompt": "You are a cheerful museum guide.", "image": "https://example.com/portrait.jpg"}'

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/agent/generate",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={"model": "expression-2", "prompt": "You are a cheerful museum guide.", "image": "https://example.com/portrait.jpg"},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/generate", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "api-secret": process.env.BITHUMAN_API_SECRET,
    "Idempotency-Key": "museum-guide-1",
  },
  body: JSON.stringify({
    model: "expression-2",
    prompt: "You are a cheerful museum guide.",
    image: "https://example.com/portrait.jpg",
  }),
});
console.log(await resp.json());

Response

{"success": true, "message": "Agent generation started", "agent_id": "A80HVD8577", "status": "processing"}

Poll status

GET /v1/agent/status/{agent_id} getAgentStatus

Reports a creation’s progress. Poll every 5 seconds until status is ready or failed. On the way status takes other values, such as generating and success, which are not final. A boolean final field, true only for ready and failed, is coming; until it appears in responses, stop on ready or failed. A missing or mistyped agent_id returns 404 NOT_FOUND. The response is the agent’s full record, the same as Get an agent; the fields below are the ones to poll.

current_stepProgressStage
paymentabout 2%credits reserved
persona5–15%persona prepared
voice_imageabout 20%voice and portrait
videoabout 45%identity video (Essence models)
lip_sync70–99%the model step; the longest for second-generation models
done100%ready

Notes

  • supported_models lists the models the agent can be launched as, spelled as model values you can send back.
  • model_status gives each requested model’s state (pending, ready, failed). Models never requested are absent.
  • For second-generation models model_url is a storage reference, not a link: fetch the file with Download an agent’s model.
  • The downloadable model file is published shortly after ready; until then the download returns 404 MODEL_ARTIFACT_NOT_READY. Retry.

curl

curl https://api.bithuman.ai/v1/agent/status/A80HVD8577 -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.get(
    "https://api.bithuman.ai/v1/agent/status/A80HVD8577",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/status/A80HVD8577", {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{"success": true, "data": {"agent_id": "A80HVD8577", "status": "ready", "progress": 1.0, "current_step": "done", "error_message": null, "supported_models": ["expression-2"], "model_status": {"expression-2": {"state": "ready", "reason": null}}, "name": "Museum Guide"}}
import os, time, requests

def wait_until_ready(agent_id, timeout_s=3 * 3600):
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"https://api.bithuman.ai/v1/agent/status/{agent_id}",
                         headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]}, timeout=30)
        if r.ok:
            data = r.json()["data"]
            if data["status"] in ("ready", "failed"):
                if data["status"] == "failed":
                    raise RuntimeError(data["error_message"])
                return data
        time.sleep(5)   # creation continues server-side through transient errors
    raise TimeoutError(agent_id)

Get an agent

GET /v1/agent/{code} getAgent

Returns the agent’s full record: persona (system_prompt, name, language), voice_id, media URLs, creation state, model and supported_models.

curl

curl https://api.bithuman.ai/v1/agent/A80HVD8577 -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

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

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/A80HVD8577", {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{"success": true, "data": {"code": "A80HVD8577", "status": "ready", "model": "expression-2", "supported_models": ["expression-2"], "name": "Museum Guide", "system_prompt": "You are a cheerful museum guide.", "language": "en", "voice_id": "bf0a246a-8642-498a-9950-80c35e9276b5", "image_url": "https://…/image.jpg"}}

List your agents

GET /v1/agents listAgents

Lists your agents, newest first. Query: limit (default 20, max 100), offset, status (for example ready). Items are summaries (code, name, model, status, supported_models, created_at and a few more); read the full record with Get an agent. Deleted agents appear with status: "deleted". Older agents can also show completed, success or unknown; read supported_models to see what they can launch as.

curl

curl "https://api.bithuman.ai/v1/agents?status=ready&limit=20" -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.get(
    "https://api.bithuman.ai/v1/agents?status=ready&limit=20",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/agents?status=ready&limit=20", {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{"success": true, "data": [{"code": "A80HVD8577", "name": "Museum Guide", "model": "expression-2", "status": "ready"}], "pagination": {"limit": 20, "offset": 0, "total": 1, "has_more": false}}

Update an agent

POST /v1/agent/{code} updateAgent

POST /v1/agent/{code} (not PATCH, which returns 404) changes the system_prompt, the voice-provider selection (providers, see Voice providers), or both. Send at least one, or the call returns 400 MISSING_PARAM. The name is generated and cannot be set. The voice cannot be set here: change it in the Console (Voices).

curl

curl -X POST https://api.bithuman.ai/v1/agent/A80HVD8577 \
  -H "Content-Type: application/json" -H "api-secret: $BITHUMAN_API_SECRET" \
  -d '{"system_prompt": "You are a concise sales assistant."}'

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/agent/A80HVD8577",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={"system_prompt": "You are a concise sales assistant."},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/A80HVD8577", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "api-secret": process.env.BITHUMAN_API_SECRET,
  },
  body: JSON.stringify({ system_prompt: "You are a concise sales assistant." }),
});
console.log(await resp.json());
{"agent_code": "A80HVD8577", "updated": true}

Delete an agent

DELETE /v1/agent/{code} deleteAgent

Deletes an agent you own. Usage history is kept. An unknown or unowned code returns 404.

curl

curl -X DELETE https://api.bithuman.ai/v1/agent/A80HVD8577 -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.delete(
    "https://api.bithuman.ai/v1/agent/A80HVD8577",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/A80HVD8577", {
  method: "DELETE",
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{"success": true, "agent_code": "A80HVD8577", "deleted": true}

Add a model to an existing agent

POST /v1/agent/{code}/models addAgentModel

Adds a model to a ready agent without re-creating it; the body is {"model": "<model>"}. Re-adding a model the agent has costs nothing, and a failed add is refunded.

modelNeedsTimeCredits
expression-1a stored image and voice, of a real personimmediatefree
expression-2a stored imagetakes longer to build2000
essence-2a stored identity video and a photoreal personabout 2–2.5 h (an add still trains; a new essence-2 agent is ready in minutes)500
essence-1a stored image or identity video, of a real person10–20 min250

Poll status until model_status["essence-2"].state is ready or failed. The top-level status stays ready during an add. A failed add is refunded, and its reason says why. Errors: 409 AGENT_NOT_READY, 422 MODEL_PREREQUISITE_MISSING, 422 MODEL_SUBJECT_MISMATCH (an essence-2, essence-1 or expression-1 add for an agent that isn’t a real person; nothing is charged, see Choosing a model).

curl

curl -X POST https://api.bithuman.ai/v1/agent/A80HVD8577/models \
  -H "Content-Type: application/json" -H "api-secret: $BITHUMAN_API_SECRET" \
  -d '{"model": "essence-2"}'

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/agent/A80HVD8577/models",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={"model": "essence-2"},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/A80HVD8577/models", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "api-secret": process.env.BITHUMAN_API_SECRET,
  },
  body: JSON.stringify({ model: "essence-2" }),
});
console.log(await resp.json());
{"success": true, "agent_id": "A80HVD8577", "model": "essence-2", "status": "processing", "supported_models": ["expression-2"]}

Download an agent’s model

GET /v1/agent/{code}/model/download downloadAgentModel

Redirects (302) to the agent’s model file, a .imx container, for the SDKs and CLI. Pass ?model= to choose a model when the agent has several; the default is the model it was created with. Sample avatars download with no credential; your own agents need the api-secret header.

Name the output file yourself (-o). Add ?redirect=false to get the URL as JSON:

StatusCodeMeaning
400MODEL_NOT_DOWNLOADABLEthe model has no file (expression-1)
403PLAN_REQUIREDthe model is not in your plan; the message names the plan
404MODEL_ARTIFACT_NOT_READYpublished shortly after ready; retry
409MODEL_NOT_GENERATEDthe agent does not have that model; add it

curl

curl -fL -o A80HVD8577.imx -H "api-secret: $BITHUMAN_API_SECRET" \
  "https://api.bithuman.ai/v1/agent/A80HVD8577/model/download?model=expression-2"

Python

import os, requests

resp = requests.get(
    "https://api.bithuman.ai/v1/agent/A80HVD8577/model/download?model=expression-2",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
resp.raise_for_status()
with open("A80HVD8577.imx", "wb") as f:
    f.write(resp.content)

Node

import { writeFile } from "node:fs/promises";

const resp = await fetch("https://api.bithuman.ai/v1/agent/A80HVD8577/model/download?model=expression-2", {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
await writeFile("A80HVD8577.imx", Buffer.from(await resp.arrayBuffer()));
{"success": true, "data": {"code": "A80HVD8577", "model": "expression-2", "filename": "A80HVD8577.imx", "url": "https://…", "expires_in": 3600}}

List the freely downloadable showcase models

GET /v1/models/showcase getShowcaseManifest

Lists the sample avatars anyone can download, with no API secret and no account: slug, agent code, model and size. Each entry’s url is Download an agent’s model. bithuman list and bithuman pull read this list.

curl

curl https://api.bithuman.ai/v1/models/showcase

Python

import requests

resp = requests.get("https://api.bithuman.ai/v1/models/showcase")
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/models/showcase");
console.log(await resp.json());
{"version": 2, "updated": "2026-09-10", "models": [{"slug": "bolt", "name": "Bolt", "model": "expression-2", "agent_code": "X03BOLT", "url": "https://api.bithuman.ai/v1/agent/X03BOLT/model/download?model=expression-2", "size": 198106157}]}

List an agent’s live sessions

GET /v1/agent/{code}/sessions listAgentSessions

Lists the agent’s open sessions. Use a room_id whose deliverable is true with speak or add-context.

curl

curl https://api.bithuman.ai/v1/agent/A80HVD8577/sessions -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

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

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/A80HVD8577/sessions", {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{"agent_code": "A80HVD8577", "sessions": [{"room_id": "room-A80HVD8577-x1y2", "num_participants": 2, "created_at": 1788480000, "deliverable": true}]}

Make an agent speak

POST /v1/agent/{code}/speak agentSpeak

Makes the avatar say message in its live sessions: one session with room_id, or every deliverable session without it. With no live session it returns 404 NOT_FOUND.

curl

curl -X POST https://api.bithuman.ai/v1/agent/A80HVD8577/speak \
  -H "Content-Type: application/json" -H "api-secret: $BITHUMAN_API_SECRET" \
  -d '{"message": "We have a 20% discount today.", "room_id": "room-A80HVD8577-x1y2"}'

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/agent/A80HVD8577/speak",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={
        "message": "We have a 20% discount today.",
        "room_id": "room-A80HVD8577-x1y2",
    },
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/agent/A80HVD8577/speak", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "api-secret": process.env.BITHUMAN_API_SECRET,
  },
  body: JSON.stringify({
    message: "We have a 20% discount today.",
    room_id: "room-A80HVD8577-x1y2",
  }),
});
console.log(await resp.json());
{"agent_code": "A80HVD8577", "delivered_to_rooms": 1, "rooms": ["room-A80HVD8577-x1y2"], "rooms_skipped": [], "rooms_failed": []}

This section moved: docs.bithuman.ai/api/knowledge#inject-knowledge

This section moved: docs.bithuman.ai/api/errors#agent-operations