Outbound Notifications API
Overview
The Outbound Notifications API, a Talkdesk Digital Engagement™ solution, allows you to send notification messages at scale through multiple channels, utilizing various message types. You can proactively reach your customers by sending messages via WhatsApp or SMS to multiple recipients in a single batch request.
Use CaseConsider the case where you need to send bulk notifications to your customers — such as order confirmations, appointment reminders, or promotional messages. You can use the Outbound Notifications API to send messages from a WhatsApp or SMS sender you control. For WhatsApp, each message can be sent as free-form content or using pre-approved templates. For SMS, messages are sent as plain text. Each message can target multiple destinations in a single request.
Outbound Notifications API
The Outbound Notifications API lets clients send outbound notification messages in bulk through Talkdesk's outbound messaging service. To send messages, provide the channel, a sender identifier, and an array of messages, each with at least one destination. Content can be sent as plain text or via a pre-approved WhatsApp template, specified by name, language, and optional variables for personalization. Messages are trackable using a unique bulk request identifier and can be grouped with an optional identifier, name, and metadata. Outbound messages can also be linked to active Talkdesk conversations.
Channel support
The API supports the following channels:
WhatsApp — two message types:
- direct content messages
- template-based messages
- For contacts without reply in the last 24 hours, only WhatsApp template messages are permitted. See this process to register templates.
SMS — one message type:
- plain text messages via the
bodyfield
Sender configuration
| Channel | Sender requirement |
|---|---|
| Must be configured as a WhatsApp touchpoint in Channels App, following this process. | |
| SMS | SMS sender identifier. Must be a valid Talkdesk touchpoint (phone number) registered with the "PROACTIVE" use type. |
This operation is asynchronous. A successful call returns 202 Accepted with provider message identifiers and an initial delivery status.
Endpoint
POST https://api.talkdeskapp.com/outbound/notifications
- Method:
POST - Path:
/outbound/notifications - Operation ID:
SendOutboundNotificationMessage - Tag:
Outbound Messaging
Authentication
This endpoint requires the Draft security scheme with the following scope:
outbound-notifications:write
Request body
Content type: application/json
Top-level request schema:
| Field | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Channel used to send the notification. Allowed values: WHATSAPP, SMS. |
sender | string | Yes | Sender identifier applied to all messages in the request. |
messages | array | Yes | Messages to send. Minimum 1, maximum 500. The message schema depends on the channel (see below). |
options | object | No | Optional batch-level settings such as grouping metadata and conversation binding. See Request options below. |
messages[].destinations[] (common)
messages[].destinations[] (common)Each message must include a destinations array with at least one recipient. This schema is shared across all channels:
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Recipient destination identifier, such as a phone number. |
message_id | string | No | Optional client-provided message identifier for tracking. |
WhatsApp message format
When channel is WHATSAPP, each item in messages[] must provide exactly one of:
content— a direct content messagetemplate— a template-based message
| Field | Type | Required | Description |
|---|---|---|---|
destinations | array | Yes | Recipient list. See destinations[] above. |
content | object | Conditionally | Required when sending a direct content message. |
template | object | Conditionally | Required when sending a template-based message. |
Direct content messages
Due to Meta restrictions, this only applies to contact numbers that have sent messages to the touchpoint sender within the last 24 hours.
When using content, the payload shape follows the WhatsApp content model.
Text message body
{
"content": {
"body": {
"type": "TEXT",
"text": "Hello, how can I help you?"
}
}
}Supported direct body variants:
| Field | Type | Required | Notes |
|---|---|---|---|
content.body.type | string | Yes | TEXT. |
content.body.text | string | Depends | Required for TEXT. |
Template messages
The template must be approved by Meta. The sending success rate depends on the quality of the sender and the template.
The template's header (text, image, video or document), button types, and carousel card layout are now resolved automatically from the approved WhatsApp template configuration. You only need to send the dynamic values below, and every one of them is optional — a template with no placeholders at all still needs nothing but name and language (see the Example below).
When using a template, provide the WhatsApp template metadata:
| Field | Type | Required | Description |
|---|---|---|---|
template.name | string | Yes | Template name. |
template.language | string | Yes | Template language code, for example en. |
template.variables | object<string,string> | No | Optional template variables keyed by numeric string, such as "1" and "2". |
template.header_variables | object<string,string> | No | Variables for the template header, only used when the header is TEXT and contains a placeholder. Uses its own numeric keys, independent from variables. Omit entirely for IMAGE, VIDEO or DOCUMENT headers — the media is resolved automatically from the template's configuration and never needs to be sent — and for TEXT headers without a placeholder. |
template.buttons | array<object> | No | Dynamic values for the template's buttons, in the same order as the buttons that need one (URL buttons with a placeholder, and COPY_CODE/authentication buttons). Static buttons (PHONE_NUMBER, plain QUICK_REPLY) are resolved automatically and must be omitted from this array. Omit the whole array if no button needs a dynamic value — the common case. See template.buttons[] below. |
template.carousel | array<object> | No | One entry per carousel card, in the same order as the cards configured on the template. Required only for carousel templates; omit entirely otherwise. See template.carousel[] below. |
template.buttons[]
| Field | Type | Required | Description |
|---|---|---|---|
suffix | string | No | Dynamic value for the button — the URL's trailing placeholder for a URL button, or the one-time code for a COPY_CODE/authentication button. |
template.carousel[]
| Field | Type | Required | Description |
|---|---|---|---|
variables | object<string,string> | No | This card's body variables keyed by numeric string. |
buttons | array<object> | No | Dynamic values for this card's buttons, following the same rules as template.buttons[] above. |
Here's the available language code for the template:
| Language | Code |
|---|---|
| Afrikaans | af |
| Arabic | ar |
| Arabic (UAE) | ar_AE |
| Arabic (EGY) | ar_EG |
| Arabic (LBN) | ar_LB |
| Arabic (MAR) | ar_MA |
| Arabic (QAT) | ar_QA |
| Azerbaijani | az |
| Belarusian | be_BY |
| Bulgarian | bg |
| Bengali | bn |
| Bengali (IND) | bn_IN |
| Catalan | ca |
| Czech | cs |
| Danish | da |
| German | de |
| German (AUT) | de_AT |
| German (CHE) | de_CH |
| Greek | el |
| English | en |
| English (UAE) | en_AE |
| English (AUS) | en_AU |
| English (CAN) | en_CA |
| English (UK) | en_GB |
| English (GHA) | en_GH |
| English (IRL) | en_IE |
| English (IND) | en_IN |
| English (JAM) | en_JM |
| English (MYS) | en_MY |
| English (NZL) | en_NZ |
| English (QAT) | en_QA |
| English (SGP) | en_SG |
| English (UGA) | en_UG |
| English (US) | en_US |
| English (ZAF) | en_ZA |
| Spanish | es |
| Spanish (ARG) | es_AR |
| Spanish (CHL) | es_CL |
| Spanish (COL) | es_CO |
| Spanish (CRI) | es_CR |
| Spanish (DOM) | es_DO |
| Spanish (ECU) | es_EC |
| Spanish (SPA) | es_ES |
| Spanish (HND) | es_HN |
| Spanish (MEX) | es_MX |
| Spanish (PAN) | es_PA |
| Spanish (PER) | es_PE |
| Spanish (URY) | es_UY |
| Estonian | et |
| Persian | fa |
| Finnish | fi |
| Filipino | fil |
| French | fr |
| French (BEL) | fr_BE |
| French (CAN) | fr_CA |
| French (CHE) | fr_CH |
| French (CIV) | fr_CI |
| French (MAR) | fr_MA |
| Irish | ga |
| Gujarati | gu |
| Hausa | ha |
| Hebrew | he |
| Hindi | hi |
| Croatian | hr |
| Hungarian | hu |
| Indonesian | id |
| Italian | it |
| Japanese | ja |
| Georgian | ka |
| Kazakh | kk |
| Kannada | kn |
| Korean | ko |
| Kyrgyz (Kyrgyzstan) | ky_KG |
| Lao | lo |
| Lithuanian | lt |
| Latvian | lv |
| Macedonian | mk |
| Malayalam | ml |
| Marathi | mr |
| Malay | ms |
| Norwegian | nb |
| Dutch | nl |
| Dutch (BEL) | nl_BE |
| Punjabi | pa |
| Polish | pl |
| Dari | prs_AF |
| Pashto | ps_AF |
| Portuguese (BR) | pt_BR |
| Portuguese (POR) | pt_PT |
| Romanian | ro |
| Russian | ru |
| Kinyarwanda | rw_RW |
| Sinhala | si_LK |
| Slovak | sk |
| Slovenian | sl |
| Albanian | sq |
| Serbian | sr |
| Swedish | sv |
| Swahili | sw |
| Tamil | ta |
| Telugu | te |
| Thai | th |
| Turkish | tr |
| Ukrainian | uk |
| Urdu | ur |
| Uzbek | uz |
| Vietnamese | vi |
| Chinese (CHN) | zh_CN |
| Chinese (HKG) | zh_HK |
| Chinese (TAI) | zh_TW |
| Zulu | zu |
Example:
{
"template": {
"name": "welcome_message",
"language": "en",
"variables": {
"1": "Talkdesk",
"2": "Support Team"
}
}
}Header variables (TEXT header with a placeholder):
{
"template": {
"name": "order_notification",
"language": "en",
"header_variables": {
"1": "Wireless Bluetooth Headset"
},
"variables": {
"1": "Jane",
"2": "ORD20260714"
}
}
}Media header (IMAGE, VIDEO or DOCUMENT — no header_variables needed, the media is resolved from the template configuration):
{
"template": {
"name": "video_header_reply",
"language": "en_US",
"variables": {
"1": "Tom",
"2": "meeting"
}
}
}Buttons (dynamic value for a URL button):
{
"template": {
"name": "promotion_offer",
"language": "en",
"variables": {
"1": "Jane"
},
"buttons": [
{ "suffix": "579852585149360" }
]
}
}Carousel template (one entry per card, in card order):
{
"template": {
"name": "product_carousel",
"language": "en",
"variables": {
"1": "Jane"
},
"carousel": [
{ "variables": { "1": "Giraffe plush", "2": "$29.99" } },
{ "variables": { "1": "Zebra plush", "2": "$19.99" } }
]
}
}Authentication template (COPY_CODE button, uses buttons[].suffix for the one-time code):
{
"template": {
"name": "auth_verification_code",
"language": "en",
"variables": {
"1": "482913"
},
"buttons": [
{ "suffix": "482913" }
]
}
}WhatsApp example request
{
"channel": "WHATSAPP",
"sender": "+123456789",
"messages": [
{
"destinations": [
{
"to": "+987654321",
"message_id": "client-msg-1"
},
{
"to": "+5551234567"
}
],
"template": {
"name": "welcome_message",
"language": "en",
"variables": {
"1": "Talkdesk",
"2": "Support Team"
}
}
}
],
"options": {
"bulk_id": "bulk-123",
"group_id": "campaign-42",
"group_name": "Spring promo",
"group_info": {
"source": "crm",
"segment": "vip"
},
"bind_conversation": {
"enabled": true,
"ttl_in_minutes": 1440
}
}
}SMS message format
When channel is SMS, each item in messages[] must include a body field with the plain text content:
| Field | Type | Required | Description |
|---|---|---|---|
destinations | array | Yes | Recipient list. See destinations[] above. |
body | string | Yes | The text content of the SMS message. Must not be empty. |
SMS does not support content or template fields. Use body only.
SMS example request
{
"channel": "SMS",
"sender": "+123456789",
"messages": [
{
"destinations": [
{
"to": "+987654321",
"message_id": "sms-msg-1"
}
],
"body": "Your order #12345 has been shipped. Track at: https://example.com/track/12345"
}
],
"options": {
"bulk_id": "sms-bulk-456",
"group_id": "shipping-notifications",
"group_name": "Order updates"
}
}Request options
The optional options object applies settings to the whole request:
| Field | Type | Required | Description |
|---|---|---|---|
bulk_id | string | No | Optional bulk identifier. If omitted, the service generates a UUID. |
group_id | string | No | Optional logical group identifier. If omitted, bulk_id is used. |
group_name | string | No | Optional logical group name. |
group_info | object<string, string> | No | Optional metadata map for the group. |
bind_conversation | object | No | Optional conversation binding settings (WhatsApp only). Has no effect for SMS channel. |
options.bind_conversation
options.bind_conversation| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | No | Whether sent messages should be bound to conversations. Default: false (WhatsApp only). |
ttl_in_minutes | integer | No | Time-to-live for the conversation binding, in minutes. Valid range: 30–21600 (30 minutes to 15 days). Only applicable when enabled is true (WhatsApp only). |
Success response
Status code:
202 Accepted
Response schema:
| Field | Type | Required | Description |
|---|---|---|---|
bulk_id | string | Yes | Unique bulk identifier for the accepted request. |
messages | array | Yes | Per-destination acceptance results. |
messages[] in the response
messages[] in the response| Field | Type | Description |
|---|---|---|
message_id | string | Unique provider message identifier. |
destination | string | Destination that the message was accepted for. |
status | string | Initial message status. Current documented value: PENDING. |
Example:
{
"bulk_id": "0f0d3c86-2355-4409-9968-ac871cdf88d0",
"messages": [
{
"message_id": "infobip-msg-id-123",
"destination": "+987654321",
"status": "PENDING"
}
]
}Error responses
| Status | Meaning | Example body |
|---|---|---|
400 | Bad Request | {"code":"2140001","message":"Bad Request"} |
401 | Unauthorized | {"code":"2140002","message":"Unauthorized"} |
403 | Forbidden | {"code":"2140003","message":"Forbidden"} |
500 | Internal Server Error | {"code":"2140007","message":"Internal Server Error"} |
Notes
- The request is batch-oriented: one call can submit up to
500messages. - The response confirms acceptance, not final delivery.
- A destination can include a client-side
message_idfor correlation. - Message grouping and conversation binding are controlled at the request level through
options. - WhatsApp messages are delivered via the Infobip provider. SMS messages are delivered via the Twilio provider.
- SMS does not support template-based messages or direct content messages (
content). Use thebodyfield for SMS. - The
bind_conversationoption only applies to WhatsApp. It has no effect for SMS.
Updated 20 days ago