Developers and the API

Relay has a REST API for building your own integrations, and outgoing webhooks for reacting to events in real time. Both live under Settings → API keys and Settings → Webhooks, admin and owner only.

API keys

An API key authenticates requests to Relay's public REST API. Create one from Settings → API keys → New API key: give it a name (name it after the integration that will use it) and pick only the scopes it needs.

Available scopes:

ScopeGrants
messages:sendSend messages
messages:readRead messages
contacts:readRead contacts
contacts:writeCreate and update contacts
conversations:readRead conversations
broadcasts:sendSend campaigns
webhooks:manageCreate, update and delete webhook endpoints

A key's scopes are all that gates what it can do — they're independent of the role of whoever created it. A key with no scopes at all can still call GET /api/v1/me to verify it works.

REST API basics

All public endpoints live under /api/v1 and require a bearer key:

Authorization: Bearer relay_live_xxxxxxxxxxxx

A few things that hold across every endpoint:

  • Account-scoped. A key only ever sees and acts on the account it belongs to.
  • Multi-number aware. If your account has more than one connected number, pass ?from=num_<uuid> to target a specific one; omitting it uses your default number.
  • Response envelope. Successful responses are wrapped as { "data": ... }; errors as { "error": { "code": "...", "message": "..." } }.
  • Rate limited. 120 requests per minute per key. Going over returns 429 rate_limited with a Retry-After header (and X-RateLimit-* headers on every response so you can see how close you are).
  • GET /api/v1/me is the discovery endpoint — it tells you the account, the key's scopes, and the numbers available to it, which is the fastest way to sanity-check a new key.

Error codes worth knowing

HTTPCodeMeaning
401unauthorizedMissing or invalid key
402upgrade_requiredThe action isn't included in your plan
402insufficient_balanceYour plan allows it, but the prepaid wallet doesn't have enough funds
403forbiddenKey doesn't have the required scope
429rate_limitedOver 120 requests/minute
400bad_requestMalformed request
404not_foundNo such resource for this account
500internalSomething went wrong on Relay's side

upgrade_required and insufficient_balance are deliberately distinct 402s — one means "change your plan," the other means "top up the wallet" (see Usage and limits and Wallet).

Outgoing webhooks

Webhooks push events to your own HTTPS endpoint in real time, instead of you polling the API. Create one from Settings → Webhooks → New webhook: give it a URL (must be https://) and pick which events it should receive.

Available events:

EventFires when
message.receivedAn inbound WhatsApp message arrives
message.status_updatedA sent message's delivery status changes (sent, delivered, read, failed)
conversation.createdA new conversation is started

Verifying a delivery

Every delivery is signed. Relay sends a header:

X-Relay-Signature: t=<unix_seconds>,v1=<hex>

v1 is HMAC-SHA256(secret, "${t}.${rawBody}"), computed with the endpoint's signing secret. Verify it by recomputing the same HMAC over the timestamp and raw request body and comparing — reject anything that doesn't match, and reject stale timestamps to guard against replay.

A few operational notes:

  • Endpoints are checked against SSRF risks (no pointing a webhook at your own internal network) before Relay will accept them.
  • An endpoint that fails delivery repeatedly, past a threshold, is automatically disabled — a warning appears once it's most of the way there, so you have a chance to fix it first. A disabled endpoint has to be re-enabled manually once the underlying problem is fixed.
  • Webhooks are a plan-gated feature and the number of endpoints you can create is capped by your plan (see Usage and limits).

The relay-mcp server

relay-mcp is a small Model Context Protocol server that wraps the public API, so an AI tool (Claude, or anything else that speaks MCP) can act on your Relay account directly. Point it at your account with two environment variables:

{
  "mcpServers": {
    "relay": {
      "command": "npx",
      "args": ["-y", "relay-mcp"],
      "env": {
        "RELAY_BASE_URL": "https://your-relay-domain.example.com",
        "RELAY_API_KEY": "relay_live_xxxxxxxxxxxx"
      }
    }
  }
}

By default it's read-only — tools like list_contacts, get_contact, list_conversations, list_messages and get_broadcast work out of the box, but nothing that writes or sends does. Opt in explicitly per capability:

  • RELAY_ENABLE_WRITES=true — unlocks send_message, create_contact, update_contact.
  • RELAY_ENABLE_BROADCASTS=true — unlocks send_broadcast, which additionally requires confirm: true on every call since it's marked destructive.

Who can manage this

API keys and webhooks are admin-and-owner gated, same as the rest of account-wide settings (see Members and roles).