Quick Setup
Enter endpoint URL
Must be HTTPS.
Select events
Choose room.join , chat.push , or both.
Add authentication (optional)
Authorization : Bearer your-api-token
X-API-Key : your-secret-key
All payloads follow the same structure:
Field Type Description agent_idstring The agent that triggered the event event_typestring Event name (room.join or chat.push) dataobject Event-specific data timestampfloat Unix timestamp
room.join
{
"agent_id" : "agent_abc123" ,
"event_type" : "room.join" ,
"data" : {
"room_name" : "customer-support" ,
"participant_count" : 1 ,
"session_id" : "session_xyz789"
},
"timestamp" : 1705312200.0
}
chat.push
{
"agent_id" : "agent_abc123" ,
"event_type" : "chat.push" ,
"data" : {
"role" : "user" ,
"message" : "Hello, I need help with my order" ,
"session_id" : "session_xyz789" ,
"timestamp" : 1705312285.0
},
"timestamp" : 1705312285.0
}
Implementation Examples
Flask (Python)
Express (Node.js)
from flask import Flask, request, jsonify
import hmac, hashlib
app = Flask( __name__ )
WEBHOOK_SECRET = "your-webhook-secret"
@app.route ( '/webhook' , methods = [ 'POST' ])
def handle_webhook ():
signature = request.headers.get( 'X-bitHuman-Signature' , '' )
if not verify_signature(request.data, signature):
return jsonify({ 'error' : 'Invalid signature' }), 401
data = request.json
event_type = data.get( 'event_type' )
if event_type == 'room.join' :
print ( f "User joined session { data[ 'data' ][ 'session_id' ] } " )
elif event_type == 'chat.push' :
print ( f "[ { data[ 'data' ][ 'role' ] } ] { data[ 'data' ][ 'message' ] } " )
return jsonify({ 'status' : 'ok' })
def verify_signature ( payload , signature ):
expected = hmac.new(
WEBHOOK_SECRET .encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest( f "sha256= { expected } " , signature)
if __name__ == '__main__' :
app.run( port = 3000 )
Signature Verification
All webhook requests include an X-bitHuman-Signature header. Verify it using HMAC SHA-256:
Compute HMAC-SHA256(secret, raw_request_body)
Compare the hex digest against the signature header (strip sha256= prefix)
Use constant-time comparison to prevent timing attacks
Always use HTTPS. HTTP endpoints are rejected.
Testing
Local development with ngrok
ngrok http 3000
# Use the resulting HTTPS URL as your webhook endpoint
Manual curl test
curl -X POST https://your-app.com/webhook \
-H "Content-Type: application/json" \
-H "X-bitHuman-Signature: sha256=test" \
-d '{
"agent_id": "test_agent",
"event_type": "room.join",
"data": {
"room_name": "test-room",
"participant_count": 1,
"session_id": "session_123"
},
"timestamp": 1705312200.0
}'
Retry Policy
Failed deliveries (non-2xx responses) are retried automatically:
Attempt Delay 1st retry 1 second 2nd retry 5 seconds 3rd retry 30 seconds
Maximum 3 retries. Your endpoint must respond within 30 seconds.
Troubleshooting
Issue Solution Signature invalid Verify HMAC SHA-256 against raw request body Timeout errors Return 200 immediately, process async 404 Not Found Check endpoint URL in Developer → Webhooks SSL errors Use a valid HTTPS certificate