Broadcast to customers
Send notifications to multiple customers immediately—order confirmations, system alerts, flash sales, or urgent updates. A broadcast accepts one or many recipients and sends through SMS or email. Every accepted recipient gets an individual Chime that you can track after execution.
Don't use Chimes for verification codes. If you need to authenticate or verify users with one-time passwords, use the OTP API instead. The OTP API handles token generation, expiry, validation, and rate limiting—critical security features that Chimes doesn't provide. See Verify customers with OTP to learn how to implement secure authentication flows.
How broadcasting works
Broadcasts begin processing accepted recipients immediately. The API validates message structure, resolves saved customers, and validates inline phone numbers and email addresses before accepting the request. A malformed or unresolved recipient rejects the entire broadcast; later transmission failures remain isolated to the affected recipient.
Recipients receive messages through SMS or email. Inline recipients use type plus a matching phone.number or email.address; saved-customer recipients use transport plus customer_id. All recipients in a single broadcast must resolve to the same transport.
Email broadcasts use either a top-level email object or a stored email message_template object. SMS broadcasts use message_template as raw SMS text or as a stored SMS template object. Do not include both email and message_template for the same broadcast, and keep the stored template channel aligned with the recipient transport.
{
"recipients": [
{ "customer_id": "cu_gloria_k", "transport": "email" },
{
"type": "email",
"email": {
"address": "[email protected]"
}
}
],
"email": {
"subject": "Your order has shipped",
"text": "Your order has shipped. Track it at https://yourstore.example/track/or_12345.",
"from": {
"address": "[email protected]"
}
},
"sender": "YourStore"
}
Broadcast a notification
Call POST /chimes/broadcast with your message content and recipient list. The API returns a broadcast ID immediately and begins delivery within seconds.
sender is optional, but you should provide it explicitly because the broadcast endpoint applies no default when it is omitted.
Broadcast notification
curl https://api.zebo.dev/chimes/broadcast \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": [
{ "type": "phone", "phone": { "number": "+233544998605" } },
{ "type": "phone", "phone": { "number": "+233501234567" } },
{ "type": "phone", "phone": { "number": "+233208765432" } }
],
"message_template": "Your order #12345 has shipped! Track at https://shop.com/track/abc123 - estimated delivery: tomorrow by 5PM.",
"sender": "ShopBrand",
"purpose": "order_notification",
"request_meta": {
"idempotency_key": "order_12345_shipped"
}
}'
The response confirms your broadcast with an ID, recipient count, and creation timestamp. Store the broadcast ID if you need to track delivery later or debug failed recipients.
Recipient validation
Commerce resolves and validates every recipient during request processing. Inline phone and email contacts must be well formed, while saved-customer references must exist and contain a valid contact for the selected transport. If any recipient fails these checks, correct the recipient and submit the broadcast again.
Acceptance does not guarantee later delivery. After execution, transmission failures are recorded per recipient and do not change whether the other accepted recipients can be sent.
Delivery timing
Broadcasts begin processing after the request is accepted, but the API does not guarantee an exact transmission or delivery time. Allow for processing and network delays.
If you need to send at a specific future time, use scheduled Chimes instead. For time-critical notifications like password resets or purchase confirmations, broadcasts deliver faster than scheduled sends.
Broadcast to many recipients
Broadcast one message to thousands of recipients with a single API call. Each recipient receives an individual Chime—no shared message threads or group texts. Track each delivery independently through the Chime API.
Broadcast campaign
curl https://api.zebo.dev/chimes/broadcast \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": [
{ "type": "phone", "phone": { "number": "+233544998605" } },
{ "type": "phone", "phone": { "number": "+233501234567" } },
{ "type": "phone", "phone": { "number": "+233208765432" } },
{ "type": "phone", "phone": { "number": "+233277654321" } },
{ "type": "phone", "phone": { "number": "+233248888888" } }
],
"message_template": "FLASH SALE! 50% off everything for next 2 hours. Shop: https://shop.example.com/flash Use code: FLASH50",
"sender": "ShopBrand",
"purpose": "marketing_campaign"
}'
The API currently enforces that recipients contains at least one item but does not publish a fixed maximum. Apply your own bounded batch size and monitor each broadcast independently.
Managing broadcasts
Tracking execution and delivery
Check broadcast status, view delivered message IDs, and identify failed recipients using POST /broadcasts/lookup. Returns the broadcast details plus IDs of all created Chimes after execution. The errors array includes failure reasons for any recipients that didn't receive messages.
Track broadcast status
curl https://api.zebo.dev/broadcasts/lookup \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"broadcast_id": "brc_kPvqTrqGsopu07wfC7ttoWqmfwt48ZW7BGvYUWk"
}'
Before execution, executed_at is absent and chime_ids may be omitted. After execution, chime_ids, when present, contains IDs of successfully created Chimes. Use Lookup a Chime with these IDs to check individual delivery status and transmission details. The optional errors array lists per-recipient execution failures.
Use this to debug broadcast campaigns: if 485 out of 500 messages delivered, the errors array tells you exactly which 15 recipients failed and why. Correct the invalid addresses and create a new broadcast for just those recipients.
Canceling pending broadcasts
Cancel broadcasts before execution using POST /broadcasts/cancel. Since broadcasts execute immediately, cancellation only works if called within seconds of creation before execution begins. Returns an error if the broadcast already executed or was previously canceled.
Cancel broadcast
curl https://api.zebo.dev/broadcasts/cancel \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cancel-broadcast-kPvqTrqGsopu07wf" \
-d '{
"broadcast_id": "brc_kPvqTrqGsopu07wfC7ttoWqmfwt48ZW7BGvYUWk"
}'
Cancellation only works on pending broadcasts. Repeating a new cancellation after the broadcast is canceled returns an error. A network retry is safe only when it reuses the same Idempotency-Key, which replays the original cancellation result.
Using idempotency keys
Prevent duplicate broadcasts during network failures or retries by providing request_meta.idempotency_key or the Idempotency-Key header. If your server crashes after broadcasting but before recording the broadcast ID, retry with the same key—the API returns the original broadcast instead of creating a duplicate.
const idempotencyKey = 'ship_notif_order_12345'
const payload = {
recipients: customerPhones,
message_template: 'Your order has shipped',
sender: 'ShopBrand',
request_meta: {
idempotency_key: idempotencyKey,
},
}
async function createBroadcast() {
const response = await fetch('https://api.zebo.dev/chimes/broadcast', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.COMMERCE_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify(payload),
})
if (!response.ok) {
throw new Error(`Commerce API request failed: ${response.status}`)
}
return response.json()
}
// First attempt succeeds, but the connection fails before the ID is saved.
await createBroadcast()
// Retrying the identical request with the same key returns the first broadcast.
const { broadcast } = await createBroadcast()
// Save broadcast ID to your database
await db.orders.update(orderId, {
notification_broadcast_id: broadcast.id,
})
Construct idempotency keys from stable identifiers like order IDs, transaction IDs, or event IDs plus the notification type—ship_notify_order_${orderId} or payment_confirm_txn_${txnId}.
Next steps
- Chime API Reference — Complete endpoint documentation including broadcast management
- Send scheduled notifications — Schedule notifications for future delivery
- Send customer notifications — Send individual notifications
- Product: Chime — Deep dive into Chime features and architecture