API Reference

Vyora API

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

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.

X-API-KEY: vya_live_your_key_here

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.

Rate limits

Limit
Value
Applies to
API requests
60 / minute
Per API key
Concurrent calls
Plan dependent
Per workspace
Webhook timeout
5 seconds
Per delivery attempt

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

// 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');
}

List agents

GET/v1/agents

Returns all active agents in your workspace. Use the id or name when triggering calls. Agent IDs are also shown in your dashboard under Agents → copy icon.

Param
Type
Description
limit
integer
Max agents to return. Default 50, max 100.
offset
integer
Number of agents to skip. Default 0. Use with limit for pagination.

Request

curl https://api.vyora.ai/v1/agents \
  -H "X-API-KEY: vya_live_your_key_here"

Paginated request

curl "https://api.vyora.ai/v1/agents?limit=10&offset=20" \
  -H "X-API-KEY: vya_live_your_key_here"

Response

{
  "agents": [
    { "id": "abc123-...", "name": "Home Loan Agent",   "language": "hi-IN", "agent_type": "outbound" },
    { "id": "def456-...", "name": "Admission Enquiry", "language": "en-IN", "agent_type": "outbound" }
  ],
  "total": 2,
  "limit": 50,
  "offset": 0
}

Get call

GET/v1/calls/:call_id

Fetch the full record for a single call — status, transcript, recording URL, and AI analysis. Use the call_id returned by POST /v1/calls. Note: transcript and analysis are only available after all_processing_completed fires.

Request

curl https://api.vyora.ai/v1/calls/abc123xyz \
  -H "X-API-KEY: vya_live_your_key_here"

Response

200 OKSuccess
{
  "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://cdn.vyora.ai/recordings/abc123.mp3",
  "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"
  },
  "called_at": "2026-05-24T10:30:00Z"
}
404Not found
{ "error": "Call not found" }

Also returned if the call belongs to a different workspace — IDs are not guessable but access is always scoped to your API key.

Polling for completion

If you need synchronous-style behaviour without webhooks, poll until status is completed and analysis is non-null. Webhooks are more efficient for production use.

async function waitForAnalysis(callId, apiKey, maxWaitMs = 60000) {
  const start = Date.now();
  while (Date.now() - start < maxWaitMs) {
    const res  = await fetch(`https://api.vyora.ai/v1/calls/${callId}`,
                   { headers: { 'X-API-KEY': apiKey } });
    const call = await res.json();
    if (call.status === 'completed' && call.analysis) return call;
    await new Promise(r => setTimeout(r, 3000)); // poll every 3s
  }
  throw new Error('Timed out waiting for call analysis');
}

Trigger a call

POST/v1/calls

Starts an outbound AI call immediately. The agent will call the number, run the conversation, and deliver results via webhook.

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_name": "Home Loan Agent",
    "custom_args": {
      "name": "Rahul Sharma",
      "product": "Home Loan"
    }
  }'

Request body

Field
Type
Description
phone_numberrequired
string
E.164 format. e.g. +919876543210
agent_name
string
Name of the agent to use — e.g. 'Home Loan Agent'. Easier than agent_id. Case-insensitive.
agent_id
string
Agent ID from GET /v1/agents. Use this or agent_name — one is required.
custom_args
object
Key-value pairs passed to the agent as context. Use name, product, intent, etc.

Response

200 OKSuccess
{
  "call_id": "abc123xyz",
  "status": "registered"
}
4xx / 5xxError
{
  "error": "Insufficient credits"
}

Webhooks

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

Request headers

Every webhook request includes these 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
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. Find your secret in Settings → Integrations. Always verify the signature before processing the event to prevent spoofed requests.

// 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}`);
  }

  if (event === 'all_processing_completed') {
    console.log('Analysis ready:', body.analysis?.summary);
  }

  res.status(200).json({ received: true });
});

Always use timing-safe comparison (crypto.timingSafeEqual / hmac.compare_digest) to prevent timing attacks. Never compare signatures with ===.

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://cdn.vyora.ai/recordings/abc123.mp3",
  "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"
  },
  "campaign_id": "campaign_456",
  "custom_args": { "name": "Rahul Sharma", "product": "Home Loan" },
  "called_at": "2026-05-24T10:30:00Z",
  "timestamp": "2026-05-24T10:31:15Z"
}

Event types

Event
When it fires
call_started
Call connects to the recipient
call_completed
Call ends — includes duration, status, recording URL
all_processing_completed
Transcript + AI analysis ready (summary, classification)

Select which events to receive in Settings → Integrations. Unsubscribed events are not delivered.

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_name, or call ID does not exist
429
Too Many Requests
Rate limit exceeded — back off and retry with exponential delay
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.