REST · v1

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

http
https://<your-workspace-host>/api/v1

Authentication

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:

bash
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.

json
{ "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

GET/api/v1/agents

List the AI voice agents in your workspace.

Parameters

limitint
Page size, 1–100 (default 25).
offsetint
Rows to skip (default 0).
json
{
  "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

GET/api/v1/calls

List calls, newest first.

Parameters

statusstring
Filter e.g. completed, failed, no-answer.
directionstring
outbound or inbound.
campaign_iduuid
Only calls from this campaign.
limit / offsetint
Pagination.
GET/api/v1/calls/{id}

Retrieve one call with its full transcript and extracted variables.

json
{
  "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" }
  }
}
POST/api/v1/calls

Place an outbound AI call. Returns immediately; the call record appears in the list once the media stream begins.

Parameters

agent_idrequuid
An agent in your workspace.
toreqstring
Destination number in E.164 (+91…).
fromstring
A number you own; defaults to your account number.
json
// 202 Accepted
{ "ok": true, "status": "queued", "request_uuid": "…", "to": "+9198…", "agent_id": "ag_…" }

Numbers

GET/api/v1/numbers

List the phone numbers on your workspace, with monthly cost and renewal date.

Campaigns

GET/api/v1/campaigns

List calling campaigns with live counts (queued, running, completed, failed, answered).

Contacts

GET/api/v1/contacts

List contacts in your workspace.

POST/api/v1/contacts

Create a contact.

Parameters

namereqstring
Contact name.
phonereqstring
E.164 number.
city / company / emailstring
Optional details.
tagsstring[]
Optional labels.

Wallet

GET/api/v1/wallet

Your wallet balance, currency, low-balance threshold and recent transactions.

Webhooks (read)

GET/api/v1/webhooks

List 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).

json
{
  "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.

javascript
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 →