Veytrix AI

VeytrixAI API Documentation & REST API Reference

Use the VeytrixAI voice calling API to place calls, read what was said, and act on the outcome — from your own code. Everything the dashboard does to a call, an agent or a list, you can do over HTTP with an API key.

Base URLhttps://veytrix-api.fly.dev/api
AuthAuthorization: Bearer acai_…
Formatapplication/json

Your first call in one request

Create a key in the dashboard under Settings → Developer, then dial your own phone. Nothing else needs to be set up first, as long as the workspace owns a number and has a wallet balance.

Place a call
curl -X POST https://veytrix-api.fly.dev/api/agents/YOUR_AGENT_ID/test-call \
  -H "Authorization: Bearer acai_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phoneNumber": "+919876543210" }'

Authentication

Every request carries an API key as a bearer token. Keys begin acai_, belong to one workspace, and act with admin rights inside it — treat one as a password for the whole workspace and keep it server-side.

Every request
-H "Authorization: Bearer acai_YOUR_KEY"

Creating and revoking

Keys are created in Settings → Developer. The full key is shown once, at creation — only a SHA-256 hash is stored, so a lost key cannot be recovered and has to be replaced. Revoking takes effect on the next request.

What a key can reach

A key is restricted to the twelve mounts below. Any other path answers 403 with “This endpoint is not available via API key” — including endpoints your own user can open in the dashboard. Billing changes, workspace settings, team management and the platform console are all deliberately outside the key's reach.

/agents/campaigns/batches/calls/alert/ratings/dashboard/analytics/wallet/balances/numbers/recordings

Rate limits

Each key gets 5,000 requests a day and 100,000 a month by default. Both can be raised per key. The daily window resets at midnight IST; the monthly one on the 1st.

Every response carries your current position:

HeaderMeaning
X-RateLimit-LimitYour daily ceiling.
X-RateLimit-RemainingRequests left today.
X-RateLimit-ResetUnix seconds until the window resets.
Retry-AfterSent only on a 429 — seconds to wait.
Response · 429
{
  "message": "Daily API limit reached (5,000 requests). Resets at midnight IST.",
  "scope": "daily",
  "limit": 5000,
  "used": 5000
}

Errors

Every error is JSON with a human-readable message. Show it to your operators — the text is written to be actionable rather than generic.

Error shape
{ "message": "Agent not found." }
StatusWhat it meansWhat to do
400The request is missing or malforming a field.Read message; it names the field.
401The key is wrong, or has been revoked.Issue a new key.
403Out of scope for a key, or a quota is exhausted.Check the scope list, then the wallet and minute quota.
404No such record in this workspace.A valid id from another workspace also 404s — that is intentional.
409That number already has a call in flight.The body carries the live callId; wait for it to settle.
429Rate limit reached.Back off for Retry-After seconds.

Alert dial

One request, many phones ringing at once. The fastest path from an event in your system to a live call — no batch to create, no start step, no lifecycle to manage.

POST/alert

Dial every number in the list immediately

Creates one call per phone number and enqueues all of them in the same request, then returns. Announcement agents bypass the per-organization concurrency counter, so the phones ring near-simultaneously rather than one after another. This is the endpoint to use for security alerts, outage notifications, and anything else where the delay of a batch would defeat the purpose.

https://veytrix-api.fly.dev/api/alert

Body

FieldTypeDescription
agentIdrequiredstringThe agent that will place the calls.
phonesrequiredstring[] | { name?, phone }[]Numbers in E.164 format (+919876543210). Accepts plain strings, or objects carrying a name used as the call's target name.
fromNumberstringCaller ID override. Omit and the server resolves it: the agent's telephony credential, then the workspace's default outbound number.
variablesobjectApplied to every call in the request. Fills {name}, {role} and any other placeholder in the welcome message and prompt.

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
  • Malformed numbers are skipped, not rejected — an alert still reaches the valid numbers, and `skipped` tells you how many were dropped. A request where every number is malformed returns 400.
  • Duplicate numbers in one request are dialed once.
  • Returns 403 when the monthly minute quota is exhausted, and 400 when the workspace owns no phone number.
  • The response returns before the calls connect. Subscribe to `call.completed` to learn how each one went.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/alert`, {
  method: "POST",
  headers,
  body: JSON.stringify({
      "agentId": "6f1c2f4a-1c7e-4c9a-9d21-2b6f5f0a1c33",
      "phones": ["+919876543210", "+919812345678"],
      "variables": { "site": "Gate 3", "severity": "high" }
    })
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Alert dialing 2 number(s) via plivo.",
  "calls": [
    { "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77", "phone": "+919876543210" },
    { "callId": "c9a7d1b2-33f8-42aa-8e10-7c5b9e2f4a18", "phone": "+919812345678" }
  ],
  "dialed": 2,
  "skipped": 0
}

Agents

The AI persona that runs a conversation: its prompt, voice, language, model and call behaviour. Create one here or in the dashboard — same object either way.

GET/agents

List every agent in the workspace

https://veytrix-api.fly.dev/api/agents

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
[
  {
    "id": "6f1c2f4a-1c7e-4c9a-9d21-2b6f5f0a1c33",
    "name": "Screening — Backend Engineer",
    "status": "active",
    "conversationEngine": "pipeline",
    "language": "English",
    "llmProvider": "Gemini",
    "llmModel": "gemini-2.5-flash"
  }
]
POST/agents

Create an agent

https://veytrix-api.fly.dev/api/agents

Body

FieldTypeDescription
namerequiredstringShown in the dashboard and on call records.
conversationEngine"pipeline" | "announcement" | "keypad"pipeline = two-way conversation (default). announcement = play one message and hang up. keypad = DTMF menu.
welcomeMessagestringSpoken the instant the call connects.
promptstringThe instructions the AI follows for the rest of the call.
languagestringDrives both transcription and the voice list.

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents`, {
  method: "POST",
  headers,
  body: JSON.stringify({
      "name": "Screening — Backend Engineer",
      "conversationEngine": "pipeline",
      "welcomeMessage": "Hi, this is Maya from Acme. Do you have two minutes?",
      "prompt": "Ask about years of experience, primary stack, notice period. Keep replies under two sentences."
    })
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "6f1c2f4a-1c7e-4c9a-9d21-2b6f5f0a1c33",
  "name": "Screening — Backend Engineer",
  "status": "active",
  "conversationEngine": "pipeline",
  "language": "English",
  "llmProvider": "Gemini",
  "llmModel": "gemini-2.5-flash"
}
GET/agents/{agentId}

Fetch a single agent

https://veytrix-api.fly.dev/api/agents/{agentId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "6f1c2f4a-1c7e-4c9a-9d21-2b6f5f0a1c33",
  "name": "Screening — Backend Engineer",
  "status": "active",
  "conversationEngine": "pipeline",
  "language": "English",
  "llmProvider": "Gemini",
  "llmModel": "gemini-2.5-flash"
}
PATCH/agents/{agentId}

Update any subset of an agent's fields

https://veytrix-api.fly.dev/api/agents/{agentId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}`, {
  method: "PATCH",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "6f1c2f4a-1c7e-4c9a-9d21-2b6f5f0a1c33",
  "name": "Screening — Backend Engineer",
  "status": "active",
  "conversationEngine": "pipeline",
  "language": "English",
  "llmProvider": "Gemini",
  "llmModel": "gemini-2.5-flash"
}
DELETE/agents/{agentId}

Soft-delete an agent

https://veytrix-api.fly.dev/api/agents/{agentId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}`, {
  method: "DELETE",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
No response bodyThis request does not return any response body.
POST/agents/{agentId}/restore

Undo a soft-delete

https://veytrix-api.fly.dev/api/agents/{agentId}/restore

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}/restore`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Undo a soft-delete."
}
POST/agents/{agentId}/activate

Reactivate a dormant agent

An agent idle past the platform inactivity window stops having its welcome audio kept warm. Activating re-warms it so the next dial is as fast as any other.

https://veytrix-api.fly.dev/api/agents/{agentId}/activate

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}/activate`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Reactivate a dormant agent."
}
POST/agents/{agentId}/deactivate

Stop an agent from being dialed

https://veytrix-api.fly.dev/api/agents/{agentId}/deactivate

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}/deactivate`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Stop an agent from being dialed."
}
POST/agents/{agentId}/test-call

Place a single call to one number

The one-off dial. For more than a handful of numbers use batches, or /alert when they must ring at once.

https://veytrix-api.fly.dev/api/agents/{agentId}/test-call

Body

FieldTypeDescription
phoneNumberrequiredstringE.164, e.g. +919876543210.
variablesobjectValues for the {placeholders} in the welcome message and prompt.

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
  • 409 with the in-flight `callId` when that number already has a call running — dial again once it settles.
  • 403 when the monthly minute quota is spent.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}/test-call`, {
  method: "POST",
  headers,
  body: JSON.stringify({ "phoneNumber": "+919876543210", "variables": { "name": "Priya" } })
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "6f1c2f4a-1c7e-4c9a-9d21-2b6f5f0a1c33",
  "name": "Screening — Backend Engineer",
  "status": "active",
  "conversationEngine": "pipeline",
  "language": "English",
  "llmProvider": "Gemini",
  "llmModel": "gemini-2.5-flash"
}
POST/agents/{agentId}/chat

Exchange text turns with the agent, no phone call

https://veytrix-api.fly.dev/api/agents/{agentId}/chat

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/{agentId}/chat`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Exchange text turns with the agent, no phone call."
}
POST/agents/generate

Draft a whole agent from a plain-language brief

https://veytrix-api.fly.dev/api/agents/generate

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/generate`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Draft a whole agent from a plain-language brief."
}
POST/agents/prompt-assist

Improve an existing prompt

https://veytrix-api.fly.dev/api/agents/prompt-assist

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/agents/prompt-assist`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Improve an existing prompt."
}

Calls

Everything that happened on the phone: status, duration, transcript, summary and recording. This is the group most integrations spend their time in.

GET/calls

List calls, newest first

https://veytrix-api.fly.dev/api/calls

Query parameters

ParameterTypeDescription
pagenumber1-based. Default 1.
limitnumberRows per page.
statusstringComma-separated: completed,failed,no-answer,in-progress,queued.
searchstringMatches name, phone and agent.
deepbooleanAlso search inside transcripts. Costs a scan bounded to the last 90 days, and needs a search term of 3+ characters.
providerstringFilter by telephony provider.

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
  • `status` in the response is the display form ("Completed", "No Answer"), while the `status` query parameter takes the raw form (`completed`, `no-answer`). They are deliberately different — filter with the raw values.
  • `duration` and `startedAt` are pre-formatted for display, not machine values. Use the webhook payload's `durationSeconds` when you need arithmetic.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
      "studentName": "Priya Sharma",
      "phone": "+919876543210",
      "email": null,
      "campaignName": "--",
      "agentName": "Screening — Backend Engineer",
      "duration": "4m 12s",
      "status": "Completed",
      "provider": "plivo",
      "startedAt": "12 minutes ago",
      "summary": "5 years on Node and React. 30-day notice. Open to Hyderabad.",
      "calendarStatus": null,
      "calendarEventUrl": null,
      "calendarError": null,
      "transcript": []
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25,
  "stats": { "total": 1284, "completedCount": 1102, "failedCount": 182 }
}
GET/calls/{callId}

One call, with its full transcript

Same shape as a list row, except `transcript` is populated with every turn in order.

https://veytrix-api.fly.dev/api/calls/{callId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/{callId}`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
  "studentName": "Priya Sharma",
  "status": "Completed",
  "summary": "5 years on Node and React. 30-day notice.",
  "transcript": [
    { "speaker": "Bot", "text": "Hi, this is Maya from Acme. Do you have two minutes?" },
    { "speaker": "Candidate", "text": "Yes, go ahead." }
  ]
}
GET/calls/{callId}/transcript

Transcript only

https://veytrix-api.fly.dev/api/calls/{callId}/transcript

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
  • Turn-level rows are pruned after 90 days. `transcriptText` is denormalised onto the call and is not pruned, so use it for anything you keep.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/{callId}/transcript`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "transcriptText": "Bot: Hi, this is Maya…\nCandidate: Yes, go ahead.",
  "turns": [
    { "speaker": "assistant", "text": "Hi, this is Maya…", "createdAt": "2026-08-15T09:12:04.881Z" }
  ]
}
GET/calls/{callId}/turns

Raw turn rows, unformatted

https://veytrix-api.fly.dev/api/calls/{callId}/turns

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/{callId}/turns`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
      "studentName": "Priya Sharma",
      "phone": "+919876543210",
      "agentName": "Screening — Backend Engineer",
      "duration": "4m 12s",
      "status": "Completed",
      "provider": "plivo",
      "startedAt": "12 minutes ago",
      "summary": "5 years on Node and React. 30-day notice.",
      "transcript": []
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/calls/{callId}/recording-url

Short-lived signed URL for the recording audio

The stored URL points at a private bucket. This returns a pre-signed link your client can fetch directly.

https://veytrix-api.fly.dev/api/calls/{callId}/recording-url

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
  • `url` is null when the call was not recorded, or when its retention window has passed.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/{callId}/recording-url`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{ "url": "https://s3.ap-south-1.amazonaws.com/…?X-Amz-Signature=…", "callId": "b21e0e4c" }
GET/calls/{callId}/recording

Stream the recording audio through the API

https://veytrix-api.fly.dev/api/calls/{callId}/recording

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/{callId}/recording`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
      "studentName": "Priya Sharma",
      "phone": "+919876543210",
      "agentName": "Screening — Backend Engineer",
      "duration": "4m 12s",
      "status": "Completed",
      "provider": "plivo",
      "startedAt": "12 minutes ago",
      "summary": "5 years on Node and React. 30-day notice.",
      "transcript": []
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/calls/export.csv

Download the filtered call list as CSV

https://veytrix-api.fly.dev/api/calls/export.csv

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/export.csv`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Download the filtered call list as CSV."
}
POST/calls/{callId}/cancel

Cancel a queued call, or hang up a live one

https://veytrix-api.fly.dev/api/calls/{callId}/cancel

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/{callId}/cancel`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Cancel a queued call, or hang up a live one."
}
POST/calls/{callId}/redeliver-webhook

Re-send this call's webhook events

For when your endpoint was down. Replays to every active subscription that covers the event.

https://veytrix-api.fly.dev/api/calls/{callId}/redeliver-webhook

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/calls/{callId}/redeliver-webhook`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Re-send this call's webhook events."
}

Batches

Call a list of people with one agent. Three steps by design — create, add items, start — so a half-uploaded list can never start dialing on its own.

POST/batches

Create an empty batch

https://veytrix-api.fly.dev/api/batches

Body

FieldTypeDescription
namerequiredstringHow the batch appears in the dashboard.
agentIdrequiredstringThe agent that places every call in it.
scheduledStartAtstring (ISO 8601)Start automatically at this time instead of on demand.

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches`, {
  method: "POST",
  headers,
  body: JSON.stringify({ "name": "Backend shortlist — week 33", "agentId": "6f1c2f4a" })
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "9d3a71c8-5f22-4b0e-9a7d-1c8e4b2f6a90",
  "name": "Backend shortlist — week 33",
  "agentName": "Screening — Backend Engineer",
  "status": "running",
  "total": 240,
  "completed": 118,
  "failed": 17,
  "pending": 105
}
POST/batches/{batchId}/items

Add people to the batch

Call as many times as you need before starting. Each item carries its own variables, so every call is personalised.

https://veytrix-api.fly.dev/api/batches/{batchId}/items

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}/items`, {
  method: "POST",
  headers,
  body: JSON.stringify({
      "items": [
        { "name": "Priya Sharma", "phone": "+919876543210", "variables": { "role": "Backend Engineer" } },
        { "name": "Arun Nair",    "phone": "+919812345678", "variables": { "role": "Backend Engineer" } }
      ]
    })
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Add people to the batch."
}
POST/batches/{batchId}/start

Begin dialing

Calls are paced by the workspace's concurrency limit, so a 2,000-item batch runs for as long as that limit dictates rather than all at once.

https://veytrix-api.fly.dev/api/batches/{batchId}/start

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}/start`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Begin dialing."
}
POST/batches/{batchId}/pause

Stop dialing; keep the remaining items queued

https://veytrix-api.fly.dev/api/batches/{batchId}/pause

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}/pause`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Stop dialing; keep the remaining items queued."
}
POST/batches/{batchId}/resume

Continue a paused batch

https://veytrix-api.fly.dev/api/batches/{batchId}/resume

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}/resume`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Continue a paused batch."
}
POST/batches/{batchId}/stop

End the batch and drop its pending items

https://veytrix-api.fly.dev/api/batches/{batchId}/stop

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}/stop`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "End the batch and drop its pending items."
}
POST/batches/{batchId}/retry

Re-dial the items that failed or went unanswered

https://veytrix-api.fly.dev/api/batches/{batchId}/retry

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}/retry`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Re-dial the items that failed or went unanswered."
}
GET/batches

List batches with their progress counts

https://veytrix-api.fly.dev/api/batches

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "9d3a71c8-5f22-4b0e-9a7d-1c8e4b2f6a90",
      "name": "Backend shortlist — week 33",
      "agentName": "Screening — Backend Engineer",
      "status": "running",
      "total": 240,
      "completed": 118,
      "failed": 17,
      "pending": 105
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/batches/{batchId}

One batch

https://veytrix-api.fly.dev/api/batches/{batchId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "9d3a71c8-5f22-4b0e-9a7d-1c8e4b2f6a90",
  "name": "Backend shortlist — week 33",
  "agentName": "Screening — Backend Engineer",
  "status": "running",
  "total": 240,
  "completed": 118,
  "failed": 17,
  "pending": 105
}
GET/batches/{batchId}/items

The people in a batch and each one's latest status

https://veytrix-api.fly.dev/api/batches/{batchId}/items

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}/items`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "The people in a batch and each one's latest status."
}
PATCH/batches/{batchId}

Rename or reschedule

https://veytrix-api.fly.dev/api/batches/{batchId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}`, {
  method: "PATCH",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "9d3a71c8-5f22-4b0e-9a7d-1c8e4b2f6a90",
  "name": "Backend shortlist — week 33",
  "agentName": "Screening — Backend Engineer",
  "status": "running",
  "total": 240,
  "completed": 118,
  "failed": 17,
  "pending": 105
}
DELETE/batches/{batchId}

Delete a batch

https://veytrix-api.fly.dev/api/batches/{batchId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/batches/{batchId}`, {
  method: "DELETE",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
No response bodyThis request does not return any response body.

Campaigns

Batches built around a CSV upload, with a students list and per-campaign export. Use these when the list arrives as a file; use batches when it arrives from your own system.

GET/campaigns

List campaigns

https://veytrix-api.fly.dev/api/campaigns

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "3c1f9a76-84be-4f21-b0d5-7ea2c6415d88",
      "name": "March intake",
      "agentName": "Screening — Backend Engineer",
      "status": "running",
      "total": 500,
      "completed": 322,
      "failed": 41,
      "pending": 137
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
POST/campaigns

Create a campaign

https://veytrix-api.fly.dev/api/campaigns

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "3c1f9a76-84be-4f21-b0d5-7ea2c6415d88",
  "name": "March intake",
  "agentName": "Screening — Backend Engineer",
  "status": "running",
  "total": 500,
  "completed": 322,
  "failed": 41,
  "pending": 137
}
GET/campaigns/{campaignId}

One campaign with its progress

https://veytrix-api.fly.dev/api/campaigns/{campaignId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "3c1f9a76-84be-4f21-b0d5-7ea2c6415d88",
  "name": "March intake",
  "agentName": "Screening — Backend Engineer",
  "status": "running",
  "total": 500,
  "completed": 322,
  "failed": 41,
  "pending": 137
}
PATCH/campaigns/{campaignId}

Update a campaign

https://veytrix-api.fly.dev/api/campaigns/{campaignId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}`, {
  method: "PATCH",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "3c1f9a76-84be-4f21-b0d5-7ea2c6415d88",
  "name": "March intake",
  "agentName": "Screening — Backend Engineer",
  "status": "running",
  "total": 500,
  "completed": 322,
  "failed": 41,
  "pending": 137
}
DELETE/campaigns/{campaignId}

Delete a campaign

https://veytrix-api.fly.dev/api/campaigns/{campaignId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}`, {
  method: "DELETE",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
No response bodyThis request does not return any response body.
POST/campaigns/{campaignId}/students/upload

Upload the call list as CSV

multipart/form-data. Columns beyond name and phone become variables available to the prompt.

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/students/upload

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/students/upload`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Upload the call list as CSV."
}
POST/campaigns/{campaignId}/start

Begin dialing

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/start

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/start`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Begin dialing."
}
POST/campaigns/{campaignId}/pause

Pause dialing

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/pause

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/pause`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Pause dialing."
}
POST/campaigns/{campaignId}/resume

Resume dialing

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/resume

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/resume`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Resume dialing."
}
POST/campaigns/{campaignId}/stop

Stop and drop pending calls

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/stop

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/stop`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Stop and drop pending calls."
}
POST/campaigns/{campaignId}/retry

Re-dial failures

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/retry

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/retry`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Re-dial failures."
}
GET/campaigns/{campaignId}/calls

Calls placed by this campaign

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/calls

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/calls`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "3c1f9a76-84be-4f21-b0d5-7ea2c6415d88",
      "name": "March intake",
      "agentName": "Screening — Backend Engineer",
      "status": "running",
      "total": 500,
      "completed": 322,
      "failed": 41,
      "pending": 137
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/campaigns/{campaignId}/students

The uploaded list and each row's status

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/students

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/students`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "3c1f9a76-84be-4f21-b0d5-7ea2c6415d88",
      "name": "March intake",
      "agentName": "Screening — Backend Engineer",
      "status": "running",
      "total": 500,
      "completed": 322,
      "failed": 41,
      "pending": 137
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/campaigns/{campaignId}/export

Results as a downloadable file

https://veytrix-api.fly.dev/api/campaigns/{campaignId}/export

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/campaigns/{campaignId}/export`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "3c1f9a76-84be-4f21-b0d5-7ea2c6415d88",
      "name": "March intake",
      "agentName": "Screening — Backend Engineer",
      "status": "running",
      "total": 500,
      "completed": 322,
      "failed": 41,
      "pending": 137
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}

Ratings

The scorecard produced after each call, using the rating template attached to the agent. Rating runs in the background, so a call reaches `completed` before its rating exists.

GET/ratings

List rated calls

https://veytrix-api.fly.dev/api/ratings

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
      "candidateName": "Priya Sharma",
      "overallRating": 4.2,
      "agentName": "Screening — Backend Engineer",
      "columns": [
        { "name": "Communication", "score": 4, "reason": "Clear, gave concrete examples." },
        { "name": "Relevant experience", "score": 5, "reason": "5 years, matching stack." }
      ]
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/ratings/{callId}

The scorecard for one call

https://veytrix-api.fly.dev/api/ratings/{callId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/{callId}`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
  "candidateName": "Priya Sharma",
  "overallRating": 4.2,
  "agentName": "Screening — Backend Engineer",
  "columns": [
    { "name": "Communication", "score": 4, "reason": "Clear, gave concrete examples." },
    { "name": "Relevant experience", "score": 5, "reason": "5 years, matching stack." }
  ]
}
GET/ratings/by-phone/{phone}

Every rating for a phone number

Useful when the same person has been called more than once and you want the history in one request.

https://veytrix-api.fly.dev/api/ratings/by-phone/{phone}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/by-phone/{phone}`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
  "candidateName": "Priya Sharma",
  "overallRating": 4.2,
  "agentName": "Screening — Backend Engineer",
  "columns": [
    { "name": "Communication", "score": 4, "reason": "Clear, gave concrete examples." },
    { "name": "Relevant experience", "score": 5, "reason": "5 years, matching stack." }
  ]
}
GET/ratings/stats

Counts and averages across the workspace

https://veytrix-api.fly.dev/api/ratings/stats

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/stats`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
      "candidateName": "Priya Sharma",
      "overallRating": 4.2,
      "agentName": "Screening — Backend Engineer",
      "columns": [
        { "name": "Communication", "score": 4, "reason": "Clear, gave concrete examples." },
        { "name": "Relevant experience", "score": 5, "reason": "5 years, matching stack." }
      ]
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/ratings/analytics

Score distribution, broken down by column

https://veytrix-api.fly.dev/api/ratings/analytics

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/analytics`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
      "candidateName": "Priya Sharma",
      "overallRating": 4.2,
      "agentName": "Screening — Backend Engineer",
      "columns": [
        { "name": "Communication", "score": 4, "reason": "Clear, gave concrete examples." },
        { "name": "Relevant experience", "score": 5, "reason": "5 years, matching stack." }
      ]
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/ratings/export-all

Every rating as a downloadable file

https://veytrix-api.fly.dev/api/ratings/export-all

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/export-all`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
      "candidateName": "Priya Sharma",
      "overallRating": 4.2,
      "agentName": "Screening — Backend Engineer",
      "columns": [
        { "name": "Communication", "score": 4, "reason": "Clear, gave concrete examples." },
        { "name": "Relevant experience", "score": 5, "reason": "5 years, matching stack." }
      ]
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
POST/ratings/export-sheet

Push ratings into a Google Sheet

https://veytrix-api.fly.dev/api/ratings/export-sheet

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/export-sheet`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Push ratings into a Google Sheet."
}
POST/ratings/{callId}/rate-now

Rate a call immediately instead of waiting for the queue

https://veytrix-api.fly.dev/api/ratings/{callId}/rate-now

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/{callId}/rate-now`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Rate a call immediately instead of waiting for the queue."
}
POST/ratings/{callId}/regenerate

Score the call again

Use after editing the template — the existing scorecard is replaced.

https://veytrix-api.fly.dev/api/ratings/{callId}/regenerate

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/{callId}/regenerate`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Score the call again."
}
POST/ratings/bulk-regenerate

Re-score many calls at once

https://veytrix-api.fly.dev/api/ratings/bulk-regenerate

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/bulk-regenerate`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Re-score many calls at once."
}
POST/ratings/reload

Re-read templates and refresh pending ratings

https://veytrix-api.fly.dev/api/ratings/reload

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/ratings/reload`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Re-read templates and refresh pending ratings."
}

Phone numbers

The caller IDs your workspace owns. A number is rented monthly; the first month is paid online and every renewal is taken from the wallet.

GET/numbers

List the numbers this workspace owns

https://veytrix-api.fly.dev/api/numbers

Query parameters

ParameterTypeDescription
for"inbound"Only numbers that can receive calls.
agentIdstringWith for=inbound, also include the number already assigned to this agent.

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "7b2e5c19-40aa-4d63-8f11-2c9d7e0a5b34",
      "phoneNumber": "+918047182290",
      "label": "Main line",
      "provider": "plivo",
      "isActive": true,
      "isDefaultOutbound": true,
      "assignedAgentId": null,
      "nextRenewalAt": "2026-09-15T00:00:00.000Z"
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/numbers/available-pool

Numbers available to rent

https://veytrix-api.fly.dev/api/numbers/available-pool

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers/available-pool`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "7b2e5c19-40aa-4d63-8f11-2c9d7e0a5b34",
      "phoneNumber": "+918047182290",
      "label": "Main line",
      "provider": "plivo",
      "isActive": true,
      "isDefaultOutbound": true,
      "assignedAgentId": null,
      "nextRenewalAt": "2026-09-15T00:00:00.000Z"
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
GET/numbers/renewals-due

Numbers renewing soon, with their dates

https://veytrix-api.fly.dev/api/numbers/renewals-due

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers/renewals-due`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "rows": [
    {
      "id": "7b2e5c19-40aa-4d63-8f11-2c9d7e0a5b34",
      "phoneNumber": "+918047182290",
      "label": "Main line",
      "provider": "plivo",
      "isActive": true,
      "isDefaultOutbound": true,
      "assignedAgentId": null,
      "nextRenewalAt": "2026-09-15T00:00:00.000Z"
    }
  ],
  "total": 1284,
  "page": 1,
  "limit": 25
}
POST/numbers

Register a number against the workspace

https://veytrix-api.fly.dev/api/numbers

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "7b2e5c19-40aa-4d63-8f11-2c9d7e0a5b34",
  "phoneNumber": "+918047182290",
  "label": "Main line",
  "provider": "plivo",
  "isActive": true,
  "isDefaultOutbound": true,
  "assignedAgentId": null,
  "nextRenewalAt": "2026-09-15T00:00:00.000Z"
}
PATCH/numbers/{numberId}

Set the label, the inbound agent, or the default caller ID

https://veytrix-api.fly.dev/api/numbers/{numberId}

Body

FieldTypeDescription
labelstringFree text, e.g. "Main line".
assignedAgentIdstring | nullThe agent that answers inbound calls on this number.
isDefaultOutboundbooleanMake this the caller ID for the workspace's outbound calls.
isActivebooleanWhether the number is usable.

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
  • An inbound number rings exactly one agent. Assigning it to a second agent moves it.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers/{numberId}`, {
  method: "PATCH",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "id": "7b2e5c19-40aa-4d63-8f11-2c9d7e0a5b34",
  "phoneNumber": "+918047182290",
  "label": "Main line",
  "provider": "plivo",
  "isActive": true,
  "isDefaultOutbound": true,
  "assignedAgentId": null,
  "nextRenewalAt": "2026-09-15T00:00:00.000Z"
}
POST/numbers/{numberId}/renew

Renew now, charged to the wallet

https://veytrix-api.fly.dev/api/numbers/{numberId}/renew

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers/{numberId}/renew`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Renew now, charged to the wallet."
}
POST/numbers/{numberId}/release

Return the number to the shared pool

https://veytrix-api.fly.dev/api/numbers/{numberId}/release

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
  • Irreversible. The number becomes available to other workspaces and cannot be reclaimed.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers/{numberId}/release`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Return the number to the shared pool."
}
DELETE/numbers/{numberId}

Remove a number record

https://veytrix-api.fly.dev/api/numbers/{numberId}

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/numbers/{numberId}`, {
  method: "DELETE",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
No response bodyThis request does not return any response body.

Wallet & usage

Prepaid balance, what has been spent, and what is left. Poll this if you want to stop your own dialing before the wallet runs dry.

GET/wallet

Balance, spend and usage for the current month

https://veytrix-api.fly.dev/api/wallet

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
  • `lowBalance` turns true when the balance buys fewer than about 30 two-way minutes. It is the field to alert on.
  • `ratePerMin` follows the plan the balance unlocks, so it changes as the wallet is topped up.
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/wallet`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "currency": "INR",
  "ratePerMin": 4.5,
  "walletCredits": 20000,
  "amountPaid": 20000,
  "spent": 8420.5,
  "balance": 11579.5,
  "minutesUsed": 1871,
  "minutesLimit": 4444,
  "projectedDaysRemaining": 19,
  "lowBalance": false,
  "monthStart": "2026-08-01T00:00:00.000Z"
}
GET/wallet/transactions

Ledger entries, newest first

https://veytrix-api.fly.dev/api/wallet/transactions

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/wallet/transactions`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "currency": "INR",
  "ratePerMin": 4.5,
  "walletCredits": 20000,
  "balance": 11579.5,
  "minutesUsed": 1871,
  "lowBalance": false
}
GET/wallet/transactions/export.csv

The ledger as CSV

https://veytrix-api.fly.dev/api/wallet/transactions/export.csv

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/wallet/transactions/export.csv`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "The ledger as CSV."
}
GET/wallet/usage

Usage broken down by agent and campaign

https://veytrix-api.fly.dev/api/wallet/usage

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/wallet/usage`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "currency": "INR",
  "ratePerMin": 4.5,
  "walletCredits": 20000,
  "balance": 11579.5,
  "minutesUsed": 1871,
  "lowBalance": false
}
GET/wallet/auto-recharge

Current auto-recharge settings

https://veytrix-api.fly.dev/api/wallet/auto-recharge

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/wallet/auto-recharge`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "currency": "INR",
  "ratePerMin": 4.5,
  "walletCredits": 20000,
  "balance": 11579.5,
  "minutesUsed": 1871,
  "lowBalance": false
}
PATCH/wallet/auto-recharge

Change the threshold, amount or daily cap

https://veytrix-api.fly.dev/api/wallet/auto-recharge

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/wallet/auto-recharge`, {
  method: "PATCH",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "currency": "INR",
  "ratePerMin": 4.5,
  "walletCredits": 20000,
  "balance": 11579.5,
  "minutesUsed": 1871,
  "lowBalance": false
}
POST/wallet/fund-request

Request a manual top-up

https://veytrix-api.fly.dev/api/wallet/fund-request

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEYContent-Typeapplication/json
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/wallet/fund-request`, {
  method: "POST",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "message": "Request a manual top-up."
}
GET/balances

Upstream provider credit, when the workspace uses its own keys

https://veytrix-api.fly.dev/api/balances

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/balances`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "currency": "INR",
  "ratePerMin": 4.5,
  "walletCredits": 20000,
  "balance": 11579.5,
  "minutesUsed": 1871,
  "lowBalance": false
}

Dashboard & analytics

The same aggregates the dashboard renders, for when you want them in your own reporting.

GET/dashboard/overview

Headline counters

https://veytrix-api.fly.dev/api/dashboard/overview

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/dashboard/overview`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "totalCalls": 1284,
  "completedCalls": 1102,
  "failedCalls": 182,
  "totalMinutes": 5136,
  "averageDurationSeconds": 240
}
GET/dashboard/analytics

Per-agent performance

https://veytrix-api.fly.dev/api/dashboard/analytics

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/dashboard/analytics`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "totalCalls": 1284,
  "completedCalls": 1102,
  "failedCalls": 182,
  "totalMinutes": 5136,
  "averageDurationSeconds": 240
}
GET/dashboard/call-volume

Calls over time, for charting

https://veytrix-api.fly.dev/api/dashboard/call-volume

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/dashboard/call-volume`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "totalCalls": 1284,
  "completedCalls": 1102,
  "failedCalls": 182,
  "totalMinutes": 5136,
  "averageDurationSeconds": 240
}
GET/dashboard/status-breakdown

How calls ended, grouped by status

https://veytrix-api.fly.dev/api/dashboard/status-breakdown

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/dashboard/status-breakdown`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "totalCalls": 1284,
  "completedCalls": 1102,
  "failedCalls": 182,
  "totalMinutes": 5136,
  "averageDurationSeconds": 240
}
GET/dashboard/recent-calls

The latest calls, pre-trimmed for a feed

https://veytrix-api.fly.dev/api/dashboard/recent-calls

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/dashboard/recent-calls`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "totalCalls": 1284,
  "completedCalls": 1102,
  "failedCalls": 182,
  "totalMinutes": 5136,
  "averageDurationSeconds": 240
}
GET/analytics/overview

Funnel, engagement buckets and cost in one payload

https://veytrix-api.fly.dev/api/analytics/overview

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/analytics/overview`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "totalCalls": 1284,
  "completedCalls": 1102,
  "failedCalls": 182,
  "totalMinutes": 5136,
  "averageDurationSeconds": 240
}
GET/recordings

Recordings across the workspace, with playback URLs

https://veytrix-api.fly.dev/api/recordings

Authorization

TypeBearer tokenTokenacai_YOUR_KEY

Headers

AuthorizationBearer acai_YOUR_KEY
Example request
// Node 18+ — fetch is built in
const BASE = "https://veytrix-api.fly.dev/api";
const headers = {
  Authorization: "Bearer acai_YOUR_KEY",
  "Content-Type": "application/json"
};

const res = await fetch(`${BASE}/recordings`, {
  method: "GET",
  headers
});

if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());
Example response
200 OK
{
  "totalCalls": 1284,
  "completedCalls": 1102,
  "failedCalls": 182,
  "totalMinutes": 5136,
  "averageDurationSeconds": 240
}

Webhooks

Rather than polling /calls, let Veytrix tell you. Register an HTTPS endpoint in Settings → Developer, choose the events, and every matching call POSTs a signed JSON body to you.

The envelope

Every delivery has the same outer shape; only data differs by event.

Envelope
POST https://your-app.example.com/veytrix
Content-Type: application/json
X-Veytrix-Event: call.completed
X-Veytrix-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b…

{
  "event": "call.completed",
  "organizationId": "a17f…",
  "data": { },
  "sentAt": "2026-08-15T09:16:31.204Z"
}

Verifying the signature

The signature is HMAC-SHA256 over the exact raw body, keyed by the subscription secret (it begins whsec_). Compare with a timing-safe equality check, and verify before you parse — re-serialising the JSON first changes the bytes and the signature will never match.

Node.js · Express
import crypto from "node:crypto";
import express from "express";

const app = express();

// express.raw, NOT express.json — the signature covers the exact bytes.
app.post("/veytrix", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.header("X-Veytrix-Signature") ?? "";
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", process.env.VEYTRIX_WEBHOOK_SECRET)
      .update(req.body)
      .digest("hex");

  const ok =
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!ok) return res.status(401).send("bad signature");

  const { event, data } = JSON.parse(req.body.toString("utf8"));
  // Acknowledge fast, then do the work out of band — deliveries time out at 10s.
  res.sendStatus(200);
  void handle(event, data);
});
Python · Flask
import hmac, hashlib, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["VEYTRIX_WEBHOOK_SECRET"].encode()

@app.post("/veytrix")
def veytrix():
    raw = request.get_data()  # bytes, before any parsing
    expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(request.headers.get("X-Veytrix-Signature", ""), expected):
        abort(401)

    payload = request.get_json()
    handle(payload["event"], payload["data"])
    return "", 200

Events

Three events are delivered to subscriptions. Each fires once per call; rating.created always trails its call's own event, sometimes by a minute or more, because scoring runs in the background.

call.completed

A call ended with a real conversation. Fires once the transcript and summary are written.

Payload
{
  "event": "call.completed",
  "organizationId": "a17f…",
  "data": {
    "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
    "status": "completed",
    "durationSeconds": 252,
    "targetName": "Priya Sharma",
    "targetPhone": "+919876543210",
    "summaryText": "5 years on Node and React. 30-day notice."
  },
  "sentAt": "2026-08-15T09:16:31.204Z"
}
call.failed

The call did not connect, or nobody answered. Same payload shape, with status `failed` or `no-answer`.

Payload
{
  "event": "call.failed",
  "organizationId": "a17f…",
  "data": {
    "callId": "c9a7d1b2-33f8-42aa-8e10-7c5b9e2f4a18",
    "status": "no-answer",
    "durationSeconds": 0,
    "targetName": "Arun Nair",
    "targetPhone": "+919812345678",
    "summaryText": null
  },
  "sentAt": "2026-08-15T09:17:02.771Z"
}
rating.created

The scorecard for a call is ready. Always arrives after that call's own event, sometimes by a minute or more.

Payload
{
  "event": "rating.created",
  "organizationId": "a17f…",
  "data": {
    "callId": "b21e0e4c-70f2-4f5f-9a1e-4d2f1a6c9b77",
    "candidateName": "Priya Sharma",
    "overallRating": 4.2,
    "agentName": "Screening — Backend Engineer"
  },
  "sentAt": "2026-08-15T09:18:44.019Z"
}

Delivery behaviour

  • Each delivery times out after 10 seconds. Return 200 immediately and do your work afterwards.
  • A non-2xx response is logged but not automatically retried. To replay a specific call, use POST /calls/{callId}/redeliver-webhook.
  • The target must be a public HTTPS address. URLs that resolve to private or internal ranges are refused at delivery time, not just at registration — so a DNS change cannot turn your endpoint into an internal one.
  • Deliveries are not ordered. Key your handler on callId and make it idempotent.

Common integrations

Four things people build first, end to end.

Call a lead the moment it arrives

Speed to first contact is the whole game in inbound sales. Call your form handler's webhook straight into a dial — the lead's phone rings while they are still on the thank-you page.

On form submit
await fetch("https://veytrix-api.fly.dev/api/agents/AGENT_ID/test-call", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.VEYTRIX_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    phoneNumber: lead.phone,            // +919876543210
    variables: { name: lead.firstName, product: lead.interestedIn }
  })
});

Then listen for rating.created and write overallRating back onto the lead — your reps work the list in score order instead of arrival order.

Screen a shortlist overnight

Create the batch, push everyone in, start it. Each person gets their own variables, so the agent opens with their name and the role they applied for.

Three requests
const auth = { "Authorization": `Bearer ${KEY}`, "Content-Type": "application/json" };

// 1 · create
const batch = await (await fetch("https://veytrix-api.fly.dev/api/batches", {
  method: "POST", headers: auth,
  body: JSON.stringify({ name: "Backend shortlist", agentId: AGENT_ID })
})).json();

// 2 · fill
await fetch(`https://veytrix-api.fly.dev/api/batches/${batch.id}/items`, {
  method: "POST", headers: auth,
  body: JSON.stringify({
    items: candidates.map(c => ({
      name: c.name, phone: c.phone, variables: { role: c.role }
    }))
  })
});

// 3 · dial
await fetch(`https://veytrix-api.fly.dev/api/batches/${batch.id}/start`, { method: "POST", headers: auth });

Pass scheduledStartAt at step 1 instead of calling start, and the batch begins on its own at that time.

Ring every on-call engineer at once

When something is on fire, a batch is the wrong shape — it paces calls. /alert rings all of them in parallel from a single request, and returns before they connect.

From your monitoring hook
await fetch("https://veytrix-api.fly.dev/api/alert", {
  method: "POST",
  headers: { "Authorization": `Bearer ${KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    agentId: ANNOUNCEMENT_AGENT_ID,
    phones: onCall.map(p => p.phone),
    variables: { service: incident.service, severity: incident.severity }
  })
});

Use an announcement agent here: it plays one message and hangs up, costs no LLM time, and its audio is cached — so the message starts almost the instant the call connects.

Sync every outcome into your CRM

Subscribe to call.completed and rating.created, verify the signature, and upsert on callId. Two events per call arrive separately, so write them independently rather than waiting for both.

Handler
async function handle(event, data) {
  if (event === "call.completed") {
    await crm.upsertActivity({
      externalId: data.callId,
      phone: data.targetPhone,
      durationSeconds: data.durationSeconds,
      notes: data.summaryText
    });
  }

  if (event === "rating.created") {
    await crm.updateActivity(data.callId, { score: data.overallRating });
  }

  // call.failed carries status "failed" or "no-answer" — worth a retry task.
}

Need the audio too? Call GET /calls/{callId}/recording-url when the event arrives and store the file yourself — the signed URL is short-lived by design.