Skip to content

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 ​

  1. Go to Settings → Connections (Admin role required).
  2. Under API keys, enter a name (for example Make) and click Create.
  3. 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).

bash
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:

json
{ "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.

StatuscodeMeaning
400ERR-50020, ERR-50023–ERR-50028, ERR-50010Invalid 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.
401ERR-51004, ERR-51005Missing or invalid API key.
402ERR-52250, ERR-56007The subscription is unpaid, suspended or cancelled (ERR-52250, on every request). Outbound calling is not in the plan (ERR-56007, only POST /v1/calls).
403ERR-52056, ERR-56008Not allowed: the IP address is not on your business's list (ERR-52056), or outbound calling is blocked (ERR-56008).
404ERR-56000, ERR-56002, ERR-56004, ERR-56006, ERR-51044Object not found. A path that does not exist returns 404 without a code.
413ERR-50021The body is larger than 8 MB.
429ERR-50022Too many requests with this key. A 429 without code means the shared platform limit was reached. Retry after a short wait.
500ERR-50000Unexpected error; retry later.

Endpoints ​

GET /v1/me ​

Returns the business the key belongs to.

json
{
  "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.

json
[{ "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).

json
{
  "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:

json
{ "conversationId": "v3:…", "status": "dialing" }

This endpoint can also return these errors:

StatuscodeMeaning
400ERR-56001No number is available: no number is outbound-capable, or from is not yours or is not outbound-capable.
402ERR-56007Outbound calling is not included in this business's plan.
403ERR-56008Outbound calling is blocked for this business. Contact support.
404ERR-56000The 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:

FieldMeaning
transcriptArray 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.
recordingUrlTemporary 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 (limit default 200, max 500). status may be given in any case: new, qualified, contacted, booked, lost. Any other value returns 400 with code ERR-56003.
  • GET /v1/leads/{id}
  • PATCH /v1/leads/{id} with { "status": "contacted", "notes": "Called back, quote sent" } (both optional; notes is at most 4000 characters and may be null).

Lead object:

json
{
  "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 (limit default 500, max 2000). With phone you 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 with phone and/or email (one is required), and optionally name (at most 160 characters, split into first and last name) or firstName/lastName, company, notes, tags, blocked, fields. Without phone and email you get 400 with code ERR-56005. At the maximum of 1,000,000 contacts per business you get 400 with code ERR-52058; details.count holds 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, except name; blocked: true rejects 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.

json
[{
  "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 ​

FieldMeaning
idConversation id; also used in conversationLink variables.
assistantIdAssistant that handled it.
channelvoice, sms, whatsapp or webchat.
directioninbound or outbound.
customerPhoneE.164 number. For webchat usually null: the widget and the chat page do not pass a number.
customerPhoneVerifiedtrue when the customer proved the number in this conversation (they called from it, a code, or WhatsApp), false when not, null without a number.
customerPhoneVerifiedByHow: caller_id, sms_code, email_code, call_code, whatsapp or phone_change, or null.
agentNumberYour number the conversation ran on, or null for webchat.
contactIdRecognised contact, or null.
statusactive, handed_over (a colleague is replying) or ended.
outcomeresolved, 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.
summaryAI summary, filled in after the conversation ends.
leadId, appointmentIdLinked lead and appointment, or null.
durationSecondsCall length; null for text channels.
variablesThe 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.
inboundContextFields returned by your pre-call webhook.
analysisThe 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).
campaignIdSet for outbound campaign calls.
startedAt, endedAtISO 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.

Yarla Help — the guide to your AI phone assistant.