API
The REST API gives your own systems access to your business's conversations, leads, contacts and appointments. Use it to pull conversations into a CRM, start outbound calls from a job-planning tool, or sync contacts.
Authentication
Create an API key
- Go to Settings → Connections (Admin role required).
- Under API keys, enter a name (for example
Make) and click Create. - Copy the key immediately. It starts with
pk_and is shown only once; after that the dashboard shows only the first 10 characters, so you can recognise it later.
Delete a key from the same page when it is no longer needed. You can have up to 200 keys. There are no per-key permissions: every key can do everything this API can do for your business, so store it like a password. Creating or deleting a key only works within 2 hours after you sign in, and only from your own login, see Changes that ask you to sign in again. The keys stop working when your subscription is unpaid, suspended or cancelled.
On an Enterprise plan you can limit keys to certain IP addresses: under Settings → Access management, in Security rules, enter the allowed IP ranges and turn on Also limit API keys to these IP ranges. A request from another address then gets a 403.
Make requests
Send the key as a bearer token. The base URL is your API domain (visible under Settings → Connections, in API keys).
curl https://<api-domain>/v1/me \
-H "Authorization: Bearer pk_..."Requests and responses are JSON. A successful request always returns status 200, including POST. Phone numbers are in E.164 format (+31612345678). Timestamps are ISO 8601 in UTC.
Rate limit
Each API key may make 600 requests per minute; above that you get 429 with code ERR-50022. All API customers together also share a platform limit of 50 requests per second, with bursts up to 100. Poll no more often than once a minute; for real-time data use webhooks instead.
Errors
Errors return an HTTP status and a JSON body:
{ "error": "lead not found", "code": "ERR-56004", "errorId": "…" }error is an English description and code is the error code. errorId identifies this one error; quote it when you report a problem. Some errors also have a details field.
| Status | code | Meaning |
|---|---|---|
400 | ERR-50020, ERR-50023–ERR-50028, ERR-50010 | Invalid input. The code names the first problem: ERR-50023 text too long and ERR-50028 too short (details.max / details.min), ERR-50024 too many items in a list, ERR-50025 a required field empty or missing, ERR-50026 an invalid email address, ERR-50027 an invalid web address, and ERR-50020 anything else. details.field and error name the field. ERR-50010 is invalid JSON. Some endpoints have their own code, listed with the endpoint below. |
401 | ERR-51004, ERR-51005 | Missing or invalid API key. |
402 | ERR-52250, ERR-56007 | The subscription is unpaid, suspended or cancelled (ERR-52250, on every request). Outbound calling is not in the plan (ERR-56007, only POST /v1/calls). |
403 | ERR-52056, ERR-56008 | Not allowed: the IP address is not on your business's list (ERR-52056), or outbound calling is blocked (ERR-56008). |
404 | ERR-56000, ERR-56002, ERR-56004, ERR-56006, ERR-51044 | Object not found. A path that does not exist returns 404 without a code. |
413 | ERR-50021 | The body is larger than 8 MB. |
429 | ERR-50022 | Too many requests with this key. A 429 without code means the shared platform limit was reached. Retry after a short wait. |
500 | ERR-50000 | Unexpected error; retry later. |
Endpoints
GET /v1/me
Returns the business the key belongs to.
{
"business": { "id": "…", "name": "Pipes & Co", "slug": "pipes-co-1a2b", "timezone": "Europe/Amsterdam" },
"apiKey": { "id": "…", "name": "Make" }
}GET /v1/assistants
Array of assistants: id, name, language.
[{ "id": "01J…", "name": "Sam", "language": "nl-NL" }]POST /v1/calls
Starts an outbound call with an assistant. Requires an outbound-capable number (see Outbound calls).
{
"to": "+31612345678",
"assistantId": "01J…",
"from": "+31201234567",
"variables": { "name": "Jan", "order": "BP20260016" }
}from is optional (the first outbound-capable number is used). variables is optional too: up to 200 variables, names up to 64 characters, values as text up to 2000 characters, a number or true/false. They fill {{name}} and {{order}} in the Outbound greeting, the assistant receives the values as data during the call, and you can use them afterwards in your actions. They are not filled in within the prompt text itself: {{name}} becomes empty there. So write "the customer's name" in the prompt instead of {{name}}.
Response:
{ "conversationId": "v3:…", "status": "dialing" }This endpoint can also return these errors:
| Status | code | Meaning |
|---|---|---|
400 | ERR-56001 | No number is available: no number is outbound-capable, or from is not yours or is not outbound-capable. |
402 | ERR-56007 | Outbound calling is not included in this business's plan. |
403 | ERR-56008 | Outbound calling is blocked for this business. Contact support. |
404 | ERR-56000 | The assistant was not found. |
GET /v1/conversations
Query parameters: from, to (ISO timestamps in UTC on startedAt), channel (voice, sms, whatsapp, webchat), outcome, assistantId, sentiment (positive, neutral, negative), flag (one of the flags below, or any for every flagged conversation), limit (default 100, max 500). An unknown sentiment or flag returns 400 with code ERR-52121. Returns an array of conversation objects, newest first. Archived conversations are not returned.
GET /v1/conversations/
One conversation object plus:
| Field | Meaning |
|---|---|
transcript | Array of { "role": "user" | "assistant" | "system", "text": "…", "createdAt": "…" }. user is the customer, assistant is the assistant or a colleague who took over the conversation, system are platform lines such as the greeting. text can contain markers, such as [image] for an image or the name of a tool used, in brackets. |
recordingUrl | Temporary download link for the recording (valid one hour), or null. Recordings are deleted after 30 days; after that the link no longer works. |
Leads
Leads are the requests you see in the dashboard under Inbox → Requests, see Requests. new and qualified both show there as Open, contacted as Got in touch, booked as Appointment made and lost as Closed.
GET /v1/leads?status=&limit=— array of leads, newest first (limitdefault 200, max 500).statusmay be given in any case:new,qualified,contacted,booked,lost. Any other value returns400with codeERR-56003.GET /v1/leads/{id}PATCH /v1/leads/{id}with{ "status": "contacted", "notes": "Called back, quote sent" }(both optional;notesis at most 4000 characters and may benull).
Lead object:
{
"id": "01J…",
"businessId": "01J…",
"conversationId": "v3:…",
"channel": "voice",
"status": "QUALIFIED",
"customerName": "Jan de Vries",
"customerPhone": "+31612345678",
"address": "Damrak 1, Amsterdam",
"problem": "Leaking kitchen tap",
"urgency": "urgent",
"preferredTime": "tomorrow morning",
"summary": "Leaking tap, wants a visit tomorrow morning",
"notes": null,
"phoneProof": null,
"customerPhoneVerified": false,
"customerPhoneVerifiedBy": null,
"createdAt": "2026-09-11T10:00:00.000Z",
"updatedAt": "2026-09-11T10:00:00.000Z"
}urgency is emergency, urgent, routine or unknown. status is returned in upper case. A lead from Try it out with Perform actions for real is included too; its conversationId starts with test:. customerPhoneVerified is true when the customer proved the number in the conversation, false when they did not, and null on a lead from before this field. customerPhoneVerifiedBy (and phoneProof.by) says how: caller_id (the customer called from that number), sms_code, email_code, call_code, whatsapp or phone_change.
Contacts
GET /v1/contacts?phone=&limit=— contacts, newest first (limitdefault 500, max 2000). Withphoneyou get an array with the contact that has exactly that E.164 number, or an empty array; write the+in the URL as%2B.POST /v1/contacts— body withphoneand/oremail(one is required), and optionallyname(at most 160 characters, split into first and last name) orfirstName/lastName,company,notes,tags,blocked,fields. Withoutphoneandemailyou get400with codeERR-56005. At the maximum of 1,000,000 contacts per business you get400with codeERR-52058;details.countholds the maximum. Duplicates are not checked: search with?phone=first if the number may already exist.GET /v1/contacts/{id}PATCH /v1/contacts/{id}— same fields as create, exceptname;blocked: truerejects future calls and SMS and WhatsApp messages from that number.
A contact also contains id, businessId, lastSeenAt, conversationCount, createdAt and updatedAt.
GET /v1/appointments
Query parameters from and to (ISO timestamps in UTC on startAt; default now to 90 days ahead). Returns at most 500 appointments booked on a service in Yarla, earliest first and including cancelled ones. Bookings through Cal.com or eTermin are not included.
[{
"id": "01J…",
"businessId": "01J…",
"eventTypeId": "01J…",
"startAt": "2026-09-15T07:00:00.000Z",
"endAt": "2026-09-15T08:00:00.000Z",
"timezone": "Europe/Amsterdam",
"status": "confirmed",
"guestName": "Jan de Vries",
"guestEmail": "jan@example.com",
"guestPhone": "+31612345678",
"fields": { "address": "Damrak 1" },
"source": "voice",
"conversationId": "v3:…",
"contactId": null,
"externalEventId": null,
"meetingUrl": "",
"remindersSent": [],
"phoneProof": { "phone": "+31612345678", "by": "caller_id", "at": "2026-09-11T09:58:00.000Z" },
"guestPhoneVerified": true,
"guestPhoneVerifiedBy": "caller_id",
"createdAt": "2026-09-11T10:00:00.000Z",
"updatedAt": "2026-09-11T10:00:00.000Z"
}]status is confirmed, rescheduled, cancelled, completed or no_show. source is voice, sms, whatsapp, webchat, web, dashboard or api. The customer's private manage link is never included. An appointment from Try it out with Perform actions for real is included too; its conversationId starts with test:. guestPhoneVerified is true or false when the appointment was booked or changed in a conversation, and null otherwise. guestPhoneVerifiedBy says how, as on a lead.
The conversation object
| Field | Meaning |
|---|---|
id | Conversation id; also used in conversationLink variables. |
assistantId | Assistant that handled it. |
channel | voice, sms, whatsapp or webchat. |
direction | inbound or outbound. |
customerPhone | E.164 number. For webchat usually null: the widget and the chat page do not pass a number. |
customerPhoneVerified | true when the customer proved the number in this conversation (they called from it, a code, or WhatsApp), false when not, null without a number. |
customerPhoneVerifiedBy | How: caller_id, sms_code, email_code, call_code, whatsapp or phone_change, or null. |
agentNumber | Your number the conversation ran on, or null for webchat. |
contactId | Recognised contact, or null. |
status | active, handed_over (a colleague is replying) or ended. |
outcome | resolved, transferred, appointment_booked, callback_requested, voicemail, abandoned, no_interaction, spam. Often still null while the conversation is active, but appointment_booked, callback_requested, transferred and voicemail can already be set during it. The final value follows after the conversation ends. |
summary | AI summary, filled in after the conversation ends. |
leadId, appointmentId | Linked lead and appointment, or null. |
durationSeconds | Call length; null for text channels. |
variables | The variables passed at the start (POST /v1/calls, a campaign, or the contact's name as name when you call a contact from the dashboard), plus the Custom variables to extract that the assistant took from the conversation afterwards. The widget and the chat page pass no variables at the start. |
inboundContext | Fields returned by your pre-call webhook. |
analysis | The assistant rating after the conversation, or null until it has been rated: sentiment (positive, neutral, negative), score (0 to 100), flags (zero or more of off_topic, not_grounded, failed_task, frustrated, wants_human, manipulation_attempt) and checks (Enterprise custom checks: { "id", "question", "passed" }, where passed is null when the question did not apply). |
campaignId | Set for outbound campaign calls. |
startedAt, endedAt | ISO timestamps; endedAt is null while active. |
The API uses its own field names (customerPhone, startedAt, recordingUrl). Webhook payloads use template variable names instead (personNumber for the customer number, startTimestamp, audioLink). The conversation object's id is the conversationId in the webhook; join the two on that. See Webhooks.