Developer Documentation

SendBroad API

Sync contacts, browse templates, and trigger WhatsApp campaigns from your own systems. The Public v1 API is a small, focused REST surface authenticated with a single header.

Overview

Introduction

The SendBroad Public API lets you keep your contact book in sync, list approved message templates, and kick off WhatsApp campaigns programmatically. Every endpoint is authenticated with an API key you generate from the dashboard.

REST + JSON

Predictable resource paths, standard HTTP verbs, and JSON request and response bodies.

API key auth

A single x-api-key header. Generate, rotate, or revoke keys from your settings at any time.

Versioned

All endpoints live under /public/v1/* so future changes ship without breaking your integration.

Getting started

Quickstart

A complete integration in three steps: generate a key, store it as an environment variable, and call your first endpoint.

  1. 1

    Generate an API key

    Open Settings → API & Developer and click Generate API Key. The full key is shown once, so copy it before closing the dialog.
  2. 2

    Store it securely

    Add it to your environment, never to source control:
    .env
    export SENDBROAD_API_KEY="sb_live_xxxxxxxxxxxx"
  3. 3

    Call the API

    Make a first authenticated request to list your WhatsApp templates:
    first-request
    curl -X GET "https://api.sendbroad.com/api/public/v1/templates?limit=5" \
      -H "x-api-key: $SENDBROAD_API_KEY"

Authentication

Generate an API key

API keys are scoped to a single workspace and can be regenerated or revoked at any time. Each key inherits your account permissions.

To generate a new key:

  1. Sign in and head to Settings.
  2. Open the API & Developer section.
  3. Click Generate API Key. If you already have a key, the button reads Regenerate API Key; regenerating immediately invalidates the old one.
  4. Copy the full key from the blue confirmation banner. It will only be shown once.

Treat keys like passwords

Anyone with your API key can send messages and read your data. Never commit a key to git, never share it in screenshots, and rotate it immediately if you think it may have leaked.

Lost your key?

Only the prefix (the first 12 characters) and the generation timestamp are stored. If you lose the full key, click Regenerate API Key to mint a new one and update your environment variables.

Authenticating requests

Send your key as the x-api-key header on every request. Requests without it return 401 Unauthorized.

headers
curl https://api.sendbroad.com/api/public/v1/templates \
  -H "x-api-key: $SENDBROAD_API_KEY"

Base URL & versioning

All endpoints are mounted under a single, versioned prefix.

https://api.sendbroad.com/api/public/v1

We will never make a breaking change inside v1. Future revisions ship as v2 on a separate prefix.

Conventions

Errors

Errors use standard HTTP status codes plus a machine-readable JSON body. The name field is stable across versions and is safe to switch on in your code.

Response
{
  "name": "BadRequestError",
  "message": "No valid contacts found in request"
}
FieldTypeDescription
400BadRequestErrorThe request body or query is missing or malformed. The message explains which field failed validation.
401UnauthorizedErrorMissing or invalid x-api-key header. Generate a fresh key from the dashboard and try again.
404NotFoundErrorReferenced resource (template, segment, etc.) does not exist on your workspace.
429RateLimitErrorToo many requests. The Retry-After header tells you how many seconds to wait before retrying.
500InternalServerErrorSomething went wrong on our end. Retry with exponential backoff and contact support if it persists.

Rate limits

Public v1 endpoints share a per-account budget. When you exceed it, we return a 429 with a Retry-After header in seconds.

Backoff strategy

On a 429, sleep for the number of seconds in Retry-After (or the retryAfterSeconds value in the body) and retry. Never retry tighter than 1 request per second per account.

Endpoints

Sync contacts

Upsert a batch of contacts onto your workspace. Existing contacts (matched by phone number) get updated; new ones are created. Segments are auto-created if they don't exist.

POST/public/v1/contacts

Body

FieldTypeDescription
contactsrequiredContact[]Array of contacts to upsert. At least one entry is required.
contacts[].namerequiredstringDisplay name for the contact.
contacts[].countryCoderequiredstringCountry dialing code. May include a leading +; non-digits are ignored. Example: +91.
contacts[].numberrequiredstringLocal phone number, digits only. The country code is prepended internally.
contacts[].segmentsstring[]Optional list of segment names. Missing segments are created on the fly. Names are normalized (lowercased, spaces become underscores).

Request

sync-contacts
curl -X POST https://api.sendbroad.com/api/public/v1/contacts \
  -H "x-api-key: $SENDBROAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      {
        "name": "Aanya Sharma",
        "countryCode": "+91",
        "number": "9876543210",
        "segments": ["vip", "mumbai-launch"]
      },
      {
        "name": "Rahul Verma",
        "countryCode": "+91",
        "number": "9123456789"
      }
    ]
  }'

Response

Response
{
  "received": 2,
  "inserted": 1,
  "updated": 1,
  "failed": 0
}

failed counts contacts that were dropped because name, countryCode, or number was empty after normalization. The request still succeeds as long as at least one contact is valid.

List templates

Paginate through your message templates. Use the filters to narrow down by status, channel, or name.

GET/public/v1/templates

Query parameters

FieldTypeDescription
statusstring | string[]Filter by template status. Repeat the parameter or pass a comma-separated list. Allowed values: DRAFT, PENDING, APPROVED, REJECTED, STALE.
channelstringFilter by channel. One of whatsapp or email.
searchstringCase-insensitive substring match against the template name. Max 120 characters.
pagenumber1-indexed page number. Defaults to 1.
limitnumberPage size, between 1 and 100. Defaults to 20.

Request

list-templates
curl -X GET "https://api.sendbroad.com/api/public/v1/templates?status=APPROVED&channel=whatsapp&page=1&limit=20" \
  -H "x-api-key: $SENDBROAD_API_KEY"

Response

Response
{
  "items": [
    {
      "id": "65fb1e2bd2a2c70012a3f456",
      "name": "Order Shipped v2",
      "uniqueName": "order_shipped_v2",
      "status": "APPROVED",
      "channel": "whatsapp",
      "whatsappTemplateId": "1234567890",
      "createdAt": "2026-04-01T08:30:00.000Z",
      "updatedAt": "2026-05-12T11:42:18.000Z"
    }
  ],
  "page": 1,
  "limit": 20,
  "total": 1,
  "totalPages": 1
}

Trigger a campaign

Schedule a WhatsApp campaign against one of your APPROVED templates and a set of segments. The campaign is queued for delivery in 5 minutes, leaving a window to cancel from the dashboard.

POST/public/v1/campaigns/trigger

Body

FieldTypeDescription
templateIdrequiredstringID of an APPROVED template that belongs to your workspace.
segmentsrequiredstring[]One or more existing segment names. Unknown segments are returned in the response under unknownSegments and are skipped.

Request

trigger-campaign
curl -X POST https://api.sendbroad.com/api/public/v1/campaigns/trigger \
  -H "x-api-key: $SENDBROAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "65fb1e2bd2a2c70012a3f456",
    "segments": ["vip", "mumbai-launch"]
  }'

Response

Response
{
  "campaignId": "6651a93cc7e2a40012b8d111",
  "matchedSegments": [
    {
      "id": "6650f2aa10b4a30012a01122",
      "name": "vip"
    },
    {
      "id": "6650f2c810b4a30012a01133",
      "name": "mumbai-launch"
    }
  ],
  "unknownSegments": [],
  "recipientCount": 482,
  "scheduledAt": "2026-05-30T11:25:00.000Z"
}

Plan limits apply

Campaign creation honours your plan's daily message budget and template approval state. If a request fails with BadRequestError, the message explains exactly which limit was hit (no recipients, template not approved, quota exceeded, etc.).

Help

Need a hand?

If something feels off, we'd rather hear about it sooner than later.