API reference
The ZazaVoice public API is the primitive layer you build your own product on top of. Auth via API key, JSON in / JSON out, the same shape across every endpoint.
If you're new here, read Quickstart first — it walks through dispatching your first call in 5 minutes. This page is the exhaustive reference. For a candid list of what the API supports today and what's not yet in scope, see Capabilities & limits.
The API is at the
/api/v1/prefix and is API-key authenticated. The dashboard's own routes (/api/agents,/api/campaigns, …) are for the frontend and use JWT auth — a different, internal surface.
Base URL
https://zazavoice-api-569199843054.us-central1.run.app/api/v1
Local dev:
http://localhost:8081/api/v1
Authentication
Bearer token in the Authorization header. Keys are prefixed zaza_ so
they're easy to identify.
Authorization: Bearer zaza_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Generating an API key
Dashboard → Settings → API Keys → Create key. Give it a memorable name
(e.g. "campaign-dispatch-prod"). The plaintext key is shown exactly
once — copy it to your secrets store immediately. We only store a
SHA-256 hash and can't recover the original.
Keys are scoped to the organization that created them.
Revoking
Dashboard → Settings → API Keys → trash icon. Effective immediately.
There's no per-key permission scoping yet — every key has full org-level
access via the /v1 API.
Rate limits
A per-key rate limit applies via ApiKeyRateLimitFilter. Defaults are
generous for normal use; if you're scripting a large dispatch loop,
back off when you see 429.
Endpoints
List voice agents
GET /v1/agents
Returns the org's agents.
[
{
"id": 12,
"name": "Zara from Acme",
"personality": "FRIENDLY",
"voiceType": "FEMALE_1",
"isActive": true,
"greetingMessage": "Hi {{NAME}}, this is Zara…"
}
]
Get one voice agent
GET /v1/agents/{id}
Same shape as above but a single object.
Dispatch an outbound call
POST /v1/calls/outbound
Body:
{
"fromNumber": "+14155550100",
"toNumber": "+14155550199",
"agentId": 12
}
fromNumbermust be an ACTIVE phone number your org owns.agentIdmust be one of your active voice agents.toNumbermust be E.164 (++ country code + number, no spaces or dashes).
Response:
{
"callId": 3401,
"callSid": "CA7d23f…",
"status": "queued",
"from": "+14155550100",
"to": "+14155550199"
}
The call is queued at the carrier immediately and rings within a few
seconds. Status updates flow through Twilio's status callback to our
backend; you can poll GET /v1/calls/{id} or subscribe to webhooks (see
webhooks.md) to receive call.completed / call.failed.
List calls
GET /v1/calls
Query parameters:
| Param | Type | Description |
|---|---|---|
status | repeated string | Filter by call status. Repeat for OR (?status=NO_ANSWER&status=FAILED). |
direction | INBOUND | OUTBOUND | Filter by direction. |
from | ISO datetime | Started after this time. |
to | ISO datetime | Started before this time. |
agentId | number | Filter by voice agent. |
page, size | int | Standard Spring pagination. |
Get one call
GET /v1/calls/{id}
Returns the call record plus its conversation turns (transcript).
List campaigns
GET /v1/campaigns
Get one campaign
GET /v1/campaigns/{id}
Includes lead summary counts (reached / not interested / etc.).
Errors
All errors return the same JSON shape:
{
"status": 400,
"message": "Phone number limit reached (5). Upgrade your plan to add more numbers.",
"code": "FK_CONSTRAINT",
"timestamp": "2026-05-21T15:30:00"
}
status— HTTP status, mirrors the response code.message— human-readable. Display this verbatim to users.code— optional, machine-readable. Examples:NEEDS_PAYMENT_METHOD— add a card before retrying.AGENT_IN_USE— entity blocked by referencing records.FK_CONSTRAINT— generic foreign-key violation.INTEGRITY_VIOLATION— generic data-integrity issue.
Status codes:
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad input or business-rule failure (read message). |
| 401 | Missing / invalid API key. |
| 403 | Authenticated but not allowed (org mismatch, etc.). |
| 404 | Resource not found, or not visible to this org. |
| 409 | Conflict — usually FK violation; the resource has dependent records. |
| 429 | Rate limit exceeded — back off and retry. |
| 500 | Unexpected error on our side. Retry, then contact support. |
| 502 | External service (carrier, payment, AI) is temporarily unavailable. |
Idempotency
The API is not yet idempotency-key aware. If a POST /v1/calls/outbound
times out before you got a response, the call may have been queued; check
GET /v1/calls?direction=OUTBOUND&from=… to see if it landed before
retrying. Idempotency-Key header support is planned.
SDKs
There are no official SDKs yet. The API is small enough to call directly with any HTTP library. If you build a community SDK, let us know and we'll link it from here.
Versioning
The current version is /v1. Breaking changes get a new version
prefix (/v2); we'll keep /v1 running for at least 12 months after a
deprecation announcement. Additive changes (new fields, new endpoints)
ship under /v1 without a version bump.