HTTP API
The gateway has a small HTTP API for health checks, per-call settings, outbound calls and housekeeping. Every response is JSON. The examples use port 8081 on the same host; replace it with your HTTP_PORT.
Access
| Variable | Default | Meaning |
|---|---|---|
HTTP_HOST |
127.0.0.1 |
Address the API listens on. |
HTTP_PORT |
8080 |
Port the API listens on. The fallback collides with AUDIOSOCKET_PORT, so set it explicitly (this manual uses 8081). |
HTTP_ALLOWED_IPS |
unset | Comma-separated exact IPs and IPv4 CIDR blocks. Unset means the whitelist is off and any client that can reach the port is served. A blocked request gets 403. |
HTTP_TRUST_PROXY |
false |
Set true behind a reverse proxy so the client address is read from X-Forwarded-For. |
HTTP_HOST=127.0.0.1
HTTP_PORT=8081
HTTP_ALLOWED_IPS=127.0.0.1,192.0.2.0/24
The API has no API key. Restrict it by IP, bind it to loopback, or put an authenticating proxy in front. Anyone who can reach it can create sessions and place outbound calls.
Health and licence
GET /health reports gateway state. status is ok or degraded. The billing block appears only when BILLING_CDR_ENABLED=true, and sipBridge only when the SIP bridge is on.
curl -s http://127.0.0.1:8081/health
{
"status": "ok",
"timestamp": "2026-10-24T07:19:33.032Z",
"storage": { "storage_type": "memory", "redis_connected": false, "memory_sessions": 0, "default_ttl": 3600 },
"telephonyMode": "both",
"server": { "http_host": "0.0.0.0", "http_port": 8081, "audiosocket_host": "127.0.0.1", "audiosocket_port": 8080 },
"concurrentCalls": { "active": 0, "max": 30, "utilization": "0%" },
"circuitBreakers": { "litebot-chat": { "state": "CLOSED", "failures": 0, "successRate": "0%", "totalRequests": 0 } },
"billing": { "enabled": true, "pending": 2, "dead": 2, "last_delivery_at": "2026-10-23T15:52:10.435Z", "last_error": "HTTP 400" },
"sipBridge": { "active": true, "mode": "sip_bridge", "sessions": 0, "sipPort": "5080", "registered": false, "registerUri": null }
}
concurrentCalls.max is your licence cap. Counter values here are examples.
GET /license shows the licence in force. The free tier shows tier FREE, concurrentCalls 4, licensee null and isFree true. expiry is an ISO date or "Never". machineId equals the output of --get-machine-id; see Licence.
curl -s http://127.0.0.1:8081/license
{ "tier": "PROFESSIONAL", "tierName": "Professional", "licensee": "Example Co", "concurrentCalls": 30, "expiry": "Never", "machineId": "<id>", "isFree": false, "valid": true }
Session metadata
Metadata is per-call information the gateway uses for that call: who is calling, which bot answers, which language and voice. Send it before the call reaches the gateway. For the Asterisk dialplan that sends it, see Send caller details first; for SIP, see SIP trunk.
POST /session/{uuid}/metadata takes a JSON body or a form-encoded body (Asterisk CURL() sends form). It merges into the session, so call it again to update.
curl -s -X POST http://127.0.0.1:8081/session/0b9d6c52-1f3a-4c1e-9a77-2f6e5d1c8a10/metadata \
-H 'Content-Type: application/json' \
-d '{"caller_id":"+9198xxxxxxxx","caller_name":"Priya","botId":"sales-followup","language_code":"en-IN"}'
{ "success": true, "message": "Session metadata stored", "session_uuid": "0b9d6c52-1f3a-4c1e-9a77-2f6e5d1c8a10" }
GET /session/{uuid}/metadata returns the stored object, or 404 if there is none. DELETE /session/{uuid} removes the session and returns { "success": true, "message": "Session deleted" }.
curl -s http://127.0.0.1:8081/session/0b9d6c52-1f3a-4c1e-9a77-2f6e5d1c8a10/metadata
curl -s -X DELETE http://127.0.0.1:8081/session/0b9d6c52-1f3a-4c1e-9a77-2f6e5d1c8a10
Accepted keys
| Key | Meaning |
|---|---|
caller_id |
Caller number. Form alias callerid. Default "Unknown". |
caller_name |
Caller name. Form alias name. |
channel |
Asterisk channel name. Required for transfers on AudioSocket calls. |
language_code |
Language for this call. Default ml-IN. |
destination_number |
The number (DID) that was called. |
botId |
LiteBot bot for this call. Case-sensitive; an unknown id uses the default bot. |
tts_provider |
Speech provider for this call: sarvam, google, gemini, elevenlabs, deepgram, azure, murf, vexyl, smallest or openrouter. |
stt_provider |
Recognition provider: sarvam, groq, gemini, openai, deepgram, google or vexyl-stt. |
tts_model |
Per-call model. Only openrouter and gemini read it. |
tts_voice |
Per-call voice. Read only by the provider named in tts_provider. |
realtimeProvider |
litebot, gemini-live, or off (also pipeline) to use the normal pipeline. |
greeting_mode |
Outbound only: static, dynamic, hybrid or wait. |
initial_greeting_audio |
Outbound only: path to a WAV greeting. |
llm_context |
Free text handed to the AI brain as background. |
| any other key | Passed to the brain as context. |
Id rules for the per-call TTS keys:
| Key | Accepted form |
|---|---|
tts_model |
vendor/name or a bare id. Each part is 1 to 64 characters of A-Za-z0-9_.-; the second part may also contain :. |
tts_voice |
1 to 160 characters of A-Za-z0-9_.:-. |
An invalid tts_provider, stt_provider, tts_model or tts_voice is dropped with a warning in the log and the request still succeeds. The call then uses the host default, so check the log if a voice does not change. Browser widget sessions cannot set these keys.
Outbound calls
Outbound calls need these variables in .env:
| Variable | Default | Meaning |
|---|---|---|
OUTBOUND_ENABLED |
off | Set true to open the outbound routes; otherwise they return 403. |
OUTBOUND_ORIGINATE_ENDPOINT |
unset | Your webhook that places the call. The gateway posts the session id, phone number, language, a timestamp and the request metadata to it. Without it the session is created and the reply carries a warning. |
OUTBOUND_TIMEOUT |
10000 |
Milliseconds to wait for that webhook. |
OUTBOUND_GREETING_MODE |
static |
Default greeting mode. |
SIP bridge hosts place the call themselves; see Outbound SIP calls. On AudioSocket hosts your originate webhook, for example one that talks to your PBX, places it.
POST /outbound/initiate starts a call.
| Field | Meaning |
|---|---|
phoneNumber |
Required. Destination number. |
language |
Language code. Default is DEFAULT_LANGUAGE, else ml-IN. |
greetingMode |
static, dynamic, hybrid or wait. |
initialGreeting |
Greeting text. |
audioFile |
Path to a WAV greeting. |
webhookUrl |
URL for status callbacks. |
tts_provider, tts_model, tts_voice, stt_provider, botId |
Per-call overrides, same rules as session metadata. |
metadata |
Object with botId, caller_name, llm_context, language_code, the TTS keys and any custom fields. |
curl -s -X POST http://127.0.0.1:8081/outbound/initiate \
-H 'Content-Type: application/json' \
-d '{"phoneNumber":"+9198xxxxxxxx","language":"en-IN","greetingMode":"dynamic","metadata":{"botId":"sales-followup","caller_name":"Priya","llm_context":"Follow-up on a demo request."}}'
{ "sessionUuid": "550e8400-e29b-41d4-a716-446655440000", "status": "initiated", "message": "Outbound call webhook triggered successfully" }
GET /outbound/status/{uuid} returns the call state: initiated, answered, failed or cancelled.
curl -s http://127.0.0.1:8081/outbound/status/550e8400-e29b-41d4-a716-446655440000
{
"sessionUuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "answered",
"phoneNumber": "+9198xxxxxxxx",
"language": "en-IN",
"initiatedAt": "2026-10-24T12:00:00.000Z",
"answeredAt": "2026-10-24T12:00:15.000Z",
"completedAt": null,
"metadata": { "call_direction": "outbound", "destination_number": "+9198xxxxxxxx", "greeting_mode": "dynamic", "language_code": "en-IN", "botId": "sales-followup", "caller_name": "Priya" }
}
POST /outbound/cancel/{uuid} marks the session cancelled. It returns 404 for an unknown id and 400 if the call was already answered or completed. It does not hang up a ringing leg: that is your originate webhook's job.
curl -s -X POST http://127.0.0.1:8081/outbound/cancel/550e8400-e29b-41d4-a716-446655440000
{ "success": true, "message": "Call marked as cancelled", "sessionUuid": "550e8400-e29b-41d4-a716-446655440000" }
Active calls and stats
GET /calls/stats shows counters since the process started and the calls in progress.
curl -s http://127.0.0.1:8081/calls/stats
{ "activeCalls": 0, "maxCalls": 30, "tier": "Professional", "totalHandled": 12, "totalRejected": 0, "peakConcurrent": 1, "utilizationPercent": 0, "activeCallDetails": [] }
GET /calls/active returns the same call list with a count and the cap.
curl -s http://127.0.0.1:8081/calls/active
{ "count": 0, "max": 30, "calls": [] }
TTS cache
GET /cache/stats shows the cache hit rate since the process started. hitRateChars tracks the speech bill best, because a hit on a short word and a miss on a long sentence are not equal. Per-call figures are in the billing records.
curl -s http://127.0.0.1:8081/cache/stats
{
"cache": {
"enabled": true,
"hits": 80,
"misses": 40,
"errors": 0,
"hitRate": "66.7%",
"totalSizeMB": "4.10",
"entryCount": 120,
"lastCleanup": "2026-01-01T09:00:00.000Z",
"estimatedSavings": { "apiCallsSaved": 80, "estimatedCostUSD": "0.24" }
},
"byProvider": {
"google": { "hits": 80, "misses": 40, "hitChars": 3900, "missChars": 2800 }
},
"hitRateRequests": "66.7%",
"hitRateChars": "58.2%",
"sinceProcessStart": true
}
POST /cache/warmup renders phrases ahead of time so the first caller does not wait for them. It returns 400 unless TTS_CACHE_ENABLED=true. See The TTS cache.
curl -s -X POST http://127.0.0.1:8081/cache/warmup \
-H 'Content-Type: application/json' \
-d '{"phrases":[{"text":"Hello, how can I help you today?","languageCode":"en-IN"}]}'
{ "success": true, "cached": 1, "skipped": 0, "errors": 0, "total": 1 }
Billing replay
POST /billing/replay retries billing records that failed to deliver. Use {"scope":"dead"} after fixing the billing endpoint or its token: it moves every dead-lettered record back to pending and sweeps. {"scope":"pending"} (the default) forces an immediate retry sweep. It returns 400 unless BILLING_CDR_ENABLED=true. See Webhooks and billing.
curl -s -X POST http://127.0.0.1:8081/billing/replay \
-H 'Content-Type: application/json' -d '{"scope":"dead"}'
{ "scope": "dead", "moved": 2, "pending": 2, "dead": 0 }
Circuit breakers
When a provider fails repeatedly the gateway stops calling it for a while and uses a fallback. GET /circuit-breakers shows each breaker. State is CLOSED (normal), OPEN (provider skipped) or HALF_OPEN (testing). Breakers appear as providers are used. nextAttemptIn says when an OPEN breaker retries.
curl -s http://127.0.0.1:8081/circuit-breakers
{ "litebot-chat": { "name": "litebot-chat", "state": "CLOSED", "failures": 0, "successRate": "0%", "totalRequests": 0, "lastFailureTime": null, "lastErrorMessage": null, "nextAttemptIn": null } }
POST /circuit-breakers/reset resets one breaker by name (404 if unknown). An empty body resets all.
curl -s -X POST http://127.0.0.1:8081/circuit-breakers/reset \
-H 'Content-Type: application/json' -d '{"name":"litebot-chat"}'
{ "success": true, "message": "Circuit breaker litebot-chat reset successfully" }
Hosted customers
This API is not exposed on a hosted service. See Hosted service.