Getting started

Quickstart

Five requests: a key, a list, some contacts, a call, and the transcript it produced.

Before you start

You need an API key and a phone number on the account. Everything else below is created as you go.

Base URLhttps://vocalis.io/api/v1

Node.js and TypeScript

In Node.js, Bun, Deno or an edge runtime, use the TypeScript SDK. It types every request and response, retries safely and follows calls live. The cURL walkthrough below makes the same requests by hand.

npm install @pendral/vocalis
import Vocalis from "@pendral/vocalis";
const vocalis = new Vocalis(); // reads VOCALIS_API_KEY
const call = await vocalis.campaigns.trigger("cmp_...", {
phoneNumber: "+14155551234",
calleeName: "Dr. Jane Smith",
variables: { memberId: "M-10293" },
});
console.log(call.callId, call.status); // "queued"

Then read the conversation back once it ends:

const transcript = await vocalis.calls.transcript(callId);
const details = await vocalis.calls.get(callId);

The walkthrough

  1. Create a key

    Keys live under Settings → API Keys. A key belongs to the organization you were in when you made it and reads nothing outside it. Keep it in an environment variable. It is a password, not an identifier.

    export VOCALIS_API_KEY="dk_..."
  2. Check that it works

    The cheapest possible request. A 200 here means the key is valid and scoped to read calls.

    curl "https://vocalis.io/api/v1/calls?pageSize=1" \
    -H "Authorization: Bearer $VOCALIS_API_KEY"
  3. Create a contact list

    A list carries a schema, and the schema is what a campaign can say out loud: every field you declare here is available to the agent as a variable when it calls someone on the list.

    curl -X POST "https://vocalis.io/api/v1/contact-lists" \
    -H "Authorization: Bearer $VOCALIS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "name": "Q1 renewals",
    "schema": {
    "fields": [
    { "fieldName": "memberId", "displayName": "Member ID", "dataType": "string", "isRequired": true },
    { "fieldName": "renewalDate", "displayName": "Renewal date", "dataType": "date" }
    ]
    }
    }'
  4. Add contacts

    Send the rows inline, or a URL to a JSON or CSV file if there are more than a few thousand. Either way the response is an import job rather than the contacts themselves.

    curl -X POST "https://vocalis.io/api/v1/contact-lists/{listId}/contacts" \
    -H "Authorization: Bearer $VOCALIS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "contacts": [
    { "phoneNumber": "+14155551234", "fullName": "John Doe", "data": { "memberId": "M-10293", "renewalDate": "2026-04-01" } },
    { "phoneNumber": "+14155555678", "fullName": "Jane Smith", "data": { "memberId": "M-10294", "renewalDate": "2026-04-18" } }
    ]
    }'

    Poll the job until it settles. Rows that fail validation are reported individually; the rest still land.

    GET /api/v1/import-jobs/{jobId}
    {
    "id": "job_abc123",
    "status": "completed",
    "progress": { "total": 100, "processed": 100, "successful": 98, "failed": 2 },
    "completedAt": "2026-03-04T10:30:45Z"
    }
  5. Read what was said

    Once the campaign runs, each conversation is a call. Analysis is not immediate: a call ends, then the recording is fetched, transcribed and analysed, so check isAnalyzed before reading analysis.

    curl "https://vocalis.io/api/v1/calls/{callId}" \
    -H "Authorization: Bearer $VOCALIS_API_KEY"

    Do not poll for this

    Subscribe to call.completed instead and you will be told the moment the analysis lands. Webhooks.

Where to go next