InfraVoice

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/v1 Auth header: Authorization: Bearer usf-… Content type: application/json unless 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

  1. Sign in at your-infravoice-host.
  2. Click your email address at the bottom-left of the sidebarAccount settings.
  3. Scroll to the Organization API key card.
  4. 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.

MethodEndpointPurpose
GET/api/v1/auth/organizations/meYour 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/revealReturn the current key in plaintext.
POST/api/v1/auth/organizations/key/rotateIssue a new key and deactivate the current one. Returns the new plaintext secret.
JSON
{
  "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:

Shell
curl https://your-infravoice-host/api/v1/agents \
  -H "Authorization: Bearer usf-YOUR_ORG_KEY"
Python
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"])
JavaScript
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.

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

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

Shell
# 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.

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

Shell
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:

Text
https://your-infravoice-host/api/v1/telephony/calls/inbound

That 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_dirty on the agent: if it is true, your providers are sitting in an unpublished draft.

The same thing in Python

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

StepPage
1, 2, 3 — agent, providers, publishAgents
4, 5 — number, webhook, outbound callsTelephony & Campaigns
Talking to the agent from your own app instead of a phoneRealtime Voice
Reading the transcript afterwardsRealtime Voice → Conversations

Key scope

The organization key is deliberately simple, and you should understand its blast radius before you hand it out.

ScopeYour entire organization — every workspace in it, acting as the organization owner.
GranularityThere is no read-only key, no per-workspace key, and no per-endpoint scoping. One key, full access.
Where it worksEvery 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 as X-API-Key (not Authorization). Your usf- 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.

JSON
{
  "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:

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

CodeMeaning
200 / 201Success.
400Malformed request — a required field is missing or invalid.
401Missing, malformed or revoked key. error.code is UNAUTHORIZED.
403Authenticated, but not allowed to touch this workspace or resource. error.code is FORBIDDEN.
404No such resource, or it belongs to another organization.
422The request was understood but cannot be fulfilled — most often an agent that is missing a required provider.
429Rate limited. Back off and retry.
500Something 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:

Shell
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_dirty on the agent: when it is true you 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:

PageWhat you'll do
AgentsCreate an agent, give it a prompt, attach LLM / speech-to-text / text-to-speech providers, add tools and knowledge, then publish it.
Realtime Voice & ConversationsTalk to an agent over a WebSocket, then read back transcripts and summaries.
Telephony & CampaignsPut an agent on a phone number, place outbound calls, run outbound campaigns.
Call Center & Human HandoffSet up projects, human agents and queues so a caller can reach a person.
Human Agent Portal APIThe separate API your human agents' client uses to sign in, hold presence and take calls.
Analytics, QA & BillingMeasure latency and usage, score calls against a rubric, and read your balance.
Speech-to-TextThe 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.