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#

Pagination#

List endpoints accept page and per_page (default 25, max 100) and return Laravel-style links/meta:

cURL / shell
curl "https://api.vollo.io/api/v1/calls?page=2&per_page=50&status=completed" \
  -H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc"
JSON200 OK — list envelope
{
  "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:

HTTP429 Too Many Requests
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:

JSON402 Payment Required
{
  "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.

MethodPathDescriptionScope
GET/projectsList projectsread
POST/projectsCreate a projectwrite
GET/projects/{id}Retrieve a projectread
PATCH/projects/{id}Update name/descriptionwrite
DELETE/projects/{id}Delete (must be empty of live resources)write
cURL / shellCreate a project
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" }'
JSON201 Created
{
  "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.

MethodPathDescriptionScope
GET/agentsList agents (filter: project_id)read
POST/agentsCreate an agentwrite
GET/agents/{id}Retrieve an agentread
PATCH/agents/{id}Update (applies to future calls immediately)write
DELETE/agents/{id}Delete (fails 409 if referenced by a workflow)write
cURL / shellCreate an agent
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.

MethodPathDescriptionScope
GET/workflowsList workflows (filter: project_id)read
POST/workflowsCreate (draft) — body: project_id, name, optional graphwrite
GET/workflows/{id}Retrieve, including the draft graphread
PATCH/workflows/{id}Update the draft name/graphwrite
DELETE/workflows/{id}Delete (fails 409 if attached to a number)write
POST/workflows/{id}/validateValidate the draft; never mutatesread
POST/workflows/{id}/publishPublish the draft — or re-publish a versionwrite
GET/workflows/{id}/versionsList published versionsread

Validate#

cURL / shell
curl -X POST https://api.vollo.io/api/v1/workflows/7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0/validate \
  -H "Authorization: Bearer pk_live_8f3KJd92mA4qL7Zx1RttVWpc"
JSON200 OK — issues found (still 200; validation ran)
{
  "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.

JSON200 OK
{
  "data": {
    "id": "7f3a9d2e-8c1b-4f6a-b5d4-e2c9a1f7b8d0",
    "name": "Support intake",
    "published_version": 4,
    "published_at": "2026-08-18T09:02:10Z"
  }
}

Versions#

JSONGET /workflows/{id}/versions — 200 OK
{
  "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.

MethodPathDescriptionScope
GET/sessionsList sessions (filters: project_id, channel, status)read
POST/sessionsMint a web session + join tokencalls:write
GET/sessions/{id}Retrieve (never includes the join token again)read
DELETE/sessions/{id}Force-end an active sessioncalls:write

Create a session#

Body fieldType
project_idstringRequired
workflow_idstringRequired — must have a published version
variablesobjectOptional — seeds workflow variables
metadataobjectOptional — 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.

MethodPathDescriptionScope
GET/callsList calls (filters: project_id, direction, status, from, to)read
POST/callsStart an outbound callcalls:write
GET/calls/{id}Retrieve a callread

Start an outbound call#

Body fieldType
project_idstringRequired
workflow_idstringRequired — must have a published version
fromstringRequired — a registered number in E.164
tostringRequired — destination in E.164
variablesobjectOptional — seeds workflow variables
metadataobjectOptional — echoed back in webhooks
cURL / shellPOST /calls
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" }
  }'
JavaScriptPOST /calls
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"
PHPPOST /calls (Laravel HTTP client)
<?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
JSONGET /calls/{id} — 200 OK, after the call
{
  "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.

MethodPathDescriptionScope
GET/phone-numbersList numbers (filter: project_id)read
POST/phone-numbersRegister a number on a trunktelephony
GET/phone-numbers/{id}Retrieve, including dispatch rulesread
PATCH/phone-numbers/{id}Update label, project, or dispatch_rulestelephony
DELETE/phone-numbers/{id}Unregister (inbound calls then get SIP 404)telephony
JSONGET /phone-numbers/{id} — 200 OK
{
  "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.

MethodPathDescriptionScope
GET/sip-trunksList trunksread
POST/sip-trunksCreate a trunktelephony
GET/sip-trunks/{id}Retrieve (password never returned)read
PATCH/sip-trunks/{id}Update URIs, credentials, allowed IPstelephony
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.

MethodPathDescriptionScope
GET/conversationsList (filters: project_id, session_id, from_date, to_date)read
GET/conversations/{id}Retrieve with full messages arrayread

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.

MethodPathDescriptionScope
GET/usageAggregates (params: from, to, group_by)usage:read
cURL / shell
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"
JSON200 OK
{
  "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.

MethodPathDescriptionScope
GET/api-keysList keys — prefix, scopes, last_used_at; never the secretread
POST/api-keysCreate a key — the secret appears only in this responsewrite
DELETE/api-keys/{id}Revoke immediately and permanentlywrite
cURL / shellCreate a key
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"] }'
JSON201 Created — copy the key now
{
  "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"
  }
}