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
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
Store it securely
Add it to your environment, never to source control:.envexport SENDBROAD_API_KEY="sb_live_xxxxxxxxxxxx" - 3
Call the API
Make a first authenticated request to list your WhatsApp templates:first-requestcurl -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:
- Sign in and head to Settings.
- Open the API & Developer section.
- Click Generate API Key. If you already have a key, the button reads Regenerate API Key; regenerating immediately invalidates the old one.
- Copy the full key from the blue confirmation banner. It will only be shown once.
Treat keys like passwords
Lost your key?
Authenticating requests
Send your key as the x-api-key header on every request. Requests without it return 401 Unauthorized.
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.
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.
{
"name": "BadRequestError",
"message": "No valid contacts found in request"
}| Field | Type | Description |
|---|---|---|
400 | BadRequestError | The request body or query is missing or malformed. The message explains which field failed validation. |
401 | UnauthorizedError | Missing or invalid x-api-key header. Generate a fresh key from the dashboard and try again. |
404 | NotFoundError | Referenced resource (template, segment, etc.) does not exist on your workspace. |
429 | RateLimitError | Too many requests. The Retry-After header tells you how many seconds to wait before retrying. |
500 | InternalServerError | Something 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
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.
/public/v1/contactsBody
| Field | Type | Description |
|---|---|---|
contactsrequired | Contact[] | Array of contacts to upsert. At least one entry is required. |
contacts[].namerequired | string | Display name for the contact. |
contacts[].countryCoderequired | string | Country dialing code. May include a leading +; non-digits are ignored. Example: +91. |
contacts[].numberrequired | string | Local phone number, digits only. The country code is prepended internally. |
contacts[].segments | string[] | Optional list of segment names. Missing segments are created on the fly. Names are normalized (lowercased, spaces become underscores). |
Request
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
{
"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.
/public/v1/templatesQuery parameters
| Field | Type | Description |
|---|---|---|
status | string | string[] | Filter by template status. Repeat the parameter or pass a comma-separated list. Allowed values: DRAFT, PENDING, APPROVED, REJECTED, STALE. |
channel | string | Filter by channel. One of whatsapp or email. |
search | string | Case-insensitive substring match against the template name. Max 120 characters. |
page | number | 1-indexed page number. Defaults to 1. |
limit | number | Page size, between 1 and 100. Defaults to 20. |
Request
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
{
"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.
/public/v1/campaigns/triggerBody
| Field | Type | Description |
|---|---|---|
templateIdrequired | string | ID of an APPROVED template that belongs to your workspace. |
segmentsrequired | string[] | One or more existing segment names. Unknown segments are returned in the response under unknownSegments and are skipped. |
Request
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
{
"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
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.