Webhooks
Receive signed event notifications when async work finishes — register an endpoint, verify the HMAC signature, and inspect delivery attempts.
Agent generation is asynchronous (2–5 minutes). Instead of polling
GET /v1/agent/status/{id}, register a webhook and bitHuman will POST a
signed event to your endpoint the moment the work finishes.
Events
| Event | Fires when |
|---|---|
agent.ready | An agent finished generating and is ready to use. |
agent.failed | Agent generation failed (data.error has the reason). |
More event types will be added over time. Subscribe to a subset, or omit
events (or pass []) to receive all of them.
Register an endpoint
curl -X POST https://api.bithuman.ai/v1/webhooks \
-H "api-secret: $BITHUMAN_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/bithuman/webhook",
"events": ["agent.ready", "agent.failed"],
"description": "prod"
}'
{
"success": true,
"data": {
"id": "f2cd30a2-…",
"url": "https://example.com/bithuman/webhook",
"events": ["agent.ready", "agent.failed"],
"active": true,
"secret": "whsec_60fe3d…"
}
}
The
secretis returned only once. Store it — it signs every delivery and is redacted from all later responses.
What a delivery looks like
POST /bithuman/webhook HTTP/1.1
Content-Type: application/json
X-BitHuman-Event: agent.ready
X-BitHuman-Delivery: evt_a1b2c3…
X-BitHuman-Timestamp: 1780417371
X-BitHuman-Signature: sha256=9f86d081…
{"id":"evt_a1b2c3…","event":"agent.ready","created":1780417371,"data":{"agent_id":"A91XMB7113","code":"A91XMB7113"}}
Verify the signature
Compute HMAC-SHA256 over "{timestamp}.{raw_body}" using your secret and
compare it — in constant time — to the hex digest in X-BitHuman-Signature.
Reject anything older than a few minutes to prevent replays.
import hashlib, hmac, time
def verify(secret: str, headers, raw_body: bytes) -> bool:
ts = headers["X-BitHuman-Timestamp"]
if abs(time.time() - int(ts)) > 300: # 5-minute replay window
return False
sent = headers["X-BitHuman-Signature"].removeprefix("sha256=")
expected = hmac.new(
secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(sent, expected)
Always verify against the raw request body — re-serializing the JSON will change the bytes and break the signature.
Delivery & retries
- Respond
2xxquickly (within 10s). Do heavy work asynchronously. - Failed deliveries are retried up to 3 times with backoff.
- Every attempt is logged. Inspect recent attempts:
curl https://api.bithuman.ai/v1/webhooks/{id}/deliveries \
-H "api-secret: $BITHUMAN_API_SECRET"
Manage webhooks
# Send a test ping to confirm reachability
curl -X POST https://api.bithuman.ai/v1/webhooks/{id}/test -H "api-secret: $BITHUMAN_API_SECRET"
# List
curl https://api.bithuman.ai/v1/webhooks -H "api-secret: $BITHUMAN_API_SECRET"
# Delete
curl -X DELETE https://api.bithuman.ai/v1/webhooks/{id} -H "api-secret: $BITHUMAN_API_SECRET"
See the API reference for the full schema.