Skip to content

API

API reference

Auto-generated from the live OpenAPI spec served by the product app, always in sync, because the spec is the source of truth.

Raw spec: https://app.xyrachat.com/api/v1/openapi.json

Quick start

Everything you need before you call your first endpoint.

Authentication

All requests use a Bearer token. Create one in the app under Settings → API & Webhooks (workspace owners and admins only) and send it on every request:

Authorization: Bearer xyra_live_…

Keys are workspace-scoped and can be rotated or revoked at any time. Never embed a key in client-side code.

API access is included on the Edge, Prime and Infinite plans.

Rate limits

Every response carries the standard X-RateLimit-* headers. Default ceilings:

  • • 600 read requests / minute per API key
  • • 120 write requests / minute per API key
  • • Over the limit: 429 with Retry-After

Need higher? Email hello@xyrachat.com , we'll raise it.

Webhooks

Subscribe an HTTPS endpoint to any of 17 events, such as message.received or bot.handoff. Every delivery is signed:

X-Xyra-Signature: t=1760000000,v1=5f2c…

Signing, retries and the full event list are below.

Your first request

List the most recent conversations in your workspace:

curl -X GET https://app.xyrachat.com/api/v1/conversations \
  -H "Authorization: Bearer $XYRA_API_KEY" \
  -H "Accept: application/json"

You should get a JSON list back. If not, the error body tells you what to fix.

Webhooks

Signed deliveries, retried until they land.

Add an endpoint under Settings → API & Webhooks. Its signing secret is shown once, when you create it, so store it straight away.

Headers on every delivery

X-Xyra-Signature
t=<unix seconds>,v1=<hex HMAC-SHA256>
X-Xyra-Timestamp
the same unix seconds as t
X-Xyra-Event
the event name, e.g. message.received
X-Xyra-Event-Id
unique per event, the same on every retry

Retries carry the same X-Xyra-Event-Id, so store it and ignore an id you have already processed.

Verifying the signature

Compute an HMAC-SHA256, keyed with the endpoint's signing secret, over <t>.<raw request body>, and compare it with v1. Reject deliveries whose t is more than 5 minutes old.

import crypto from "node:crypto";

// rawBody: the body exactly as received, not parsed
function verifyXyraWebhook(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((p) => p.split("="))
  );
  const t = Number(parts.t);
  // Reject anything older (or newer) than 5 minutes
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Delivery and retries

  • • Your endpoint has 10 seconds to respond.
  • • Any 2xx counts as delivered.
  • • 410 Gone stops delivery and pauses the endpoint.
  • • Any other 4xx is not retried.
  • • A 5xx or a timeout is retried after 30 s, 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h.

All 17 events

Messages

  • message.received
  • message.sent

Conversations

  • conversation.opened
  • conversation.closed
  • conversation.assigned
  • conversation.unassigned

Contacts

  • contact.created
  • contact.updated
  • contact.tagged
  • contact.opted_out

Bot

  • bot.handoff
  • bot.lead_captured
  • bot.link_clicked
  • bot.qualified

Workspace

  • broadcast.completed
  • channel.disconnected
  • automation.fired

Full reference

Every endpoint, every model.

Browse, search and try schemas below, themed to feel like the rest of Xyra Chat.