Beridio
The InsightModules
Industries ▾
HealthcareManufacturingE-commerceLogisticsFinancial servicesEnergy
Use cases ▾
SalesMedia relationsAds & campaignsBrand protectionSupplier riskMarket intelligence
Pricing
Start free trial

developers

Build on Beridio

A REST API, a TypeScript-first JavaScript SDK and signed webhooks: read what your assistants hear and collect, and keep your own systems and your assistants in step.

$ npm install @beridio/sdk

REST · https://api.beridio.com/api/v1/public

Contents

  1. 01 Overview
  2. 02 Quickstart
  3. 03 Authentication
  4. 04 JavaScript SDK
  5. 05 Webhooks
  6. 06 Conventions
  7. 07 API reference

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.

The API and the SDK are for servers. An API key can read and change your workspace's data, so never put one in a web page or a mobile app.

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.

sh
npm install @beridio/sdk
export BERIDIO_API_KEY=brd_live_…

3. Make your first call.

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

http
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.

ScopeAllows
assistants:readList the assistants the key can see
calls:readList and read calls, including transcripts
records:readList and read records
records:writeCreate and change records
tickets:readList and read tickets
tickets:writeCreate, 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

sh
npm install @beridio/sdk
# or: pnpm add @beridio/sdk   ·   yarn add @beridio/sdk   ·   bun add @beridio/sdk

Configure

ts
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
});
OptionDefaultWhat it does
apiKeyBERIDIO_API_KEYYour key. Keep it on the server.
baseUrlhttps://api.beridio.com/api/v1/publicWhere the API is. Set it for a self-hosted server.
timeoutMs30000Gives up on one attempt after this long.
maxRetries2How many times to retry. See below.
fetchglobal fetchYour own fetch: a proxy, or a test double.
headersnoneExtra headers on every request.

Resources

MethodsScope
beridio.assistantslist()assistants:read
beridio.callslist(params) · get(id, { include: "transcript" })calls:read
beridio.recordslist(params) · get(id) · create(input) · update(id, patch)records:read / records:write
beridio.ticketslist(params) · get(referenceOrId) · upsert(input) · put(reference, input) · update(referenceOrId, patch)tickets:read / tickets:write
beridio.webhooksverify() · constructEvent() · sign()none

Every method takes an optional last argument, { signal, timeoutMs }, to cancel a call or give it its own timeout.

Examples

Sync everything that changed since yesterday
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
}
Read a call and its conversation
const call = await beridio.calls.get(callId, { include: "transcript" });
for (const turn of call.transcript ?? []) console.log(`${turn.role}: ${turn.text}`);
Confirm an appointment
await beridio.records.update(record.id, {
  status: "confirmed",
  notes: "Confirmed by phone",
  data: { room: "B2" }, // merged into what is there; null removes a key
});
Keep an order's status current from your own system
// 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.

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

ts
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;
  }
}
ClassStatusWhen
AuthenticationError401The key is missing, unknown, revoked or expired
PermissionError403The key lacks the scope the call needs
NotFoundError404It doesn't exist, or isn't visible to this key
ConflictError409Ambiguous: a ticket reference several assistants use. Add assistant_id
InvalidRequestError400, 422A parameter or the body isn't valid; message says which
RateLimitError429Too many requests; retryAfterSeconds says how long to wait
ServerError5xxSomething went wrong on Beridio's side
ConnectionErrornoneThe 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.

ts
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 time

05 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

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

Verify against the raw body, exactly as received. If your framework has already parsed the JSON, the original bytes are gone and the check will fail.
SDK (Express)
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": "…" } }.

StatusCodeMeaning
400invalid_parameterA query parameter isn't valid
401missing_key · invalid_key · revoked_key · expired_keyThe key can't be used
403insufficient_scopeThe key lacks the scope this operation needs
404not_found · assistant_not_foundDoesn't exist, or isn't visible to this key
409ambiguous_referenceSeveral assistants use this ticket reference: add assistant_id
422invalid_requestThe data isn't valid for this flow or ticket
429rate_limitedMore than 120 requests in a minute for this key
500internal_errorSomething 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
Recordsnew · confirmed · completed · cancelled · no_show
Ticketsopen · 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.

GET/assistantsassistants:read

List assistants. The assistants this key can see.

SDK: beridio.assistants.list()

GET/callscalls:read

List calls. Calls an assistant handled, newest first. Recording addresses are never included.

SDK: beridio.calls.list(params)

ParameterInTypeDescription
assistant_idqueryuuidOnly this assistant.
statusquerystringCall status.
outcomequerystringCall outcome.
fromquerydate-timeStarted at or after (ISO 8601).
toquerydate-timeStarted before (ISO 8601).
limitqueryintegerPage size, 1 to 100 (default 25).
cursorquerystringThe next_cursor of the previous page.
orderqueryasc | descdesc (default) newest first; asc oldest first, handy for syncing with updated_since.
GET/calls/{id}calls:read

Get a call.

SDK: beridio.calls.get(id, { include })

ParameterInTypeDescription
id *pathstring
includequerytranscripttranscript adds the conversation turns.
GET/recordsrecords:read

List records. What callers gave the assistants: appointments, reservations, leads, requests.

SDK: beridio.records.list(params)

ParameterInTypeDescription
assistant_idqueryuuidOnly this assistant.
kindqueryappointment | reservation | lead | change_request | customFlow kind.
statusquerynew | confirmed | completed | cancelled | no_showRecord status.
updated_sincequerydate-timeOnly what changed at or after this time (ISO 8601).
scheduled_fromquerydate-timeScheduled at or after.
scheduled_toquerydate-timeScheduled before.
qquerystringSearch in reference, summary, notes and flow name.
limitqueryintegerPage size, 1 to 100 (default 25).
cursorquerystringThe next_cursor of the previous page.
orderqueryasc | descdesc (default) newest first; asc oldest first, handy for syncing with updated_since.
POST/recordsrecords:write

Create a record. Same validation and history as a record taken on a call; source is api.

SDK: beridio.records.create(input)

Body: RecordCreate (JSON)

GET/records/{id}records:read

Get a record.

SDK: beridio.records.get(id)

ParameterInTypeDescription
id *pathstring
PATCH/records/{id}records:write

Change a record.

SDK: beridio.records.update(id, patch)

ParameterInTypeDescription
id *pathstring

Body: RecordPatch (JSON)

GET/ticketstickets:read

List tickets.

SDK: beridio.tickets.list(params)

ParameterInTypeDescription
assistant_idqueryuuidOnly this assistant.
referencequerystringExact reference.
external_idquerystringThe id in your own system.
statusqueryopen | in_progress | waiting_customer | ready | completed | cancelledTicket status.
phonequerystringCustomer phone number.
updated_sincequerydate-timeOnly what changed at or after this time (ISO 8601).
limitqueryintegerPage size, 1 to 100 (default 25).
cursorquerystringThe next_cursor of the previous page.
orderqueryasc | descdesc (default) newest first; asc oldest first, handy for syncing with updated_since.
POST/ticketstickets:write

Create 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)

GET/tickets/{referenceOrId}tickets:read

Get a ticket. By id or reference. A reference used by several assistants needs assistant_id (409 otherwise).

SDK: beridio.tickets.get(referenceOrId)

ParameterInTypeDescription
referenceOrId *pathstring
assistant_idqueryuuidOnly this assistant.
PUT/tickets/{referenceOrId}tickets:write

Create or update a ticket by reference. The path segment is the ticket's reference (never an id).

SDK: beridio.tickets.put(reference, input)

ParameterInTypeDescription
referenceOrId *pathstring

Body: TicketInput (JSON)

PATCH/tickets/{referenceOrId}tickets:write

Change a ticket.

SDK: beridio.tickets.update(referenceOrId, patch)

ParameterInTypeDescription
referenceOrId *pathstring
assistant_idqueryuuidOnly 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.

Beridio

Sales intelligence for B2B teams in Mexico and LatAm.

Platform

The InsightModulesthe enginePricingDevelopers

Industries

HealthcareManufacturingE-commerceLogisticsFinancial servicesEnergy

Use cases

SalesMedia relationsAds & campaignsBrand protectionSupplier riskMarket intelligence
© 2026 BeridioTermsPrivacyhello@beridio.com