Appearance
Send Template Message
Send a pre-approved WhatsApp message template. Templates can be sent outside the 24-hour messaging window.
POST /api/v1/messages/send
Request
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer YOUR_API_KEY |
Content-Type | Yes | application/json |
X-TENANT-ID | No | Tenant ID (if multi-tenant) |
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
whatsapp_account_id | string | Yes | WhatsApp account ID to send from — Dashboard → WhatsApp Numbers → API ID: |
phone | string | Yes | Recipient phone number with country code |
message_type | string | Yes | Must be template |
data | object | Yes | Template data object |
data.name | string | Yes | Template name (e.g., order_confirmation) |
data.language_code | string | No | Template language code (default: en_US) |
data.components | array | No | Meta components array with template parameters. Required when the template has an image/video/document header — pass a header component with the media link or id, otherwise the API returns TEMPLATE_HEADER_MEDIA_REQUIRED (422). |
scheduled_at | string | No | ISO 8601 datetime for delayed send |
timezone | string | No | IANA timezone (required when scheduled_at is set) |
auto_retry_on_frequency_cap | boolean | No | Opt-in 26-hour auto-retry when Meta rejects the message with error 131049 (per-recipient marketing frequency cap). The platform automatically creates a single scheduled retry — never more, to prevent loops. Recommended true for marketing templates, false for transactional ones. Default: false. |
Header media on send
If the template was created with an image, video, or document header, every send MUST include a header component carrying the actual media for that recipient. You can:
- Reuse the template's default sample: fetch the template via
GET /api/v1/templatesand readheader_sample_media_url, then pass it as thelinkbelow. - Send recipient-specific media: upload it to public storage and pass that URL — or use a previously-uploaded Meta media
id.
json
"components": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "link": "https://example.com/banner.jpg" } }
]
},
{ "type": "body", "parameters": [ {"type": "text", "text": "ORD-1234"} ] }
]Frequency-cap auto-retry (131049)
Meta enforces a per-recipient cap on marketing messages: a user can only receive a small number of marketing-category messages from businesses in any 24-hour window. When the cap is hit, Meta rejects further sends with error 131049 — not a technical bug, a policy. The retry option converts this silent failure into a deferred delivery:
- On
131049failure withauto_retry_on_frequency_cap: true, the platform creates a scheduled message atnow + 26 hours(24h Meta window + 2h safety margin) carrying the same recipient + template + params. - That retry runs through the normal scheduled-message cron; you can find it in the dashboard's Sent page with
retry_count = 1. - The retry itself runs with
auto_retry_on_frequency_cap = falseandretry_count = 1— a single retry per chain, no loops even if Meta rejects it again.
Example Request
bash
curl -X POST https://cubeconnect.io/api/v1/messages/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"whatsapp_account_id": "YOUR_WHATSAPP_ACCOUNT_ID",
"phone": "+966501234567",
"message_type": "template",
"data": {
"name": "order_confirmation",
"language_code": "en_US",
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": "ORD-1234"},
{"type": "text", "text": "500 SAR"}
]
}
]
}
}'php
$response = Http::withToken('YOUR_API_KEY')
->post('https://cubeconnect.io/api/v1/messages/send', [
'whatsapp_account_id' => env('CUBECONNECT_WHATSAPP_ACCOUNT_ID'),
'phone' => '+966501234567',
'message_type' => 'template',
'data' => [
'name' => 'order_confirmation',
'language_code' => 'en_US',
'components' => [
[
'type' => 'body',
'parameters' => [
['type' => 'text', 'text' => 'ORD-1234'],
['type' => 'text', 'text' => '500 SAR'],
],
],
],
],
]);javascript
const response = await fetch('https://cubeconnect.io/api/v1/messages/send', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
whatsapp_account_id: process.env.CUBECONNECT_WHATSAPP_ACCOUNT_ID,
phone: '+966501234567',
message_type: 'template',
data: {
name: 'order_confirmation',
language_code: 'en_US',
components: [
{
type: 'body',
parameters: [
{ type: 'text', text: 'ORD-1234' },
{ type: 'text', text: '500 SAR' },
],
},
],
},
}),
})python
import requests
response = requests.post(
'https://cubeconnect.io/api/v1/messages/send',
headers={
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
json={
'whatsapp_account_id': 'YOUR_WHATSAPP_ACCOUNT_ID',
'phone': '+966501234567',
'message_type': 'template',
'data': {
'name': 'order_confirmation',
'language_code': 'en_US',
'components': [
{
'type': 'body',
'parameters': [
{'type': 'text', 'text': 'ORD-1234'},
{'type': 'text', 'text': '500 SAR'},
],
},
],
},
},
)Response
Success 202 Accepted
json
{
"success": true,
"data": {
"status": "queued",
"message_log_id": 4522,
"conversation_category": "MARKETING",
"cost": 0.0
}
}The conversation_category is determined automatically based on the template's category in Meta Business Manager.
Template Parameters
Templates use positional placeholders like 1, 2, etc. The components array follows the Meta WhatsApp API format.
Example Template
Template name: order_confirmationTemplate body:
Your order {{1}} has been confirmed.
Total amount: {{2}}.
Thank you for your purchase!API call components:
json
{
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": "ORD-1234"},
{"type": "text", "text": "500 SAR"}
]
}
]
}Result sent to customer:
Your order ORD-1234 has been confirmed.
Total amount: 500 SAR.
Thank you for your purchase!Template Categories
| Category | Description | When to Use |
|---|---|---|
MARKETING | Promotional content | Sales, offers, product announcements |
UTILITY | Transactional updates | Order confirmations, shipping updates, account alerts |
AUTHENTICATION | OTP and verification | Login codes, 2FA, verification |
Template Without Parameters
If the template has no dynamic parameters, omit the components field:
json
{
"phone": "+966501234567",
"message_type": "template",
"data": {
"name": "welcome_message",
"language_code": "en_US"
}
}Common Errors
| Error | Cause |
|---|---|
| Template not found | The template name doesn't exist or hasn't been approved |
| Parameter count mismatch | The number of parameters in components doesn't match the template's placeholders |
| Template paused | Meta has paused the template due to quality issues |
Template Approval
Templates must be approved by Meta before they can be sent. Create and manage templates from the CubeConnect dashboard under Templates.