Build and run voice agents with the API

Create assistants, start calls, attach tools and knowledge, read results and receive events through the JSON REST API.

Bearer keys

Private keys for servers; public keys for browsers, limited to starting calls and reading assistants.

Plain JSON

Resources are JSON documents. List endpoints return results and metadata.

Webhooks

Status updates, tool calls and end-of-call reports are posted to your server with a shared secret.

Rate limits

Per organization, reported in X-RateLimit-Limit and X-RateLimit-Remaining.

Your first call in three requests

Create a private key in the dashboard under API keys. It is shown once; Ryffio stores only a hash.

1. Create an assistant

An assistant is a prompt, a first message, and the speech, model and voice providers it uses.

POST /assistantcurl
curl https://app.ryffio.com/assistant \
  -H "Authorization: Bearer $RYFFIO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Front desk",
    "firstMessage": "Thanks for calling. How can I help?",
    "model": { "provider": "openai", "model": "gpt-4.1-mini",
      "messages": [{ "role": "system",
        "content": "You answer calls for a dental clinic." }] },
    "transcriber": { "provider": "openai", "model": "gpt-transcribe" },
    "voice": { "provider": "openai", "voiceId": "alloy" }
  }'

2. Start a web call

The reply carries a single-use webCallUrl. Over WebSocket it takes audio frames; for WebRTC pass "transport": { "provider": "webrtc" } and post your SDP offer to the returned URL.

A public key (safe in a browser) can start web calls but can't change anything.

POST /call/webcurl
curl https://app.ryffio.com/call/web \
  -H "Authorization: Bearer $RYFFIO_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "assistantId": "<assistant id>" }'

# => { "id": "...", "status": "queued",
#      "webCallUrl": "wss://app.ryffio.com/call/.../ws?token=..." }

3. Read the result

When the call ends the record holds the transcript, messages, summary, cost breakdown, any structured outputs and the end reason. Poll it, or have it posted to your server.

GET /call/{id}curl
curl https://app.ryffio.com/call/<call id> \
  -H "Authorization: Bearer $RYFFIO_KEY"

# => { "status": "ended", "endedReason": "assistant-ended-call",
#      "summary": "...", "cost": 0.0412,
#      "costBreakdown": { "stt": ..., "llm": ..., "tts": ..., "total": ... },
#      "artifact": { "structuredOutputs": { ... } } }

What the API covers

ResourceWhat it doesEndpoints
AssistantsPrompt, voice, model, transcriber, limits; versions and traffic splits/assistant
CallsWeb, WebRTC and phone calls; records, recordings, control of a live call/call/web /call /call/{id}
ToolsFunction, API request, transfer and end-call tools/tool
KnowledgeKnowledge bases, files and indexing status/v2/knowledge-base /file
Structured outputsSchemas extracted after each call/structured-output
SquadsSeveral assistants in one call with handoffs/squad
Phone numbersTwilio numbers pointed at an assistant or squad/phone-number
CampaignsOutbound dialing, results per customer, do-not-call list/campaign /dnc
Evals and simulationsTest suites, eval runs, simulated callers/eval /test-suite
AnalyticsQueries, boards, insights, cost reconciliation/analytics /reporting/*
OrganizationMembers, API keys, credentials, audit log, data export/org/{id}/* /token /credential

Events, signed with your secret

Set a server URL on the assistant (or the organization) and choose which events you want. Each is a JSON POST carrying your secret in X-Ryffio-Secret. Use the same endpoint for tool calls and answer with { "results": [{ "toolCallId", "result" }] }.

  • status-update: the call started or ended
  • tool-calls: the agent wants your server to run something
  • end-of-call-report: transcript, summary, cost, structured outputs

Requests to your URLs must be public HTTPS; private and internal addresses are refused.

your server, on end of callPOST
{
  "message": {
    "type": "end-of-call-report",
    "endedReason": "customer-ended-call",
    "summary": "Caller moved Thursday to Friday.",
    "cost": 0.0412,
    "artifact": { "transcript": "..." }
  }
}

Call your tools

A function tool tells the model what it can ask your server to do. Ryffio posts the call, waits, and feeds the reply back into the conversation.

POST /toolJSON
{
  "type": "function",
  "function": {
    "name": "find_slot",
    "description": "Find an open appointment slot.",
    "parameters": { "type": "object",
      "properties": { "day": { "type": "string" } },
      "required": ["day"] }
  },
  "server": { "url": "https://api.example.com/slots", "secret": "..." }
}

Get the fields you need, every call

Define a JSON schema, list its id in the assistant's artifactPlan.structuredOutputIds, and the extracted object appears in artifact.structuredOutputs on the call record.

POST /structured-outputJSON
{
  "name": "Appointment request",
  "schema": {
    "type": "object",
    "properties": {
      "intent": { "type": "string" },
      "preferredDay": { "type": "string" }
    }
  }
}

API behavior and errors

Errors

Failures return { "message", "error", "statusCode" } with an honest status code: 400 for bad input, 401 for a bad key, 403 for missing permission, 429 when rate-limited.

Keys and roles

Private keys act for the organization. Members have admin, editor or viewer roles. Sensitive actions are recorded in the audit log.

Draining deploys

During a restart, new calls get 503 with Retry-After while live calls finish, so retrying is safe.

Make your first request

Create an account, generate a key, and send the three requests above.