VyoraVyora
API Reference

Vyora API

Trigger AI voice calls and receive real-time events. One endpoint. Works with any backend, Zapier, or Make.

Connecting this to a CRM? See how Vyora integrates with Salesforce, HubSpot, and Zoho.

One endpoint

POST /v1/calls to trigger any call instantly

Real-time events

Receive transcripts and analysis via webhook

API key auth

Simple header-based auth, no OAuth complexity

Base URL

https://api.vyora.ai

Authentication

All requests require an X-API-KEY header. Generate your key from Settings, Integrations.

Keep your API key secret. Never expose it in client-side JavaScript or public repos. If compromised, revoke it immediately from Settings and generate a new one.

X-API-KEY: vya_live_your_key_here

Rate limits

When rate limited, the API returns 429 Too Many Requests. Use exponential backoff before retrying.

API requests · Per API key
60 / minute
Concurrent calls · Per workspace
Plan dependent
Webhook timeout · Per delivery attempt
5 seconds
// Exponential backoff, retry up to 3 times
async function callWithRetry(payload, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const res = await fetch('https://api.vyora.ai/v1/calls', { ... });
    if (res.status !== 429) return res;
    await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
  }
  throw new Error('Rate limit exceeded after retries');
}
GET/v1/agents

List agents

Returns all active agents in your workspace. Pass an agent's id as agent_id when triggering calls. Agent IDs are also shown in your dashboard under Agents. The variables array lists exactly what that agent expects in custom_args — read it before you trigger a call rather than guessing the names. An empty array means the agent takes no variables.
Parameters
limit
integer · optional
Max agents to return. Default 50, max 100.
offset
integer · optional
Number of agents to skip. Default 0. Use with limit for pagination.
curl https://api.vyora.ai/v1/agents \
  -H "X-API-KEY: vya_live_your_key_here"
200 OK
{
  "agents": [
    {
      "id": "abc123-...", "name": "Home Loan Agent", "language": "hi-IN",
      "agent_type": "outbound",
      "variables": ["callee_name", "callee_first_name", "mobile_number"]
    },
    {
      "id": "def456-...", "name": "Admission Enquiry", "language": "en-IN",
      "agent_type": "outbound",
      "variables": []
    }
  ],
  "total": 2,
  "limit": 50,
  "offset": 0
}
GET/v1/numbers

List numbers

Returns the caller-line numbers assigned to your workspace, each with its id. Pass that id as from_number_id when triggering a call to select which line places it. It is required on every call and must be an ID; raw phone numbers are not accepted.
curl https://api.vyora.ai/v1/numbers \
  -H "X-API-KEY: vya_live_your_key_here"
200 OK
{
  "numbers": [
    { "id": "5d7f9a2b-...", "phone_number": "+918031137388", "display_name": "Vyora Line 1" },
    { "id": "12dcae4c-...", "phone_number": "+18085158398",  "display_name": "US Line" }
  ]
}
GET/v1/calls

List calls

Returns calls in your workspace, most recent first. Paginated. Filter by status to fetch only completed, failed, or ongoing calls. Each row carries the AI analysis, including your Post-Call Analysis fields under analysis.custom. For the full transcript, use GET /v1/calls/:id.
Parameters
limit
integer · optional
Max calls to return. Default 50, max 100.
offset
integer · optional
Number of calls to skip. Default 0. Use with limit for pagination.
status
string · optional
Filter by status, e.g. completed, failed, ongoing, registered.
curl "https://api.vyora.ai/v1/calls?limit=20&status=completed" \
  -H "X-API-KEY: vya_live_your_key_here"
200 OK
{
  "calls": [
    {
      "call_id": "abc123xyz",
      "agent_id": "def456-...",
      "agent_name": "Home Loan Agent",
      "contact_name": "Rahul Sharma",
      "phone_number": "+919876543210",
      "status": "completed",
      "direction": "outbound",
      "duration_seconds": 47,
      "recording_url": "https://lnogjboxjhnngectkffk.supabase.co/functions/v1/get-recording?call_id=abc123xyz",
      "analysis": {
        "summary": "Lead expressed strong interest, asked about EMI options.",
        "classification": "Interested",
        "custom": { "is_correct_person": true, "openness_to_discuss": true }
      },
      "campaign_id": null,
      "voicemail": false,
      "custom_args": { "name": "Rahul Sharma", "product": "Home Loan" },
      "called_at": "2026-05-24T10:30:00Z"
    }
  ],
  "total": 134,
  "limit": 20,
  "offset": 0
}
GET/v1/calls/:call_id

Get call

Fetch the full record for a single call: status, transcript, recording URL, and AI analysis. Use the call_id returned by POST /v1/calls. Any Post-Call Analysis fields you configured on the agent come back under analysis.custom, keyed by your own field names. Transcript and analysis appear only once processing has finished; poll with the snippet on the right if you are not using webhooks. recording_url is a permanent redirect — store it as-is and fetch it whenever you like; it resolves to a freshly-signed audio URL on every request, so it never goes stale.
curl https://api.vyora.ai/v1/calls/abc123xyz \
  -H "X-API-KEY: vya_live_your_key_here"
200 OK
{
  "call_id": "abc123xyz",
  "agent_id": "def456-...",
  "agent_name": "Home Loan Agent",
  "contact_name": "Rahul Sharma",
  "phone_number": "+919876543210",
  "status": "completed",
  "direction": "outbound",
  "duration_seconds": 47,
  "recording_url": "https://lnogjboxjhnngectkffk.supabase.co/functions/v1/get-recording?call_id=abc123xyz",
  "transcript": [
    { "bot": "Hi Rahul, I'm calling about your home loan inquiry..." },
    { "user": "Yes, I was interested in learning more..." }
  ],
  "analysis": {
    "summary": "Lead expressed strong interest, asked about EMI options.",
    "classification": "Interested",
    "custom": {
      "is_correct_person": true,
      "openness_to_discuss": true
    }
  },
  "campaign_id": null,
  "voicemail": false,
  "custom_args": { "name": "Rahul Sharma", "product": "Home Loan" },
  "called_at": "2026-05-24T10:30:00Z"
}
404
{ "error": "Call not found" }
POST/v1/calls

Trigger a call

Starts an outbound AI call immediately. Identify the agent with agent_id (from GET /v1/agents) and the caller line with from_number_id (from GET /v1/numbers); both required, both IDs. The agent calls the recipient, runs the conversation, and delivers results via webhook. A variable mismatch never blocks the call — if custom_args is missing something the agent declares, or carries a name it does not, the call still goes out and the response includes a warnings array saying so. Check it in staging: an empty variable is a silently worse call, not an error.
Parameters
phone_number
string · required
The number to call (the recipient). E.164 format, e.g. +919876543210
agent_id
string · required
Agent ID from GET /v1/agents. Identifies which agent runs the call.
from_number_id
string · required
Caller-line ID from GET /v1/numbers. Selects which of your assigned numbers places the call. Must be an ID.
custom_args
object · optional
Values for the variables the agent declares. Get the exact names from the variables array on GET /v1/agents. Anything the agent declares but you omit is sent empty and reported in warnings; anything it does not declare is ignored.
agent_name
string · optional
Legacy. Case-insensitive agent name, accepted as a fallback when agent_id is omitted. Prefer agent_id.
curl -X POST https://api.vyora.ai/v1/calls \
  -H "X-API-KEY: vya_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+919876543210",
    "agent_id": "def456-...",
    "from_number_id": "5d7f9a2b-...",
    "custom_args": {
      "name": "Rahul Sharma",
      "product": "Home Loan"
    }
  }'
200 OK
{
  "call_id": "abc123xyz",
  "status": "registered"
}
200 + warnings
{
  "call_id": "abc123xyz",
  "status": "registered",
  "warnings": [
    "Agent declares variable \"callee_first_name\" but no value was provided - it will be empty during the call.",
    "Ignored \"first_name\" - not a variable declared on this agent."
  ]
}
4xx / 5xx
{
  "error": "Insufficient credits"
}

Webhooks

Register a webhook URL in Settings, Integrations. Vyora POSTs to that URL after each call event. Your endpoint must respond with 200 within 5 seconds.

X-Vyora-Event

Event name, e.g. call_completed

X-Vyora-Signature

HMAC-SHA256 signature for verification

User-Agent

Always Vyora-Webhooks/1.0

Signature verification

Every webhook includes an X-Vyora-Signature header, an HMAC-SHA256 of the raw request body signed with your webhook secret (format whsec_…). A secret is created the first time you save a webhook URL. Always verify it before processing to prevent spoofed requests, and use timing-safe comparison (never ===).

// Express.js, verify signature before processing
const crypto = require('crypto');

function verifySignature(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  // Use timingSafeEqual to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig    = req.headers['x-vyora-signature'];
  const secret = process.env.VYORA_WEBHOOK_SECRET;

  if (!sig || !verifySignature(req.body, sig, secret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const body  = JSON.parse(req.body);
  const event = req.headers['x-vyora-event'];

  if (event === 'call_completed') {
    console.log(`Call ${body.call_id} ended, status: ${body.status}`);
    console.log('Summary:', body.analysis?.summary);
    // Your Post-Call Analysis fields, keyed by the names you configured
    console.log('Custom:',  body.analysis?.custom);
  }

  res.status(200).json({ received: true });
});
Request headers
POST https://your-server.com/webhook
Content-Type: application/json
X-Vyora-Event: call_completed
X-Vyora-Signature: sha256=a4b3c2d1e0f9...
User-Agent: Vyora-Webhooks/1.0
Example payload
{
  "event": "call_completed",
  "call_id": "abc123xyz",
  "phone_number": "+919876543210",
  "contact_name": "Rahul Sharma",
  "agent_id": "your-agent-id",
  "agent_name": "Home Loan Agent",
  "status": "completed",
  "direction": "outbound",
  "duration_seconds": 47,
  "recording_url": "https://lnogjboxjhnngectkffk.supabase.co/functions/v1/get-recording?call_id=abc123xyz",
  "transcript": [
    { "bot": "Hi Rahul, I'm calling about your home loan inquiry..." },
    { "user": "Yes, I was interested in learning more..." }
  ],
  "analysis": {
    "summary": "Lead expressed strong interest, asked about EMI options.",
    "classification": "Interested",
    "custom": {
      "is_correct_person": true,
      "openness_to_discuss": true
    },
    "platform": { "...": "full Ringg platform analysis" }
  },
  "campaign_id": "campaign_456",
  "voicemail": false,
  "custom_args": { "name": "Rahul Sharma", "product": "Home Loan" },
  "called_at": "2026-05-24T10:30:00Z",
  "timestamp": "2026-05-24T10:31:15Z"
}

Event types

One webhook is delivered per call. It fires once everything for that call is finished — the recording is stored and the AI analysis has run — so a single payload carries the full result and you never have to stitch several events together. Both the event field and the X-Vyora-Event header are always call_completed.

call_completed
Sent once per call, after processing finishes. Carries status, duration, recording URL, transcript, AI analysis and your custom analysis fields.
test
Sent only when you press Send test in Settings, Integrations. Same signature scheme, dummy body — use it to validate your verification code.

Error codes

400
Bad Request
Missing or invalid field. Check phone_number format, agent_id / agent_name, or JSON body
401
Unauthorized
API key is missing, invalid, or revoked
402
Payment Required
Insufficient credits. Top up or upgrade your plan
403
Forbidden
Workspace is inactive or suspended
404
Not Found
No agent found matching agent_id / agent_name, or call ID does not exist
405
Method Not Allowed
Wrong method for that path, e.g. POST to /v1/agents. Only POST /v1/calls triggers a call
429
Too Many Requests
Rate limit exceeded. Back off and retry with exponential delay. The Retry-After header tells you how many seconds remain in the current window
500
Internal Server Error
Something went wrong on our end. Retry with backoff
502
Bad Gateway
Webhook delivery failed. Your endpoint did not respond 200

Ready to build?

Generate your API key from the dashboard and make your first call in under 2 minutes.

Get API Key

We use cookies to improve your experience. By continuing to use this site, you agree to our Privacy Policy.