01 Overview
The Beridio public API gives your own software access to what your voice assistants do: the calls they handled, the records they took (appointments, reservations, leads, requests) and the tickets (orders, jobs, cases) they can tell callers about.
- Read calls, with their transcripts, and records and tickets, as they happen or in bulk.
- Write records, and keep tickets current from your own system so an assistant can tell a caller exactly where their order stands.
- React with signed webhooks the moment a call ends or a record or ticket changes.
Everything is JSON over HTTPS at https://api.beridio.com/api/v1/public. Dates are ISO 8601 in UTC and ids are UUIDs. The API only ever adds fields to what it sends, so ignore fields you don't know.
The fastest way to start is the SDK. It handles authentication, pagination, retries and webhook verification, and every request and response is typed.
The SDK is proprietary software licensed for use inside your own organization: you can build it into software you operate to talk to Beridio, but you can't redistribute it or build a competing client from it. See the license in the package.
02 Quickstart
1. Create an API key in the app under Settings → Integrations → API keys. Choose the scopes it needs, and optionally limit it to some assistants. The key is shown once.
2. Install the SDK and set the key as an environment variable.
npm install @beridio/sdk
export BERIDIO_API_KEY=brd_live_…3. Make your first call.
import { Beridio } from "@beridio/sdk";
const beridio = new Beridio(); // reads BERIDIO_API_KEY
const page = await beridio.calls.list({ limit: 5 });
for (const call of page.data) {
console.log(call.started_at, call.outcome, call.summary);
}The response is a page: { "data": [ … ], "next_cursor": "…" | null }. See Pagination.
03 Authentication
Send your key with every request, either way:
Authorization: Bearer brd_live_…
X-API-Key: brd_live_…Each key has scopes, which say what it may do, and may be limited to some assistants. Something outside the key's reach looks exactly like something that doesn't exist: 404 not_found. A key may also expire, and can be revoked at any time. A workspace can have up to 50 active keys.
| Scope | Allows |
|---|---|
assistants:read | List the assistants the key can see |
calls:read | List and read calls, including transcripts |
records:read | List and read records |
records:write | Create and change records |
tickets:read | List and read tickets |
tickets:write | Create, update and change tickets |
A key limited to exactly one assistant may leave out assistant_id on writes. Any other key must say which assistant a record or ticket is for.
04 JavaScript SDK
@beridio/sdk is the official client for JavaScript and TypeScript. It has no dependencies, works on Node 18 and newer, Bun, Deno and edge runtimes, and ships ESM and CommonJS builds with full type declarations. The types are generated from the API's own description, so they match what the server really sends.
Install
npm install @beridio/sdk
# or: pnpm add @beridio/sdk · yarn add @beridio/sdk · bun add @beridio/sdkConfigure
import { Beridio } from "@beridio/sdk";
const beridio = new Beridio({
apiKey: process.env.BERIDIO_API_KEY, // default: BERIDIO_API_KEY
timeoutMs: 30_000, // per attempt
maxRetries: 2, // 0 turns retries off
});| Option | Default | What it does |
|---|---|---|
apiKey | BERIDIO_API_KEY | Your key. Keep it on the server. |
baseUrl | https://api.beridio.com/api/v1/public | Where the API is. Set it for a self-hosted server. |
timeoutMs | 30000 | Gives up on one attempt after this long. |
maxRetries | 2 | How many times to retry. See below. |
fetch | global fetch | Your own fetch: a proxy, or a test double. |
headers | none | Extra headers on every request. |
Resources
| Methods | Scope | |
|---|---|---|
beridio.assistants | list() | assistants:read |
beridio.calls | list(params) · get(id, { include: "transcript" }) | calls:read |
beridio.records | list(params) · get(id) · create(input) · update(id, patch) | records:read / records:write |
beridio.tickets | list(params) · get(referenceOrId) · upsert(input) · put(reference, input) · update(referenceOrId, patch) | tickets:read / tickets:write |
beridio.webhooks | verify() · constructEvent() · sign() | none |
Every method takes an optional last argument, { signal, timeoutMs }, to cancel a call or give it its own timeout.
Examples
const since = new Date(Date.now() - 24 * 3600 * 1000);
// oldest first, so a sync can stop and resume where it left off
for await (const record of await beridio.records.list({ updated_since: since, order: "asc" })) {
await saveToMyCrm(record); // record is a BeridioRecord
}const call = await beridio.calls.get(callId, { include: "transcript" });
for (const turn of call.transcript ?? []) console.log(`${turn.role}: ${turn.text}`);await beridio.records.update(record.id, {
status: "confirmed",
notes: "Confirmed by phone",
data: { room: "B2" }, // merged into what is there; null removes a key
});// Safe to repeat: the same call twice leaves one ticket.
await beridio.tickets.put("ORD-1042", {
assistant_id: assistantId, // optional for a key limited to one assistant
customer_name: "Ana López",
status: "ready",
public_note: "Ready for pickup after 4 pm", // what the assistant may tell the caller
external_id: "erp-88213",
});Pagination
Lists return a Page. Use page.data for its items, await page.nextPage() to walk by hand, or for await to go through every item without handling cursors. limit is 1 to 100 (default 25). order is desc (newest first, default) or asc.
const page = await beridio.tickets.list({ status: "open", limit: 50 });
page.data; // this page
page.hasNextPage; // more to fetch?
await page.toArray(500); // up to 500 items from here on
for await (const ticket of page) { /* every open ticket, page after page */ }Errors
A failed call throws a BeridioError with status, code (the API's own) and message. Catch the specific class you care about:
import { NotFoundError, RateLimitError } from "@beridio/sdk";
try {
await beridio.records.get(id);
} catch (error) {
if (error instanceof NotFoundError) {
// no such record, or not visible to this key
} else if (error instanceof RateLimitError) {
await sleep((error.retryAfterSeconds ?? 1) * 1000);
} else {
throw error;
}
}| Class | Status | When |
|---|---|---|
AuthenticationError | 401 | The key is missing, unknown, revoked or expired |
PermissionError | 403 | The key lacks the scope the call needs |
NotFoundError | 404 | It doesn't exist, or isn't visible to this key |
ConflictError | 409 | Ambiguous: a ticket reference several assistants use. Add assistant_id |
InvalidRequestError | 400, 422 | A parameter or the body isn't valid; message says which |
RateLimitError | 429 | Too many requests; retryAfterSeconds says how long to wait |
ServerError | 5xx | Something went wrong on Beridio's side |
ConnectionError | none | The request got no answer: the network failed or it timed out |
Retries
A call is retried up to maxRetries times (default 2) with exponential backoff and jitter: after a 429, waiting as long as Retry-After says, and, for calls that are safe to repeat (GET and PUT), after a network failure or a 5xx. A POST or PATCH is never repeated after a server error, because it may already have been done. beridio.rateLimit holds { limit, remaining } from the last response.
Types
Everything the API sends or accepts is exported: Call, CallDetail, BeridioRecord, RecordCreate, RecordPatch, Ticket, TicketInput, TicketPatch, Assistant, Message, WebhookEvent (and one type per event), the parameter types such as ListCallsParams, and unions such as RecordStatus and TicketStatus. The record type is called BeridioRecord so it doesn't clash with TypeScript's own Record.
import type { BeridioRecord, RecordCreate, TicketStatus } from "@beridio/sdk";
const input: RecordCreate = { kind: "lead", data: { name: "Ana López", phone: "+525512345678" } };
// status: "nope" → a compile error, not a 422 at run time05 Webhooks
A webhook sends your server an HTTP POST when something happens, so you don't have to poll. Add an endpoint in the app under Settings → Integrations → Webhooks, choose the events it receives, and copy its signing secret (whsec_…). The secret is shown when you create the endpoint and when you rotate it.
The URL must be a public https:// address. Redirects are not followed. Answer with any 2xx status within 10 seconds; anything else counts as a failure.
What a delivery looks like
POST /webhooks/beridio
Content-Type: application/json
User-Agent: Beridio-Webhooks/1
X-Beridio-Event: record.created
X-Beridio-Event-Id: evt_0123456789abcdef01234567
X-Beridio-Delivery: <delivery id>
X-Beridio-Signature: t=1773504300,v1=<hex>{
"id": "evt_0123456789abcdef01234567",
"type": "record.created",
"created_at": "2026-03-14T16:05:00.000Z",
"assistant_id": "0d6f2d0e-…",
"data": { … }
}Verify the signature
Anyone can post to your URL, so check that a request really came from Beridio. The signature header is t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256, keyed with your endpoint's secret, of the text <t>.<raw body>. Reject a signature whose time is more than five minutes from now, so a captured request can't be replayed.
import express from "express";
import { Beridio, WebhookSignatureError } from "@beridio/sdk";
const beridio = new Beridio();
const app = express();
// express.raw keeps the body exactly as it arrived
app.post("/webhooks/beridio", express.raw({ type: "application/json" }), async (req, res) => {
try {
const event = await beridio.webhooks.constructEvent(
req.body,
req.header("x-beridio-signature")!,
process.env.BERIDIO_WEBHOOK_SECRET!,
);
switch (event.type) {
case "record.created": await onRecord(event.data); break; // BeridioRecord
case "call.completed": await onCall(event.data); break; // Call
case "ticket.updated": await onTicket(event.data); break; // Ticket
}
res.sendStatus(200);
} catch (error) {
res.sendStatus(error instanceof WebhookSignatureError ? 400 : 500);
}
});Retries and idempotency
If your endpoint doesn't answer with a 2xx, Beridio retries after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. After the sixth failed attempt the delivery is marked dead; you can retry it from the app. An endpoint that does nothing but fail for three days is paused. Every retry of a delivery carries the same id (also in X-Beridio-Event-Id), so store the ids you have handled and ignore a repeat.
In the app, Send a test delivers a webhook.test event to check your endpoint without waiting for a real one. In your own tests, await beridio.webhooks.sign(body, secret) produces the header Beridio would send.
Events
06 Conventions
Pagination
Every list answers { "data": [ … ], "next_cursor": "…" | null }. Send next_cursor back as cursor for the next page; null means the last page. Cursors are opaque and stay valid while rows change. limit is 1 to 100 (default 25). order=asc together with updated_since is the best way to sync.
Errors
Errors are always { "error": { "code": "…", "message": "…" } }.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_parameter | A query parameter isn't valid |
| 401 | missing_key · invalid_key · revoked_key · expired_key | The key can't be used |
| 403 | insufficient_scope | The key lacks the scope this operation needs |
| 404 | not_found · assistant_not_found | Doesn't exist, or isn't visible to this key |
| 409 | ambiguous_reference | Several assistants use this ticket reference: add assistant_id |
| 422 | invalid_request | The data isn't valid for this flow or ticket |
| 429 | rate_limited | More than 120 requests in a minute for this key |
| 500 | internal_error | Something went wrong on our side |
Rate limit
120 requests per minute for each key. Past that you get 429 with a Retry-After header (seconds to wait). Every answer carries X-RateLimit-Limit and X-RateLimit-Remaining. The SDK waits and retries for you.
Statuses
| Values | |
|---|---|
| Records | new · confirmed · completed · cancelled · no_show |
| Tickets | open · in_progress · waiting_customer · ready · completed · cancelled |
07 API reference
Base URL: https://api.beridio.com/api/v1/public. Every operation needs an API key with the scope shown.
/assistantsassistants:readList assistants. The assistants this key can see.
SDK: beridio.assistants.list()
/callscalls:readList calls. Calls an assistant handled, newest first. Recording addresses are never included.
SDK: beridio.calls.list(params)
| Parameter | In | Type | Description |
|---|---|---|---|
assistant_id | query | uuid | Only this assistant. |
status | query | string | Call status. |
outcome | query | string | Call outcome. |
from | query | date-time | Started at or after (ISO 8601). |
to | query | date-time | Started before (ISO 8601). |
limit | query | integer | Page size, 1 to 100 (default 25). |
cursor | query | string | The next_cursor of the previous page. |
order | query | asc | desc | desc (default) newest first; asc oldest first, handy for syncing with updated_since. |
/calls/{id}calls:readGet a call.
SDK: beridio.calls.get(id, { include })
| Parameter | In | Type | Description |
|---|---|---|---|
id * | path | string | |
include | query | transcript | transcript adds the conversation turns. |
/recordsrecords:readList records. What callers gave the assistants: appointments, reservations, leads, requests.
SDK: beridio.records.list(params)
| Parameter | In | Type | Description |
|---|---|---|---|
assistant_id | query | uuid | Only this assistant. |
kind | query | appointment | reservation | lead | change_request | custom | Flow kind. |
status | query | new | confirmed | completed | cancelled | no_show | Record status. |
updated_since | query | date-time | Only what changed at or after this time (ISO 8601). |
scheduled_from | query | date-time | Scheduled at or after. |
scheduled_to | query | date-time | Scheduled before. |
q | query | string | Search in reference, summary, notes and flow name. |
limit | query | integer | Page size, 1 to 100 (default 25). |
cursor | query | string | The next_cursor of the previous page. |
order | query | asc | desc | desc (default) newest first; asc oldest first, handy for syncing with updated_since. |
/recordsrecords:writeCreate a record. Same validation and history as a record taken on a call; source is api.
SDK: beridio.records.create(input)
Body: RecordCreate (JSON)
/records/{id}records:readGet a record.
SDK: beridio.records.get(id)
| Parameter | In | Type | Description |
|---|---|---|---|
id * | path | string |
/records/{id}records:writeChange a record.
SDK: beridio.records.update(id, patch)
| Parameter | In | Type | Description |
|---|---|---|---|
id * | path | string |
Body: RecordPatch (JSON)
/ticketstickets:readList tickets.
SDK: beridio.tickets.list(params)
| Parameter | In | Type | Description |
|---|---|---|---|
assistant_id | query | uuid | Only this assistant. |
reference | query | string | Exact reference. |
external_id | query | string | The id in your own system. |
status | query | open | in_progress | waiting_customer | ready | completed | cancelled | Ticket status. |
phone | query | string | Customer phone number. |
updated_since | query | date-time | Only what changed at or after this time (ISO 8601). |
limit | query | integer | Page size, 1 to 100 (default 25). |
cursor | query | string | The next_cursor of the previous page. |
order | query | asc | desc | desc (default) newest first; asc oldest first, handy for syncing with updated_since. |
/ticketstickets:writeCreate or update a ticket. Upsert: updates the ticket with the same reference (or external_id) of the assistant, else creates one. 201 when created, 200 when updated.
SDK: beridio.tickets.upsert(input)
Body: TicketInput (JSON)
/tickets/{referenceOrId}tickets:readGet a ticket. By id or reference. A reference used by several assistants needs assistant_id (409 otherwise).
SDK: beridio.tickets.get(referenceOrId)
| Parameter | In | Type | Description |
|---|---|---|---|
referenceOrId * | path | string | |
assistant_id | query | uuid | Only this assistant. |
/tickets/{referenceOrId}tickets:writeCreate or update a ticket by reference. The path segment is the ticket's reference (never an id).
SDK: beridio.tickets.put(reference, input)
| Parameter | In | Type | Description |
|---|---|---|---|
referenceOrId * | path | string |
Body: TicketInput (JSON)
/tickets/{referenceOrId}tickets:writeChange a ticket.
SDK: beridio.tickets.update(referenceOrId, patch)
| Parameter | In | Type | Description |
|---|---|---|---|
referenceOrId * | path | string | |
assistant_id | query | uuid | Only this assistant. |
Body: TicketPatch (JSON)
The full machine-readable description is an OpenAPI 3.1 document at https://api.beridio.com/api/v1/public/openapi.json (no key needed). Import it into Postman or Insomnia, or use it with a code generator for another language.