WhatsApp marketing templates
A marketing template carries promotional content: awareness, sales, retargeting, or a coupon. It's the widest template category, the only one Meta will move a template into on its own initiative, and the one where a mishandled failure hurts your delivery rather than just failing one send.
Before you send
Bird's managed catalog stocks no marketing template at all, so a marketing send always uses a template your workspace authored, on a WhatsApp Business Account of your own:
- Connect a WhatsApp Business Account and a number of your own. See Phone number setup.
- Author a template with category marketing and submit it for review. See Template guidelines for what passes.
- Send from a number on the same WhatsApp Business Account as the template. from is required on a marketing send, and a sender on a different account is refused 422 E15023 WhatsAppSenderWABAMismatch before anything is charged.
Sending a marketing template
POST /v1/whatsapp/messages with from set and a template object naming your own slug:
const msg = await bird.whatsapp.send({
to: "+16505551234",
from: "+13125550101",
template: {
slug: "summer_sale",
language: "en",
components: [
{
type: "header",
parameters: [{ type: "image", url: "https://cdn.example.com/banners/summer.png" }],
},
{ type: "body", parameters: [{ type: "text", name: "first_name", text: "Pablo" }] },
{ type: "button", parameters: [{ type: "text", text: "SUMMER25" }] },
],
},
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+16505551234",
from_="+13125550101",
template="summer_sale",
language="en",
components=[
{
"type": "header",
"parameters": [{"type": "image", "url": "https://cdn.example.com/banners/summer.png"}],
},
{"type": "body", "parameters": [{"type": "text", "name": "first_name", "text": "Pablo"}]},
{"type": "button", "parameters": [{"type": "text", "text": "SUMMER25"}]},
],
)
print(msg.id, msg.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
name := "Pablo"
nameKey := "first_name"
banner := "https://cdn.example.com/banners/summer.png"
coupon := "SUMMER25"
components := []bird.WhatsAppMessageTemplateComponent{
{Type: "header", Parameters: &[]bird.WhatsAppMessageTemplateComponentParameter{{Type: "image", Url: &banner}}},
{Type: "body", Parameters: &[]bird.WhatsAppMessageTemplateComponentParameter{{Type: "text", Name: &nameKey, Text: &name}}},
{Type: "button", Parameters: &[]bird.WhatsAppMessageTemplateComponentParameter{{Type: "text", Text: &coupon}}},
}
msg, err := client.Whatsapp.Send(context.Background(), bird.WhatsappSendParams{
To: "+16505551234",
From: "+13125550101",
Template: "summer_sale",
Language: "en",
Components: components,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$components = [
(new WhatsAppMessageTemplateComponent())
->setType('header')
->setParameters([
(new WhatsAppMessageTemplateComponentParameter())->setType('image')->setUrl('https://cdn.example.com/banners/summer.png'),
]),
(new WhatsAppMessageTemplateComponent())
->setType('body')
->setParameters([
(new WhatsAppMessageTemplateComponentParameter())->setType('text')->setName('first_name')->setText('Pablo'),
]),
(new WhatsAppMessageTemplateComponent())
->setType('button')
->setParameters([
(new WhatsAppMessageTemplateComponentParameter())->setType('text')->setText('SUMMER25'),
]),
];
$message = $bird->whatsapp->send(
to: '+16505551234',
from: '+13125550101',
template: 'summer_sale',
language: 'en',
components: $components,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--to +16505551234 \
--from +13125550101 \
--template summer_sale \
--language en \
--components '[{"type":"header","parameters":[{"type":"image","url":"https://cdn.example.com/banners/summer.png"}]},{"type":"body","parameters":[{"type":"text","name":"first_name","text":"Pablo"}]},{"type":"button","parameters":[{"type":"text","text":"SUMMER25"}]}]'curl -X POST "https://us1.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+16505551234",
"from": "+13125550101",
"template": {
"slug": "summer_sale",
"language": "en",
"components": [
{
"type": "header",
"parameters": [
{
"type": "image",
"url": "https://cdn.example.com/banners/summer.png"
}
]
},
{
"type": "body",
"parameters": [
{
"type": "text",
"name": "first_name",
"text": "Pablo"
}
]
},
{
"type": "button",
"parameters": [
{
"type": "text",
"text": "SUMMER25"
}
]
}
]
}
}'- Body parameters are named, the same as utility. Every parameter carries a name, and order in the array carries no meaning.
- A coupon button's code is an ordinary text parameter, like the one on the button above. There's no separate coupon-code parameter type.
- A gif header takes a gif parameter, not video or image. Marketing is the only category that accepts an animated-GIF header.
- A carousel's values go on cards, not parameters, and a send has to supply exactly the number of cards the template was approved with.
Bird routes every marketing send to Meta's Marketing Messages API automatically; you don't opt in and there's no per-send switch. The business account's onboarding status with that API gates Meta's optimizations rather than delivery itself, with one exception: a gif header needs an onboarded account or the send fails at WhatsApp. See Marketing templates for the account state, what onboarding unlocks, and where marketing is limited by country.
Opt-outs
Marketing is the category where opt-out handling matters most: it's the one a recipient can turn off on its own, separately from every other message you send them. WhatsApp gives every recipient an "Offers and announcements" control on your business profile, and Meta enforces it on its side, accepting the send and then declining to deliver it. Check your own suppression list before sending, and treat a rising count of the 131050 failure below for one recipient as a reason to suppress them yourself, since a stop made inside WhatsApp never appears on that list. See Opt-outs for recording and reading both stores.
Cost
Marketing is the most expensive category, and the rate applies whichever API carried the send, Cloud API or Marketing Messages. See Cost and billing for when a send is charged, and WhatsApp pricing for rates.
Things to watch
- Meta recategorizes into marketing, never out of it, and that's a price change. A template Meta finds promotional in substance becomes marketing whatever category you submitted, and the send keeps going out at the new, higher price. There's no opt-out and no way to edit the category back; the remedy is a new template.
- 131049 is a delivery pause, not a rate limit you configured, and retrying makes it worse. Meta reports 131049 both for its blanket US pause and for a per-user marketing cap, and its own guidance is to wait roughly a day before resending. Resending sooner can make the account unavailable to that recipient for longer and skews your own delivery rate. Bird reports the failure as rate_limited.
- 131050 means the recipient turned off "Offers and announcements", and it must never be retried. Meta accepts the send and then declines to deliver it. The correct response is the messaging-preferences path, not a resend: suppress the recipient yourself, or wait for them to re-enable delivery, which Bird learns about through the same preference mechanism that reported the stop. See Opt-outs.
- 132015 and 132016 are template pausing, not a recipient problem. 132015 is a pause for low quality; 132016 is permanent disabling after repeated pauses, and its only remedy is a new template with different content. Check the language's own status rather than the template's, since a paused language stops sending immediately.
- A sender on the wrong WhatsApp Business Account is refused before any charge. from has to sit on the same account as the template, or the send fails 422 E15023 WhatsAppSenderWABAMismatch.
Next steps
- WhatsApp templates: browsing the catalog and the shared send-by-template contract
- Marketing templates: the Marketing Messages API, onboarding status, and where marketing is limited
- Opt-outs: recording and reading suppressions and preferences
- Utility templates: order updates, appointment reminders, and account notices