VoiceOS API
Drive your voice agents programmatically — place calls, pull transcripts and recordings, manage contacts, and react to events in real time. Every request is scoped to the workspace that owns the API key; there is no cross-workspace access.
Base URL
https://<your-workspace-host>/api/v1Authentication
Create a key under Settings → API Keys. The full secret is shown once — only a prefix and a SHA-256 hash are stored. Send it as a bearer token on every request:
curl https://<host>/api/v1/agents \
-H "Authorization: Bearer voiceos_sk_xxxxxxxxxxxxxxxxxxxxxxxx"Revoked keys are rejected immediately. Rotate by creating a new key and revoking the old one. Keep keys server-side — never ship them in a browser or mobile app.
Errors & limits
Errors use standard HTTP status codes and a consistent body. 401 invalid key, 402 insufficient balance, 404 not found in your workspace, 400 bad request.
{ "error": { "code": "unauthorized", "message": "Invalid or revoked API key." } }The API applies fair-use limits. If you need sustained high throughput, get in touch via Contact for pricing.
Agents
/api/v1/agentsList the AI voice agents in your workspace.
Parameters
limitintoffsetint{
"object": "list",
"data": [
{
"id": "ag_9f2c…",
"name": "Riya — Loan Sales",
"language": "Hindi",
"voice_id": "sulafat",
"status": "active",
"direction": "outbound",
"created_at": "2026-07-01T09:12:00Z"
}
],
"limit": 25,
"offset": 0
}Calls
/api/v1/callsList calls, newest first.
Parameters
statusstringdirectionstringcampaign_iduuidlimit / offsetint/api/v1/calls/{id}Retrieve one call with its full transcript and extracted variables.
{
"call": {
"id": "call_7a1b…",
"phone": "+9198…",
"status": "completed",
"duration_sec": 96,
"cost": 11.2,
"sentiment": "positive",
"summary": "Customer agreed to pay the EMI by Friday.",
"recording_url": "/recordings/call_7a1b.wav",
"transcript": [{ "role": "agent", "text": "नमस्ते…" }],
"variables": { "callback_time": "Friday 6pm" }
}
}/api/v1/callsPlace an outbound AI call. Returns immediately; the call record appears in the list once the media stream begins.
Parameters
agent_idrequuidtoreqstringfromstring// 202 Accepted
{ "ok": true, "status": "queued", "request_uuid": "…", "to": "+9198…", "agent_id": "ag_…" }Numbers
/api/v1/numbersList the phone numbers on your workspace, with monthly cost and renewal date.
Campaigns
/api/v1/campaignsList calling campaigns with live counts (queued, running, completed, failed, answered).
Contacts
/api/v1/contactsList contacts in your workspace.
/api/v1/contactsCreate a contact.
Parameters
namereqstringphonereqstringcity / company / emailstringtagsstring[]Wallet
/api/v1/walletYour wallet balance, currency, low-balance threshold and recent transactions.
Webhooks (read)
/api/v1/webhooksList your webhook endpoints and their delivery status (the signing secret is never returned).
Webhooks
Register HTTPS endpoints under Settings → API Keys → Webhooks to receive events as they happen. Each endpoint has its own signing secret, shown once.
Events
call.completedA call ends after a real conversation.call.failedA call fails to connect or ends with no conversation.campaign.completedAn outbound campaign finishes dialing.wallet.low_balanceBalance drops to/below the low-balance threshold (calling pauses).wallet.rechargedCredits are added to the workspace.number.expiringA number renews within 7 days.Delivery & payload
Each event is an HTTP POST with this JSON body. Respond 2xx to acknowledge; non-2xx is recorded on the endpoint (last_status).
{
"id": "evt_1a2b3c4d",
"type": "call.completed",
"created_at": "2026-07-23T10:15:30.000Z",
"org_id": "b1e7…",
"data": { "id": "call_7a1b…", "duration_sec": 96, "sentiment": "positive", "summary": "…" }
}Verifying the signature
Every delivery includes an HMAC-SHA256 of the raw body, keyed with your endpoint secret, in the X-VoiceOS-Signature header. Recompute and compare in constant time.
import crypto from "crypto";
// Express: use express.raw({ type: "application/json" })
function verify(rawBody, header, secret) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(header || "");
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
app.post("/webhooks/voiceos", (req, res) => {
if (!verify(req.body, req.get("X-VoiceOS-Signature"), process.env.WHSEC)) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body);
// handle event.type …
res.sendStatus(200);
});Need something not covered here? Talk to our team →