Reference
API reference
The complete REST surface of the Vollo public API, v1. JSON in, JSON out, bearer-token auth, predictable Laravel-style pagination.
- Base URL
- https://api.vollo.io/api/v1
- Auth header
- Authorization: Bearer pk_live_…
- Content type
- application/json
- Rate limit
- 120 req/min per workspace
Conventions#
- All paths below are relative to
https://api.vollo.io/api/v1. Requests must sendContent-Type: application/jsonand anAuthorizationheader (see Authentication). - IDs are UUIDs. Timestamps are UTC ISO 8601 (
2026-08-18T09:12:44Z). Phone numbers are E.164. - Single resources are wrapped in
{"data": {…}}; lists in{"data": [...], "links": {…}, "meta": {…}}. - Successful creates return
201; updates200; deletes204with an empty body. - Errors use the envelope
{"message": "…", "errors": {…}}— see Errors. metadataon calls and sessions is yours: up to 20 keys, string/number/boolean values, echoed back verbatim in responses and webhooks.
Pagination#
List endpoints accept page and per_page (default 25, max 100) and return Laravel-style links/meta:
curl "https://api.vollo.io/api/v1/calls?page=2&per_page=50&status=completed" \
-H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc"{
"data": [ { "id": "…" } ],
"links": {
"first": "https://api.vollo.io/api/v1/calls?page=1",
"last": "https://api.vollo.io/api/v1/calls?page=7",
"prev": "https://api.vollo.io/api/v1/calls?page=1",
"next": "https://api.vollo.io/api/v1/calls?page=3"
},
"meta": {
"current_page": 2,
"from": 51,
"last_page": 7,
"path": "https://api.vollo.io/api/v1/calls",
"per_page": 50,
"to": 100,
"total": 324
}
}Lists are ordered newest-first. Follow links.next until it is null.
Rate limits & quotas#
Two distinct throttles apply:
Rate limit — 120 requests per minute per workspace. Over the limit, requests fail fast:
HTTP/1.1 429 Too Many Requests
Retry-After: 21
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
{ "message": "Too many requests. Retry after 21 seconds." }Plan quota — actions that consume plan resources (starting calls, minting sessions) fail with 402 Payment Required when the workspace quota is exhausted. This is not retryable until the quota resets or the plan is upgraded:
{
"message": "Voice minutes quota exceeded for the current billing period.",
"errors": {
"quota": [
"Plan allows 2000 voice minutes per month; 2000 used. Resets 2026-09-01T00:00:00Z."
]
}
}Projects#
Projects group agents, workflows, phone numbers, sessions, and webhook endpoints. Most other resources require a project_id.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /projects | List projects | read |
| POST | /projects | Create a project | write |
| GET | /projects/{id} | Retrieve a project | read |
| PATCH | /projects/{id} | Update name/description | write |
| DELETE | /projects/{id} | Delete (must be empty of live resources) | write |
curl -X POST https://api.vollo.io/api/v1/projects \
-H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc" \
-H "Content-Type: application/json" \
-d '{ "name": "Support line", "description": "Inbound support, EN + AR" }'{
"data": {
"id": "9b2e7c1a-4d3f-4e8a-9c5b-2f7d1e6a8b3c",
"name": "Support line",
"description": "Inbound support, EN + AR",
"created_at": "2026-08-18T08:41:02Z",
"updated_at": "2026-08-18T08:41:02Z"
}
}Agents#
Reusable conversational configurations referenced by Agent nodes. See Workflows vs. agents.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /agents | List agents (filter: project_id) | read |
| POST | /agents | Create an agent | write |
| GET | /agents/{id} | Retrieve an agent | read |
| PATCH | /agents/{id} | Update (applies to future calls immediately) | write |
| DELETE | /agents/{id} | Delete (fails 409 if referenced by a workflow) | write |
curl -X POST https://api.vollo.io/api/v1/agents \
-H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc" \
-H "Content-Type: application/json" \
-d '{
"project_id": "9b2e7c1a-4d3f-4e8a-9c5b-2f7d1e6a8b3c",
"name": "Support agent",
"system_prompt": "You are a friendly support agent for Acme. Be concise; speak naturally.",
"llm_provider": "anthropic",
"llm_model": "claude-sonnet-4-5",
"temperature": 0.4,
"stt_provider": "deepgram",
"stt_language": "en",
"tts_provider": "cartesia",
"tts_voice": "warm-en-female-2",
"greeting": "Hi! You have reached Acme support."
}'Key fields: llm_provider is one of openai, anthropic, google, openrouter; stt_provider is deepgram; tts_provider is cartesia. Optional arrays tools, knowledge_base_ids, and mcp_server_ids attach capabilities the agent may use mid-conversation.
Workflows#
The versioned node graphs — concepts in the Workflows guide, node schemas in the node reference.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /workflows | List workflows (filter: project_id) | read |
| POST | /workflows | Create (draft) — body: project_id, name, optional graph | write |
| GET | /workflows/{id} | Retrieve, including the draft graph | read |
| PATCH | /workflows/{id} | Update the draft name/graph | write |
| DELETE | /workflows/{id} | Delete (fails 409 if attached to a number) | write |
| POST | /workflows/{id}/validate | Validate the draft; never mutates | read |
| POST | /workflows/{id}/publish | Publish the draft — or re-publish a version | write |
| GET | /workflows/{id}/versions | List published versions | read |
Validate#
curl -X POST https://api.vollo.io/api/v1/workflows/7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0/validate \
-H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc"{
"data": {
"valid": false,
"errors": [
{ "node_id": "node_dtmf1", "field": "save_to_variable", "message": "This field is required for node type dtmf." }
],
"warnings": [
{ "node_id": "node_hello", "field": "text", "message": "Template references unknown variable customer_nane." }
]
}
}Publish#
Empty body publishes the current draft. Passing {"version": N} re-publishes an older version's graph as a new version (rollback). Fails 422 if validation errors exist.
{
"data": {
"id": "7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0",
"name": "Support intake",
"published_version": 4,
"published_at": "2026-08-18T09:02:10Z"
}
}Versions#
{
"data": [
{ "version": 4, "published_at": "2026-08-18T09:02:10Z", "published_by": "dev@acme.com", "schema_version": 1 },
{ "version": 3, "published_at": "2026-08-14T15:27:51Z", "published_by": "dev@acme.com", "schema_version": 1 }
]
}Fetch one version's frozen graph with GET /workflows/{id}/versions/{version}.
Sessions#
A session is one live interaction with a workflow — web, phone, or test. Web sessions are minted here; phone sessions are created automatically by calls. Full guide: Web voice sessions.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /sessions | List sessions (filters: project_id, channel, status) | read |
| POST | /sessions | Mint a web session + join token | calls:write |
| GET | /sessions/{id} | Retrieve (never includes the join token again) | read |
| DELETE | /sessions/{id} | Force-end an active session | calls:write |
Create a session#
| Body field | Type | |
|---|---|---|
project_id | string | Required |
workflow_id | string | Required — must have a published version |
variables | object | Optional — seeds workflow variables |
metadata | object | Optional — echoed back in webhooks |
Request/response example and the LiveKit connection snippet live in the sessions guide.
Calls#
Phone call records — created automatically for inbound calls, or by you for outbound.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /calls | List calls (filters: project_id, direction, status, from, to) | read |
| POST | /calls | Start an outbound call | calls:write |
| GET | /calls/{id} | Retrieve a call | read |
Start an outbound call#
| Body field | Type | |
|---|---|---|
project_id | string | Required |
workflow_id | string | Required — must have a published version |
from | string | Required — a registered number in E.164 |
to | string | Required — destination in E.164 |
variables | object | Optional — seeds workflow variables |
metadata | object | Optional — echoed back in webhooks |
curl -X POST https://api.vollo.io/api/v1/calls \
-H "Authorization: Bearer $VOLLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": "9b2e7c1a-4d3f-4e8a-9c5b-2f7d1e6a8b3c",
"workflow_id": "7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0",
"from": "+12125550142",
"to": "+962790123456",
"metadata": { "crm_id": "A-1042" }
}'const res = await fetch('https://api.vollo.io/api/v1/calls', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.VOLLO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
project_id: '9b2e7c1a-4d3f-4e8a-9c5b-2f7d1e6a8b3c',
workflow_id: '7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0',
from: '+12125550142',
to: '+962790123456',
metadata: { crm_id: 'A-1042' },
}),
});
const { data: call } = await res.json();
console.log(call.id, call.status); // "…", "queued"<?php
use Illuminate\Support\Facades\Http;
$call = Http::withToken(env('VOLLO_API_KEY'))
->post('https://api.vollo.io/api/v1/calls', [
'project_id' => '9b2e7c1a-4d3f-4e8a-9c5b-2f7d1e6a8b3c',
'workflow_id' => '7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0',
'from' => '+12125550142',
'to' => '+962790123456',
'metadata' => ['crm_id' => 'A-1042'],
])
->throw()
->json('data');
echo $call['id'] . ' — ' . $call['status']; // queued{
"data": {
"id": "c4d9e2f7-1b8a-4e3c-9f6d-7a2b5c8e1d4a",
"direction": "outbound",
"status": "completed",
"from": "+12125550142",
"to": "+962790123456",
"project_id": "9b2e7c1a-4d3f-4e8a-9c5b-2f7d1e6a8b3c",
"workflow_id": "7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0",
"workflow_version": 3,
"session_id": "5e8b3c1d-9f2a-4d7e-b6c8-1a4f7d2e9b3c",
"duration_seconds": 63,
"hangup_reason": "callee_hangup",
"metadata": { "crm_id": "A-1042" },
"started_at": "2026-08-18T09:12:51Z",
"ended_at": "2026-08-18T09:13:54Z",
"created_at": "2026-08-18T09:12:44Z"
}
}status progresses queued → ringing → in_progress → completed, or ends as no_answer, busy, failed, or cancelled. Statuses are also pushed via webhooks.
Phone numbers#
E.164 numbers registered on your SIP trunks, with dispatch rules for inbound routing. Concepts and rule shapes: Telephony.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /phone-numbers | List numbers (filter: project_id) | read |
| POST | /phone-numbers | Register a number on a trunk | telephony |
| GET | /phone-numbers/{id} | Retrieve, including dispatch rules | read |
| PATCH | /phone-numbers/{id} | Update label, project, or dispatch_rules | telephony |
| DELETE | /phone-numbers/{id} | Unregister (inbound calls then get SIP 404) | telephony |
{
"data": {
"id": "f2a8c5e1-7d4b-4e9c-b3f6-8a1d5c2e7b9f",
"number": "+12125550142",
"label": "Main support line",
"sip_trunk_id": "b7e1d4c8-3f9a-4b2e-8c6d-1e5a9f3b7d2c",
"project_id": "9b2e7c1a-4d3f-4e8a-9c5b-2f7d1e6a8b3c",
"dispatch_rules": [
{ "workflow_id": "7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0", "priority": 10,
"time_window": { "days": ["mon","tue","wed","thu","fri"], "start": "09:00", "end": "17:00", "timezone": "America/New_York" } },
{ "workflow_id": "1d6b8e3a-5c2f-4a7d-9e4b-3f8c1a6d5e2b", "priority": 20 }
],
"created_at": "2026-08-18T08:55:31Z"
}
}SIP trunks#
Carrier connections. Setup walkthrough (Twilio and generic SIP): Telephony → SIP trunks.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /sip-trunks | List trunks | read |
| POST | /sip-trunks | Create a trunk | telephony |
| GET | /sip-trunks/{id} | Retrieve (password never returned) | read |
| PATCH | /sip-trunks/{id} | Update URIs, credentials, allowed IPs | telephony |
| DELETE | /sip-trunks/{id} | Delete (fails 409 while numbers reference it) | telephony |
Create-body fields: name, provider (twilio or generic), termination_uri, auth_username, auth_password, transport (tls, udp, tcp), optional allowed_ips. The response adds the workspace's origination_uri to configure at the carrier.
Conversations#
Read-only transcripts, one per session, populated live during the interaction.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /conversations | List (filters: project_id, session_id, from_date, to_date) | read |
| GET | /conversations/{id} | Retrieve with full messages array | read |
Messages carry sequence, role (user, assistant, system, tool), content, agent_name (when multiple agents ran), spoken_at, and latency_ms. Example response: sessions guide.
Usage#
Aggregated consumption metrics for billing and monitoring.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /usage | Aggregates (params: from, to, group_by) | usage:read |
curl "https://api.vollo.io/api/v1/usage?from=2026-08-01&to=2026-08-18&group_by=metric" \
-H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc"{
"data": {
"from": "2026-08-01",
"to": "2026-08-18",
"metrics": {
"voice_minutes": 1412.4,
"calls": 903,
"sessions": 1210,
"llm_input_tokens": 8214550,
"llm_output_tokens": 1093412,
"stt_seconds": 84744,
"tts_characters": 2418760
}
}
}group_by accepts metric (default), day, project, or provider.
API keys#
Manage workspace keys programmatically. Guide: Authentication.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /api-keys | List keys — prefix, scopes, last_used_at; never the secret | read |
| POST | /api-keys | Create a key — the secret appears only in this response | write |
| DELETE | /api-keys/{id} | Revoke immediately and permanently | write |
curl -X POST https://api.vollo.io/api/v1/api-keys \
-H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc" \
-H "Content-Type: application/json" \
-d '{ "name": "call-launcher (prod)", "scopes": ["calls:write"] }'{
"data": {
"id": "e9c2b5f8-1a7d-4c4e-9b3f-6d8a2e5c1f7b",
"name": "call-launcher (prod)",
"key": "pk_live_2mQx7Vd91kFhL3Zr8NssTYbe",
"prefix": "pk_live_2mQx",
"scopes": ["calls:write"],
"last_used_at": null,
"created_at": "2026-08-18T09:20:12Z"
}
}