Skip to content

Developer docs

Build with ChatWithHuman.ai

Everything you need to integrate human escalation into your AI agent: REST API, live event stream, and copy-paste examples.

Quickstart

  1. 01

    Get your API key

    Sign up and generate a key from your dashboard (/dashboard/keys). Keys are prefixed cwh_live_, shown once, and can be revoked at any time.

  2. 02

    Escalate a session

    POST to /api/sessions with your agent_id and context. Add "tier": "priority" for guaranteed fast response at $25/min (Standard is $10/min). ChatWithHuman.ai matches the session to an available human expert.

  3. 03

    Stream the reply

    Open GET /api/sessions/{id}/events (Server-Sent Events) to receive status changes and the human's messages, then resume your agent flow. Webhooks are planned.

API reference

Base URL: https://chatwithhuman.ai/api

POST/sessions

Create a new escalation session. Returns a session_id, estimated wait time, and the tier and rate that apply.

FieldTypeDescription
agent_idrequiredstringYour registered agent identifier.
contextstringPlain-text summary of the issue for the human expert. Strongly recommended.
tier"standard" | "priority"Pricing tier for this session. Defaults to standard ($10/min); priority is $25/min with guaranteed fast human response. Legacy priority: "high" also selects priority.
webhook_urlstringStored with the session. Webhook delivery is planned, not live — use the event stream.
metadataobjectArbitrary key-value pairs, echoed back in the response.
response
{  "session_id": "ses_x9k2m",  "status": "queued",  "eta_seconds": 12,  "billing_started": false,  "tier": "standard",  "rate_cents_per_minute": 1000,  "metadata": {}}

GET/sessions/{id}

Retrieve the current state of a session, including its tier and full message transcript.

response
{  "id": 42,  "sessionId": "ses_x9k2m",  "agentId": "agent_abc123",  "status": "connected",  "priority": "normal",  "tier": "standard",  "context": "User is asking about a refund for order #8821",  "webhookUrl": null,  "operatorId": "operator_1",  "billingStartedAt": "2026-10-14T21:18:04.000Z",  "closedAt": null,  "createdAt": "2026-10-14T21:17:52.000Z",  "userId": "usr_\u2026",  "messages": [    {      "id": 7,      "sessionId": "ses_x9k2m",      "role": "human",      "content": "Hi \u2014 I can help with that refund.",      "createdAt": "2026-10-14T21:18:10.000Z"    }  ]}

POST/sessions/{id}/messages

Send a message into a session. Agents should pass role "agent".

FieldTypeDescription
contentrequiredstringThe message text to send.
role"agent" | "human" | "system"Defaults to human — set "agent" when your agent is speaking.
response
{  "message_id": "msg_7",  "created_at": "2026-10-14T21:18:04.000Z"}

GET/sessions/{id}/events

Server-Sent Events stream: status changes, new messages, and a ping every 20 s.

response
data: {"type":"status","status":"connected"} data: {"type":"message","message":{"id":7,"role":"human","content":"Hi — I can help with that refund.", …}} data: {"type":"ping"}

PATCH/sessions/{id}

Close a session with {"status": "closed"}. Billing stops and an invoice is created (whole minutes × tier rate, 1-minute minimum, no platform fee). Returns the updated session.

FieldTypeDescription
statusrequired"connected" | "closed""closed" ends the session; "connected" is set when an operator joins.
operator_idstringOperator joining the session (with status "connected").
response
{  "sessionId": "ses_x9k2m",  "status": "closed",  "priority": "normal",  "billingStartedAt": "2026-10-14T21:18:04.000Z",  "closedAt": "2026-10-14T21:20:18.000Z",  "\u2026": "\u2026"}

GET/billing/usage

Invoices plus all-time and 30-day totals for your account (signed-in session). Filter by agent.

FieldTypeDescription
agentIdstring (query)Only this agent's invoices.
limit / offsetinteger (query)Pagination; defaults 20 / 0.
response
{  "invoices": [    {      "invoiceId": "inv_3f9a\u2026",      "sessionId": "ses_x9k2m",      "totalMinutes": 2,      "ratePerMinute": "10.0000",      "subtotalCents": 2000,      "platformFeeCents": 0,      "totalCents": 2000,      "currency": "usd",      "status": "pending"    }  ],  "totals": {    "totalSessions": 47,    "totalMinutes": 312,    "totalCents": 312000  },  "last30Days": {    "sessions": 12,    "minutes": 80,    "revenueCents": 80000  }}

Webhooks (planned)

Not live yet: the server stores webhook_url but does not deliver events. Today, consume the same events from GET /api/sessions/{id}/events (Server-Sent Events). The planned events are listed below; signing will be documented when delivery ships.
EventDescription
session.queuedSession created and waiting for a human expert.
session.connectedHuman expert joined. Billing starts.
session.messageNew message from the human expert.
session.closedSession ended. Includes final duration and cost.
live today: SSE stream
// Live today: stream session events over SSE
const res = await fetch(`https://chatwithhuman.ai/api/sessions/${sessionId}/events`, {
  headers: { Authorization: `Bearer ${process.env.CHATWITHHUMAN_API_KEY}` },
});
for await (const chunk of res.body) {
  // data: {"type":"message","message":{...}}
  process.stdout.write(chunk);
}

API keys

Keys are prefixed cwh_live_ and shown once at creation. Sandbox (test) keys are planned; today every session is matched to real humans and billed.
  • -Store keys in environment variables — never commit them to source control.
  • -Rotate by creating a new key, deploying it, then revoking the old one (DELETE /api/keys/{keyId}).
  • -Scope keys per agent to isolate usage and billing.

SDKs

SDKInstallStatus
Node.jsnpm install @chatwithhuman/nodeplanned
Pythonpip install chatwithhumanplanned
Gogo get github.com/chatwithhuman/go-sdkplanned
RESTNo install requiredstable

Implemented routes

The routes the current server actually serves, under /api on the site origin. Full schemas: /openapi.json (OpenAPI 3.1).
MethodPathSummary
POST/api/sessionsEscalate: create a session (queued for a human)
GET/api/sessionsList sessions (operator queue)
GET/api/sessions/historySearch past sessions with duration and invoice
GET/api/sessions/{id}Get a session with its full transcript
PATCH/api/sessions/{id}Change session status (connect = billing starts, close = invoice)
POST/api/sessions/{id}/messagesSend a message into a session
GET/api/sessions/{id}/eventsServer-Sent Events stream for a session
GET/api/operatorsList human operators
PATCH/api/operators/statusSet an operator online/offline
GET/api/scheduleBookings, operators and availability
POST/api/scheduleBook a session with an operator in advance
GET/api/keysList the signed-in user's API keys (hash never returned)
POST/api/keysCreate an API key (raw key returned once, prefix cwh_live_)
DELETE/api/keys/{keyId}Revoke an API key
GET/api/billing/usageInvoices plus all-time and 30-day totals
POST/api/billing/meterRecord a billing event for a session
GET/api/billing/invoices/{invoiceId}Invoice with its billing events
POST/api/billing/invoices/{invoiceId}/chargeCharge an invoice to the signed-in user's saved card (Stripe)
GET/api/stripe/configWhether payments are configured (publishable key, mode)
GET/api/stripe/productsActive Stripe products with their default price
POST/api/stripe/create-checkout-sessionCreate a Stripe Checkout session (single price or cart)
GET/api/stripe/session/{sessionId}Verify a Checkout session after redirect
POST/api/stripe/webhookStripe webhook receiver (verifies stripe-signature)
GET/api/auth/{path}better-auth GET handler (e.g. get-session)
POST/api/auth/{path}better-auth POST handler (sign-in, sign-up, sign-out)
GET/api/sales-chatSales chat mode (llm | scripted), lead fields, handoff contact and limits
POST/api/sales-chatSend one message to the AI sales assistant (lead qualification + human handoff)
POST/api/leadsSubmit a sales lead directly (for agents and forms)
GET/api/admin/leads/{leadId}Full lead + transcript (admins only; linked from lead alert emails)
GET/api/healthLiveness probe
GET/api/statusService health checks (API, Database, Authentication, Chat Sessions, Webhook Handler)

MCP server

plannedThere is no MCP server yet. The descriptor at /.well-known/mcp.json documents the intended shape so agent runtimes can plan for it. Until then, use the REST API in /openapi.json. Descriptor: /.well-known/mcp.json

Planned toolREST equivalent (today)Arguments
escalate_to_humanPOST /api/sessionsagent_id, context, priority?, metadata?
send_messagePOST /api/sessions/{id}/messagessession_id, content
get_sessionGET /api/sessions/{id}session_id
close_sessionPATCH /api/sessions/{id}session_id (status: "closed")