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
}
  • fromNumber must be an ACTIVE phone number your org owns.
  • agentId must be one of your active voice agents.
  • toNumber must 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:

ParamTypeDescription
statusrepeated stringFilter by call status. Repeat for OR (?status=NO_ANSWER&status=FAILED).
directionINBOUND | OUTBOUNDFilter by direction.
fromISO datetimeStarted after this time.
toISO datetimeStarted before this time.
agentIdnumberFilter by voice agent.
page, sizeintStandard 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:

CodeMeaning
200Success
400Bad input or business-rule failure (read message).
401Missing / invalid API key.
403Authenticated but not allowed (org mismatch, etc.).
404Resource not found, or not visible to this org.
409Conflict — usually FK violation; the resource has dependent records.
429Rate limit exceeded — back off and retry.
500Unexpected error on our side. Retry, then contact support.
502External 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.