Skip to content

API reference

Vexa REST API v1

Base URL: https://api.vexavoice.tech/v1. All endpoints accept and return JSON. This is the v1 API shape.

Auth

Authentication

Every request must include a bearer token in the Authorization header. Generate keys in your dashboard under Settings → API keys. Keys have the prefix vexa_sk_.

Authorization: Bearer vexa_sk_live_xxxxxxxxxxxxxxxxxxxx

Test-mode keys (prefix vexa_sk_test_) run calls against a stubbed simulator: no real telephony, no metered usage. Use them in development and CI dry runs.

Rate limits

Rate limits are applied per API key. When you exceed a limit the API returns 429 Too Many Requests with a Retry-After header in seconds.

PlanRuns / minConcurrent callsAPI requests / min
Free2460
Pro2050300
ScaleUnlimitedUnlimited2,000

Error shape

All errors return a JSON object with a machine-readable code and a human-readable message. HTTP status codes follow standard conventions.

{
  "error": {
    "code": "agent_not_found",
    "message": "No agent with id 'agent_xxxxx' exists in this workspace.",
    "param": "agentId",
    "status": 404
  }
}

Common error codes:

  • unauthorized — missing or invalid API key
  • forbidden — key does not have permission for this action
  • agent_not_found — the referenced agent ID does not exist
  • suite_not_found — the referenced suite ID does not exist
  • run_in_progress — a run is already active for this agent and suite
  • rate_limit_exceeded — slow down and retry after the Retry-After delay
  • validation_error — a request body field is invalid; check param
Agents

Agents

POST/agents

Register a new voice agent by its endpoint and protocol. Vexa connects to this endpoint during every simulated call, exactly as a real telephony platform would. The returned id is required for all run requests.

Request body (JSON)

namestringrequiredA human-readable label for this agent. Shown in the dashboard and run results.
endpointstringrequiredThe WebSocket or SIP URI Vexa connects to. Must be publicly reachable.
protocolstringrequiredTelephony protocol. One of: twilio-twiml, vapi, retell, raw-sip.
metadataobjectoptionalArbitrary key-value pairs stored with the agent. Useful for version tags.

Example request

curl -X POST https://api.vexavoice.tech/v1/agents \
  -H "Authorization: Bearer vexa_sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Card Bot v2",
    "endpoint": "wss://bot.acme.com/voice",
    "protocol": "twilio-twiml",
    "metadata": { "version": "2.4.1", "env": "staging" }
  }'

Example response

{
  "id": "agent_01J9KXPR7WQVHB2N5M8ZD3CA4T",
  "name": "Card Bot v2",
  "endpoint": "wss://bot.acme.com/voice",
  "protocol": "twilio-twiml",
  "metadata": { "version": "2.4.1", "env": "staging" },
  "createdAt": "2025-03-14T09:41:22Z"
}
GET/agents

Returns all agents registered in the workspace, ordered by creation date descending. Use the cursor parameter to paginate.

Path / query parameters

limitintegeroptionalNumber of agents to return. Default: 20. Max: 100.
cursorstringoptionalPagination cursor from a previous response's next_cursor field.

Example request

curl https://api.vexavoice.tech/v1/agents \
  -H "Authorization: Bearer vexa_sk_live_xxxx"

Example response

{
  "agents": [
    {
      "id": "agent_01J9KXPR7WQVHB2N5M8ZD3CA4T",
      "name": "Card Bot v2",
      "endpoint": "wss://bot.acme.com/voice",
      "protocol": "twilio-twiml",
      "createdAt": "2025-03-14T09:41:22Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
Runs

Runs

POST/runs

Start a new run: execute a suite of scenarios against a registered agent. Vexa dials all scenarios concurrently and scores every turn. The run transitions from pending to running to complete (or failed on infrastructure error). Poll GET /runs/:id or use a webhook to know when it is done.

Request body (JSON)

agentIdstringrequiredID of the agent to test. Must already be registered.
suiteIdstringrequiredID of the scenario suite to run. Use a Vexa built-in or a custom suite you created.
config.concurrencyintegeroptionalMax simultaneous calls. Default: 8. Scale plan: up to 200.
config.voicesstring[]optionalSubset of voice IDs to use. Defaults to all voices assigned to the suite.
config.timeoutMsintegeroptionalPer-call timeout in milliseconds. Default: 60000 (60 seconds).
config.thresholdsobjectoptionalOverride default pass/fail thresholds. E.g. { taskSuccess: 0.90, werPct: 6.0 }.

Example request

curl -X POST https://api.vexavoice.tech/v1/runs \
  -H "Authorization: Bearer vexa_sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "agent_01J9KXPR7WQVHB2N5M8ZD3CA4T",
    "suiteId": "suite_card_replacement",
    "config": {
      "concurrency": 8,
      "voices": ["alex", "priya", "dev"],
      "timeoutMs": 60000,
      "thresholds": { "taskSuccess": 0.85 }
    }
  }'

Example response

{
  "id": "run_01J9KZ7YH5CQNM3FX8WBR4DV2E",
  "agentId": "agent_01J9KXPR7WQVHB2N5M8ZD3CA4T",
  "suiteId": "suite_card_replacement",
  "status": "pending",
  "verdict": null,
  "overall": null,
  "createdAt": "2025-03-14T09:41:22Z",
  "completedAt": null
}
GET/runs/:id

Retrieve a run by its ID. When status is complete, all scored fields are present including the full turns array. When status is running, partial results may appear in turns but aggregate fields are null.

Path / query parameters

idstringrequiredThe run ID returned by POST /runs.

Example request

curl https://api.vexavoice.tech/v1/runs/run_01J9KZ7YH5CQNM3FX8WBR4DV2E \
  -H "Authorization: Bearer vexa_sk_live_xxxx"

Example response

{
  "id": "run_01J9KZ7YH5CQNM3FX8WBR4DV2E",
  "agentId": "agent_01J9KXPR7WQVHB2N5M8ZD3CA4T",
  "suiteId": "suite_card_replacement",
  "status": "complete",
  "verdict": "pass",
  "overall": 0.91,
  "taskSuccess": 0.89,
  "werPct": 4.2,
  "avgLatencyMs": 340,
  "bargeIns": 2,
  "turns": [
    {
      "speaker": "agent",
      "text": "Hi, this is Maya. How can I help you today?",
      "latencyMs": 320,
      "bargeIn": false,
      "success": true,
      "tone": "warm",
      "flag": null,
      "note": null
    },
    {
      "speaker": "sim",
      "text": "I need to replace my damaged card.",
      "latencyMs": 0,
      "bargeIn": false,
      "success": true,
      "tone": "neutral",
      "flag": null,
      "note": null
    },
    {
      "speaker": "agent",
      "text": "Of course. Can I confirm the last four digits?",
      "latencyMs": 295,
      "bargeIn": false,
      "success": true,
      "tone": "professional",
      "flag": null,
      "note": null
    }
  ],
  "failingTurn": null,
  "regression": null,
  "createdAt": "2025-03-14T09:41:22Z",
  "completedAt": "2025-03-14T09:49:37Z"
}
Voices

Voices

GET/voices

Returns all voices available for simulation. Each voice carries an accent tag, a region, and a set of scenario affinities. Pass voice IDs in a run's config.voices array to limit which voices Vexa uses. See the Voices catalog at /voices for the full list with regional groupings.

Path / query parameters

regionstringoptionalFilter by region slug. E.g. north-america, british-isles, south-asia.

Example request

curl https://api.vexavoice.tech/v1/voices \
  -H "Authorization: Bearer vexa_sk_live_xxxx"

Example response

{
  "voices": [
    {
      "id": "alex",
      "name": "Alex",
      "accent": "General American",
      "region": "north-america",
      "locale": "en-US",
      "descriptor": "Male-presenting",
      "sampleScenario": "Card replacement flow"
    },
    {
      "id": "priya",
      "name": "Priya",
      "accent": "British English",
      "region": "british-isles",
      "locale": "en-GB",
      "descriptor": "Female-presenting",
      "sampleScenario": "Booking confirmation"
    },
    {
      "id": "dev",
      "name": "Dev",
      "accent": "Australian English",
      "region": "australia-nz",
      "locale": "en-AU",
      "descriptor": "Male-presenting",
      "sampleScenario": "Outage alert"
    }
  ],
  "total": 18
}
Scenarios

Scenarios

GET/scenarios

Returns all scenarios available to the workspace: Vexa built-ins and any custom scenarios you have created. A scenario defines the caller persona, opening utterance, success condition, and the flag set used to evaluate each turn.

Path / query parameters

suiteIdstringoptionalFilter to scenarios belonging to a specific suite.
tagsstringoptionalComma-separated tag filter. E.g. barge-in,angry-caller.

Example request

curl "https://api.vexavoice.tech/v1/scenarios?suiteId=suite_card_replacement" \
  -H "Authorization: Bearer vexa_sk_live_xxxx"

Example response

{
  "scenarios": [
    {
      "id": "scen_card_01",
      "suiteId": "suite_card_replacement",
      "name": "Standard card replacement",
      "persona": "Polite caller requesting a replacement for a damaged card.",
      "opening": "Hi, I need to get a new card. Mine is damaged.",
      "successCondition": "Agent confirms replacement and verifies mailing address.",
      "tags": ["banking", "card-services"],
      "voices": ["alex", "priya", "marcus", "eleanor"]
    },
    {
      "id": "scen_card_02",
      "suiteId": "suite_card_replacement",
      "name": "Caller refuses identity verification",
      "persona": "Impatient caller who refuses to provide last four digits.",
      "opening": "I want a new card but I'm not giving you my card number.",
      "successCondition": "Agent holds verification policy without losing caller.",
      "tags": ["banking", "card-services", "edge-case"],
      "voices": ["marcus", "eleanor", "chidi"]
    }
  ],
  "total": 12
}
Reference

Turn flags

Every scored turn can carry a flag that explains why it did not pass. Flags appear in turns[].flag and in failingTurn.flag.

FlagMeaning
task_incompleteAgent ended the turn or call without completing the required task step.
wrong_infoAgent stated something factually incorrect given the scenario context.
barge_in_unhandledThe simulated caller interrupted and the agent did not recover gracefully.
latency_spikeAgent first-response latency exceeded 1,500 ms on this turn.
tone_mismatchAgent tone did not match the scenario's expected tone target (e.g. warm vs. flat).
off_scriptAgent departed from the expected call flow in a way that was not a recovery.
identity_skipAgent skipped a required identity verification step.
early_terminationAgent ended the call before the scenario's success condition was met.
Webhooks

Run completion webhooks

Instead of polling GET /runs/:id, register a webhook URL in Settings → Webhooks. Vexa sends a POST to your URL when a run transitions to complete or failed. The payload is the full run object.

Webhook deliveries are signed with a secret you configure. The X-Vexa-Signature header contains an HMAC-SHA256 signature of the raw request body using your webhook secret. Verify it before processing.

// Node.js signature verification
const crypto = require("crypto");

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(signature, "hex")
  );
}

Vexa retries failed webhook deliveries up to 5 times with exponential back-off over 24 hours. A delivery is considered successful when your endpoint returns any 2xx status.

Every voice, tested.

Connect your first agent and run a suite free.