API Reference
Everything you can do in the InfraVoice dashboard, you can do over HTTP — create voice agents, give them a brain and a voice, put them on a phone number, route callers to human agents, and read back transcripts, scores and costs.
You need exactly one credential to start: your organization key. Get it from the dashboard, send it as a bearer token, and every endpoint in this reference opens up.
Base URL:
https://your-infravoice-host/api/v1Auth header:Authorization: Bearer usf-…Content type:application/jsonunless a page says otherwise
Get your API key
Your organization key is a single secret that starts with usf-. It authorizes the whole platform: this API, Speech-to-Text, and the dashboard itself.
From the dashboard
- Sign in at your-infravoice-host.
- Click your email address at the bottom-left of the sidebar → Account settings.
- Scroll to the Organization API key card.
- Click Reveal to show the full key, then copy it.
The card also shows which organization the key belongs to and when it was created. Between reveals the key is displayed masked (usf-••••5G7q) so it is never left on screen.
Copy it somewhere safe. The full secret is shown on creation and whenever you press Reveal. Only the masked form is stored for display — if you lose it and Reveal is unavailable, rotate the key to get a new one.
Rotating a key
If a key leaks, press Rotate key on the same card. Rotation issues a new key and deactivates the old one immediately.
Rotation is near-instant, not instant. Services cache key lookups for about 5 seconds, so a revoked key may continue to work for up to ~5s after rotation. Rotate as soon as you suspect exposure, and treat that small window as part of your incident response.
Doing it over the API
The dashboard card calls these endpoints. They authenticate with your dashboard login (JWT) — you use them to obtain the key, so they cannot require the key itself.
| Method | Endpoint | Purpose |
|---|---|---|
GET | /api/v1/auth/organizations/me | Your organization plus its masked key. Creates the organization and its first key if you have none — and returns the plaintext secret on that first call only. |
POST | /api/v1/auth/organizations/key/reveal | Return the current key in plaintext. |
POST | /api/v1/auth/organizations/key/rotate | Issue a new key and deactivate the current one. Returns the new plaintext secret. |
{
"success": true,
"data": {
"organization": { "id": "org_…", "name": "Acme Ltd" },
"key": { "key_id": "key_…", "masked": "usf-••••5G7q", "is_primary": true, "created_at": "2026-07-25T10:00:00Z" },
"secret": "usf-…"
},
"timestamp": "2026-07-26T09:00:00Z"
}secret is present only on first creation and after a reveal or rotate. Every other response omits it.
Your first request
Send the key as a bearer token on every call:
curl https://your-infravoice-host/api/v1/agents \
-H "Authorization: Bearer usf-YOUR_ORG_KEY"import httpx
API = "https://your-infravoice-host/api/v1"
KEY = "usf-YOUR_ORG_KEY"
r = httpx.get(f"{API}/agents", headers={"Authorization": f"Bearer {KEY}"})
r.raise_for_status()
print(r.json()["data"])const API = "https://your-infravoice-host/api/v1";
const KEY = "usf-YOUR_ORG_KEY";
const res = await fetch(`${API}/agents`, {
headers: { Authorization: `Bearer ${KEY}` },
});
const { data } = await res.json();
console.log(data);Never ship this key to a browser or a mobile app. It carries full access to your organization (see below). Keep it on your server, in an environment variable or a secrets manager, and never commit it.
Build an agent end to end
This is the whole path from a fresh key to an agent that answers a real phone call. Five requests. Run them in order — each one feeds the next.
1. Create the agent. This is just configuration; it cannot answer anything yet.
export KEY="Authorization: Bearer usf-YOUR_ORG_KEY"
curl -X POST https://your-infravoice-host/api/v1/agents \
-H "$KEY" -H "Content-Type: application/json" \
-d '{
"name": "Support Line",
"system_prompt": "You are Ava, the support agent for Acme. Be brief and warm. Never invent order numbers.",
"welcome_message": "Hi, this is Ava at Acme — how can I help?",
"language": "en"
}'Keep the id from the response — every step below needs it.
export AGENT="THE_ID_FROM_ABOVE"2. Give it a brain, ears and a voice. Three separate calls, one per provider slot. An agent missing any of the three cannot take a call — it fails with 422, so do not skip this.
# LLM — what it thinks with
curl -X POST https://your-infravoice-host/api/v1/agents/$AGENT/providers \
-H "$KEY" -H "Content-Type: application/json" \
-d '{"provider_type":"llm","provider_name":"openai","model_name":"gpt-4o-mini","api_key":"YOUR_OPENAI_KEY"}'
# ASR — what it hears with
curl -X POST https://your-infravoice-host/api/v1/agents/$AGENT/providers \
-H "$KEY" -H "Content-Type: application/json" \
-d '{"provider_type":"asr","provider_name":"deepgram","model_name":"nova-2","api_key":"YOUR_DEEPGRAM_KEY"}'
# TTS — what it speaks with
curl -X POST https://your-infravoice-host/api/v1/agents/$AGENT/providers \
-H "$KEY" -H "Content-Type: application/json" \
-d '{"provider_type":"tts","provider_name":"elevenlabs","voice_id":"21m00Tcm4TlvDq8ikWAM","api_key":"YOUR_ELEVENLABS_KEY"}'You bring your own provider keys. InfraVoice stores them encrypted and uses them on your behalf.
3. Publish. Everything so far edited a draft. Nothing is live until this call.
curl -X POST https://your-infravoice-host/api/v1/agents/$AGENT/versions/publish -H "$KEY"4. Attach a phone number. Your Twilio number, your Twilio account.
curl -X PUT https://your-infravoice-host/api/v1/agents/$AGENT/telephony \
-H "$KEY" -H "Content-Type: application/json" \
-d '{
"twilio_account_sid": "ACxxxxxxxx",
"twilio_auth_token": "YOUR_TWILIO_TOKEN",
"twilio_phone_number": "+14155550123",
"call_direction": "inbound"
}'5. Point Twilio at us. In the Twilio console, open your number and set the Voice webhook ("A call comes in") to:
https://your-infravoice-host/api/v1/telephony/calls/inboundThat URL is something you configure, never something you call. Save it, then dial your number — Ava answers.
If the call connects but nobody speaks, you almost certainly skipped step 3. Check
draft_dirtyon the agent: if it istrue, your providers are sitting in an unpublished draft.
The same thing in Python
import httpx
API = "https://your-infravoice-host/api/v1"
KEY = "usf-YOUR_ORG_KEY"
h = {"Authorization": f"Bearer {KEY}"}
with httpx.Client(base_url=API, headers=h, timeout=30) as c:
agent = c.post("/agents", json={
"name": "Support Line",
"system_prompt": "You are Ava, the support agent for Acme. Be brief and warm.",
"welcome_message": "Hi, this is Ava at Acme — how can I help?",
"language": "en",
}).raise_for_status().json()["data"]
aid = agent["id"]
for provider in (
{"provider_type": "llm", "provider_name": "openai",
"model_name": "gpt-4o-mini", "api_key": "YOUR_OPENAI_KEY"},
{"provider_type": "asr", "provider_name": "deepgram",
"model_name": "nova-2", "api_key": "YOUR_DEEPGRAM_KEY"},
{"provider_type": "tts", "provider_name": "elevenlabs",
"voice_id": "21m00Tcm4TlvDq8ikWAM", "api_key": "YOUR_ELEVENLABS_KEY"},
):
c.post(f"/agents/{aid}/providers", json=provider).raise_for_status()
# Nothing above affects live calls until this line runs.
c.post(f"/agents/{aid}/versions/publish").raise_for_status()
print("live:", aid)Where each piece is documented
| Step | Page |
|---|---|
| 1, 2, 3 — agent, providers, publish | Agents |
| 4, 5 — number, webhook, outbound calls | Telephony & Campaigns |
| Talking to the agent from your own app instead of a phone | Realtime Voice |
| Reading the transcript afterwards | Realtime Voice → Conversations |
Key scope
The organization key is deliberately simple, and you should understand its blast radius before you hand it out.
| Scope | Your entire organization — every workspace in it, acting as the organization owner. |
| Granularity | There is no read-only key, no per-workspace key, and no per-endpoint scoping. One key, full access. |
| Where it works | Every endpoint in this reference — agents, providers, tools, knowledge, versioning, telephony, campaigns, call center, human agent provisioning, conversations, analytics, QA, billing and Speech-to-Text. |
Issue one key per organization, not per teammate. Because the key acts as the owner across all workspaces, sharing it is equivalent to sharing owner credentials. For anything customer-facing, put your own service in front of this API rather than passing the key through.
The other credentials
You may meet two more secrets in this product. They are not interchangeable:
- Speech-to-Text
sk-key — a scoped key just for transcription, sent asX-API-Key(notAuthorization). Yourusf-key also works there. See Speech-to-Text. - Human agent token — a human agent signs in to the Agent Portal with a workspace slug, agent code and password and receives their own token. It identifies that person, which is why the agent-session routes require it and your
usf-key cannot stand in.
Responses & errors
Most endpoints wrap their payload in a standard envelope. The part you care about is data.
{
"success": true,
"data": { "id": "…", "name": "…" },
"error": null,
"meta": { "total": 42, "limit": 20, "offset": 0 },
"timestamp": "2026-07-26T09:00:00Z"
}On failure, success is false and error carries a machine-readable code plus a human-readable message:
{
"success": false,
"data": null,
"error": { "code": "INVALID_BODY", "message": "call_id and event_type are required" },
"timestamp": "2026-07-26T09:00:00Z"
}Two response shapes exist. The telephony and call-bridge endpoints are served by a different runtime and return raw JSON with no envelope — the object itself, not
{ "data": … }. Each page states which shape it returns. If you are writing one shared response parser, handle both.
Status codes
| Code | Meaning |
|---|---|
200 / 201 | Success. |
400 | Malformed request — a required field is missing or invalid. |
401 | Missing, malformed or revoked key. error.code is UNAUTHORIZED. |
403 | Authenticated, but not allowed to touch this workspace or resource. error.code is FORBIDDEN. |
404 | No such resource, or it belongs to another organization. |
422 | The request was understood but cannot be fulfilled — most often an agent that is missing a required provider. |
429 | Rate limited. Back off and retry. |
500 | Something failed on our side. Safe to retry idempotent reads. |
Drafts & publishing
Known gap — read this before relying on it. The draft/publish model below describes versioning and rollback, but it is not currently enforced at call time: the voice pipeline reads the live agent row, and every save busts its cache. In practice an edit takes effect on the next call, published or not. Publishing still records a version you can inspect and roll back to. Treat edits to a busy agent as live changes.
Every write to an agent — prompt, providers, tools, telephony, knowledge — updates a draft and records a version. Publishing marks the version you can roll back to:
curl -X POST https://your-infravoice-host/api/v1/agents/{agent_id}/versions/publish \
-H "Authorization: Bearer usf-YOUR_ORG_KEY"If a change "did nothing", this is almost always why. Check
draft_dirtyon the agent: when it istrueyou have unpublished edits. Publishing creates a new immutable version, and you can roll back to any earlier one.
Next steps
The reference follows the order you would actually build in:
| Page | What you'll do |
|---|---|
| Agents | Create an agent, give it a prompt, attach LLM / speech-to-text / text-to-speech providers, add tools and knowledge, then publish it. |
| Realtime Voice & Conversations | Talk to an agent over a WebSocket, then read back transcripts and summaries. |
| Telephony & Campaigns | Put an agent on a phone number, place outbound calls, run outbound campaigns. |
| Call Center & Human Handoff | Set up projects, human agents and queues so a caller can reach a person. |
| Human Agent Portal API | The separate API your human agents' client uses to sign in, hold presence and take calls. |
| Analytics, QA & Billing | Measure latency and usage, score calls against a rubric, and read your balance. |
| Speech-to-Text | The standalone transcription product. |
An agent needs an LLM, a speech-to-text and a text-to-speech provider before it can answer anything. If you only read one more page, read Agents.