# 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:

1. Connect a WhatsApp Business Account and a number of your own. See [Phone number setup](/docs/guides/whatsapp/phone-number-setup).
2. Author a template with category `marketing` and submit it for review. See [Template guidelines](/docs/knowledge-base/whatsapp/template-guidelines) for what passes.
3. 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`](/docs/api/errors/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:

<!-- bird:tabs typescript,python,go,php,cli,curl -->

```typescript
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);
```

```python
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)
```

```go
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)
}
```

```php
$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();
```

```cli
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
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"
              }
            ]
          }
        ]
      }
    }'
```

<!-- /bird:tabs -->

- **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](/docs/knowledge-base/whatsapp/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](/docs/guides/whatsapp/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](/docs/guides/whatsapp/sending-whatsapp#cost-and-billing) for when a send is charged, and [WhatsApp pricing](https://bird.com/pricing/whatsapp) 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](/docs/guides/whatsapp/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`](/docs/api/errors/E15023) `WhatsAppSenderWABAMismatch`.

## Next steps

- [WhatsApp templates](/docs/guides/whatsapp/templates): browsing the catalog and the shared send-by-template contract
- [Marketing templates](/docs/knowledge-base/whatsapp/marketing-templates): the Marketing Messages API, onboarding status, and where marketing is limited
- [Opt-outs](/docs/guides/whatsapp/opt-outs): recording and reading suppressions and preferences
- [Utility templates](/docs/guides/whatsapp/templates/utility): order updates, appointment reminders, and account notices