WhatsApp Template Webhooks
Overview
You can subscribe to webhook events to receive real-time notifications when WhatsApp message templates change. These webhooks notify you when:
- A template is created -- A new WhatsApp message template draft is added
- A template is updated -- A template's approval status, quality rating, or content changes
- A template is deleted -- A template is removed
This is particularly useful for monitoring Meta's template approval workflow and quality rating changes, which directly affect your WhatsApp messaging limits and delivery capabilities.
This page covers WhatsApp-specific template webhook details. For general information about template webhooks (including how to create subscriptions via the UI or API), see Template Webhooks.
Creating a WhatsApp Template Webhook
Template webhooks can be created through the Bird UI or the API. For step-by-step instructions, see Template Webhooks -- Creating a Webhook Subscription.
To receive only WhatsApp template events, add a platformId filter set to whatsapp:
Via the UI:
- Go to Developer App > Webhooks > Create webhook
- Select Templates as the service
- Choose your event (template.created, template.updated, or template.deleted)
- Add an event filter with key platformId and value whatsapp
Via the API:
Contoh kode
POST /workspaces/{workspaceId}/webhooks
{
"service": "templates",
"event": "template.updated",
"url": "https://your-server.com/webhooks/wa-templates",
"signingKey": "your-signing-key",
"eventFilters": [
{
"key": "platformId",
"value": "whatsapp"
}
]
}If you omit the platformId filter, you will receive template events for all platforms (WhatsApp, SMS, RCS, etc.). Add the filter if you only need WhatsApp events.
WhatsApp Template Lifecycle Events
WhatsApp templates go through a specific lifecycle governed by Meta's approval process. The template.updated event fires at each stage:
| Stage | Template status | platformInfo status | What happened |
|---|---|---|---|
| Draft created | draft | -- | Template created, not yet submitted |
| Submitted for approval | pending | pending | Template activated and sent to Meta for review |
| Approved by Meta | active | active | Meta approved the template; ready for sending |
| Rejected by Meta (all) | inactive | inactive | Meta rejected the template on all WABAs/locales |
| Rejected by Meta (partial) | active | inactive | Meta rejected on one WABA/locale, but another is still active |
| Quality rating changed | active | active | Meta updated the quality rating |
| Flagged by Meta | active | active | Template flagged due to low quality -- still usable but must improve before deadline or it will be disabled |
| Paused by Meta | active or inactive | inactive | Template paused on this WABA/locale; cannot send until Meta lifts it |
| Disabled by Meta | active or inactive | inactive | Template disabled after failing to improve quality while flagged |
| Reinstated by Meta | active | active | Previously flagged/paused/disabled template restored by Meta |
| In appeal | active or inactive | inactive | Rejected template is being appealed |
Partial status changes: When a template is submitted to multiple WABAs or locales, Meta manages each independently. If one WABA/locale is rejected, paused, or disabled while another remains approved, the overall template status stays active but the affected entry in platformInfo will show inactive. Always check the individual platformInfo entries rather than relying solely on the top-level status field.
Flagged vs. Paused vs. Disabled: A flagged template remains active in platformInfo -- it is a warning state and the template can still be used for sending. In contrast, paused and disabled templates show status: "inactive". To distinguish between paused, disabled, and rejected, use the Get ChannelTemplate endpoint and check the platformStatus field in the approvals array, which contains the detailed Meta status: whatsapp_flagged, whatsapp_paused, whatsapp_disabled, whatsapp_in_appeal, or whatsapp_reinstated.
WhatsApp-Specific Payload Fields
Template webhook payloads include a platformInfo object with WhatsApp-specific state. For WhatsApp, each key follows the format whatsapp:{wabaId}:{locale} -- for example, whatsapp:114128184961630:en-US.
platformInfo fields for WhatsApp
| Field | Type | Description |
|---|---|---|
| status | string | Approval status: draft, active, inactive, pending, pendingReview |
| category | string | WhatsApp category: UTILITY, MARKETING, or AUTHENTICATION |
| qualityRating | string | Quality rating: GREEN, YELLOW, RED, or UNKNOWN |
Example Payloads
Template approved by Meta
Contoh kode
{
"service": "templates",
"event": "template.updated",
"payload": {
"id": "34dcb086-82c7-47f2-8939-7f0057def64e",
"organizationId": "8a2f4e10-3b5c-4d6e-9f8a-1b2c3d4e5f6a",
"workspaceId": "7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
"projectId": "11323dfa-121f-4a4e-b4ef-35d325eaacb5",
"name": "welcome_message",
"language": "en",
"status": "active",
"platformInfo": {
"whatsapp:114128184961630:en": {
"status": "active",
"category": "MARKETING",
"qualityRating": "GREEN"
}
},
"createdAt": "2025-09-20T09:11:46.28Z",
"updatedAt": "2025-09-21T14:30:00.00Z"
}
}Quality rating degraded
This is critical to monitor -- quality degradation can lead to template pausing or messaging limit reductions.
Contoh kode
{
"service": "templates",
"event": "template.updated",
"payload": {
"id": "34dcb086-82c7-47f2-8939-7f0057def64e",
"organizationId": "8a2f4e10-3b5c-4d6e-9f8a-1b2c3d4e5f6a",
"workspaceId": "7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
"projectId": "11323dfa-121f-4a4e-b4ef-35d325eaacb5",
"name": "welcome_message",
"language": "en",
"status": "active",
"platformInfo": {
"whatsapp:114128184961630:en": {
"status": "active",
"category": "MARKETING",
"qualityRating": "YELLOW"
}
},
"createdAt": "2025-09-20T09:11:46.28Z",
"updatedAt": "2025-09-25T08:15:00.00Z"
}
}Template rejected by Meta (all WABAs)
When Meta rejects the template across all WABAs/locales, the overall template status becomes inactive.
Contoh kode
{
"service": "templates",
"event": "template.updated",
"payload": {
"id": "34dcb086-82c7-47f2-8939-7f0057def64e",
"organizationId": "8a2f4e10-3b5c-4d6e-9f8a-1b2c3d4e5f6a",
"workspaceId": "7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
"projectId": "11323dfa-121f-4a4e-b4ef-35d325eaacb5",
"name": "welcome_message",
"language": "en",
"status": "inactive",
"platformInfo": {
"whatsapp:114128184961630:en": {
"status": "inactive",
"category": "MARKETING",
"qualityRating": "UNKNOWN"
}
},
"createdAt": "2025-09-20T09:11:46.28Z",
"updatedAt": "2025-09-21T16:45:00.00Z"
}
}Template rejected by Meta (partial -- one WABA rejected, another still active)
When a template is submitted to multiple WABAs and Meta rejects it on one but not another, the overall template status remains active because there is still at least one active approval. The rejected WABA/locale entry in platformInfo shows inactive while the approved one shows active.
Contoh kode
{
"service": "templates",
"event": "template.updated",
"payload": {
"id": "34dcb086-82c7-47f2-8939-7f0057def64e",
"organizationId": "8a2f4e10-3b5c-4d6e-9f8a-1b2c3d4e5f6a",
"workspaceId": "7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
"projectId": "11323dfa-121f-4a4e-b4ef-35d325eaacb5",
"name": "welcome_message",
"language": "en",
"status": "active",
"platformInfo": {
"whatsapp:114128184961630:en": {
"status": "active",
"category": "MARKETING",
"qualityRating": "GREEN"
},
"whatsapp:998877665544332:en": {
"status": "inactive",
"category": "MARKETING",
"qualityRating": "UNKNOWN"
}
},
"createdAt": "2025-09-20T09:11:46.28Z",
"updatedAt": "2025-09-21T16:45:00.00Z"
}
}When handling template.updated events, always iterate through the individual platformInfo entries to check for rejections or quality changes. Do not rely only on the top-level status field -- it will remain active as long as at least one WABA/locale is still approved.
Template flagged by Meta (quality warning)
When Meta flags a template due to low quality, the platformInfo entry remains active but the qualityRating changes to RED. The template can still be used for sending, but if quality does not improve before Meta's deadline, it will be paused or disabled.
Contoh kode
{
"service": "templates",
"event": "template.updated",
"payload": {
"id": "34dcb086-82c7-47f2-8939-7f0057def64e",
"organizationId": "8a2f4e10-3b5c-4d6e-9f8a-1b2c3d4e5f6a",
"workspaceId": "7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
"projectId": "11323dfa-121f-4a4e-b4ef-35d325eaacb5",
"name": "welcome_message",
"language": "en",
"status": "active",
"platformInfo": {
"whatsapp:114128184961630:en": {
"status": "active",
"category": "MARKETING",
"qualityRating": "RED"
}
},
"createdAt": "2025-09-20T09:11:46.28Z",
"updatedAt": "2025-09-22T08:15:00.00Z"
}
}Note that status is still "active" even with qualityRating: "RED". Flagging is a warning -- the template is usable but at risk. To confirm the exact Meta status, call the Get ChannelTemplate endpoint and check the platformStatus field (e.g., whatsapp_flagged).
Template paused by Meta
When Meta pauses a template, the platformInfo entry changes to inactive. If other WABAs are still active, the overall template status remains active.
Contoh kode
{
"service": "templates",
"event": "template.updated",
"payload": {
"id": "34dcb086-82c7-47f2-8939-7f0057def64e",
"organizationId": "8a2f4e10-3b5c-4d6e-9f8a-1b2c3d4e5f6a",
"workspaceId": "7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
"projectId": "11323dfa-121f-4a4e-b4ef-35d325eaacb5",
"name": "welcome_message",
"language": "en",
"status": "active",
"platformInfo": {
"whatsapp:114128184961630:en": {
"status": "active",
"category": "MARKETING",
"qualityRating": "GREEN"
},
"whatsapp:998877665544332:en": {
"status": "inactive",
"category": "MARKETING",
"qualityRating": "RED"
}
},
"createdAt": "2025-09-20T09:11:46.28Z",
"updatedAt": "2025-09-22T08:15:00.00Z"
}
}In this payload, the second WABA (998877665544332) has been paused or disabled -- visible from status: "inactive". The webhook platformInfo does not distinguish between paused and disabled states. To determine the exact Meta status, call the Get ChannelTemplate endpoint and inspect the platformStatus field in the approvals array (whatsapp_paused or whatsapp_disabled).
Quality Ratings
WhatsApp assigns quality ratings to message templates based on how recipients interact with messages sent using those templates. Quality ratings directly impact your ability to send messages.
| Rating | Description |
|---|---|
| GREEN | High quality. Template is performing well with recipients. |
| YELLOW | Medium quality. Template quality is declining. Consider reviewing template content and targeting. |
| RED | Low quality. Template may be paused by Meta if quality does not improve. |
| UNKNOWN | Quality rating has not yet been assigned (e.g., newly approved template). |
When a template's quality rating drops to RED, Meta may automatically pause it, preventing you from sending messages with that template. Set up a template.updated webhook to proactively detect quality changes and take action before templates are paused.
Recommended actions by quality rating
| Rating | Recommended action |
|---|---|
| GREEN | No action needed. Continue using the template. |
| YELLOW | Review message content, targeting, and opt-in practices. Monitor for further degradation. |
| RED | Immediately review and revise the template. Consider pausing usage until quality improves. |
| UNKNOWN | Wait for Meta to assign a rating after sufficient message volume. |
WhatsApp Template Status Lifecycle
Overview
WhatsApp templates move through several states as they are created, submitted to Meta for review, and monitored for quality. The webhook platformInfo.status provides a simplified view (active, inactive, pending), while the full details -- including the exact Meta status and rejection reasons -- are available via the Get ChannelTemplate API.
Lifecycle diagram
Contoh kode
┌─────────────────────────────┐
│ draft │
└──────────┬──────────────────┘
│ activate / submit
▼
┌─────────────────────────────┐
│ pending │
└──────┬──────────────┬───────┘
approved │ │ rejected
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ active │ │ inactive │
│ (sending allowed) │ │ (rejected by Meta) │
└──┬──────────────┬───┘ └──────────┬───────────┘
│ │ │
flagged │ │ paused/disabled │ appeal
(quality │ │ ▼
warning) │ │ ┌─────────────────────┐
▼ ▼ │ inactive │
┌──────────────┐ ┌────────────┐ │ (in appeal) │
│ active │ │ inactive │ └─────────┬───────────┘
│ (flagged) │ │ (paused/ │ │
│ still usable │ │ disabled) │ approved │
└──┬────────┬──┘ └─────────────┘ │
│ │ ▼
reinstated │ │ deadline passed back to active
▼ ▼
active inactive
(restored) (paused → disabled)
Template-level statuses (top-level status)
The top-level status on the webhook payload reflects the overall template state across all WABAs and locales.
| Status | Can send? | Description |
|---|---|---|
| draft | No | Template created but not yet submitted to Meta |
| pending | No | Template submitted to Meta for review |
| active | Yes | Approved by Meta on at least one WABA/locale |
| inactive | No | Rejected, paused, or disabled on all WABAs/locales |
| pendingReview | No | Pending internal Bird approval (when approval flows are configured) |
A top-level status of active does not mean all WABAs/locales are healthy. Individual entries in platformInfo may be inactive (rejected, paused, or disabled) while others remain active.
Platform-level statuses (platformInfo.status in webhooks)
The webhook platformInfo only exposes a simplified status per WABA/locale:
| platformInfo status | Meaning |
|---|---|
| active | Approved or flagged (template can still be used for sending) |
| inactive | Rejected, paused, or disabled (cannot send on this WABA) |
| pending | Submitted to Meta, awaiting review |
Because inactive covers multiple distinct Meta states, use the Get ChannelTemplate API to distinguish between them.
Detailed platform statuses (platformStatus via Get ChannelTemplate API)
The platformStatus field on each approval in the platformContent[].approvals[] array provides the exact Meta status.
| platformStatus | Maps to | Can send? | Description |
|---|---|---|---|
| whatsapp_approved | active | Yes | Approved by Meta for this WABA/locale |
| whatsapp_flagged | active | Yes | Flagged for low quality; must improve before Meta's deadline or it will be paused |
| whatsapp_reinstated | active | Yes | Previously flagged/paused/disabled, now restored by Meta |
| whatsapp_rejected | inactive | No | Rejected by Meta during review |
| whatsapp_paused | inactive | No | Temporarily paused by Meta due to quality issues |
| whatsapp_disabled | inactive | No | Disabled after failing to improve quality while flagged/paused |
| whatsapp_in_appeal | inactive | No | Rejected template currently under appeal |
Getting rejection and status change details
The webhook payload does not include rejection reasons. When you receive a template.updated webhook where a platformInfo entry has status: "inactive", call the Get ChannelTemplate API to retrieve the full approval details:
Contoh kode
GET /workspaces/{workspaceId}/projects/{projectId}/channel-templates/{channelTemplateId}
Each approval in the response platformContent[].approvals[] array includes:
| Field | Type | Description |
|---|---|---|
| status | string | Approval status: pending, approved, rejected, blocked, deleted |
| platformStatus | string | Detailed Meta status (see table above) |
| reasonCode | string | Normalized rejection reason code (see table below), or null if not rejected |
| reasonDescription | string | Human-readable description from Meta, or null. Especially useful when reasonCode is unknown |
Rejection reason codes:
| reasonCode | Description |
|---|---|
| whatsapp_scam | Meta identified the template as a scam attempt |
| whatsapp_invalid_format | Invalid format (often a duplicate template name on the same WABA) |
| whatsapp_incorrect_category | Template content does not match the specified category |
| whatsapp_promotional | Rejected due to inappropriate promotional content |
| whatsapp_tag_content_mismatch | Submitted content does not match the tag |
| whatsapp_abusive_content | Rejected due to abusive or inappropriate content |
| whatsapp_fallback | Rejected due to fallback policy violation |
| whatsapp_none | No specific reason provided by Meta; check reasonDescription for details |
| unknown | Unrecognized reason; check reasonDescription for the raw platform reason |
Example: approval entry for a rejected template
Contoh kode
{
"approvalReference": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"platformAccountIdentifier": "114128184961630",
"platform": "whatsapp",
"status": "rejected",
"platformStatus": "whatsapp_rejected",
"reasonCode": "whatsapp_incorrect_category",
"reasonDescription": "The content of this template does not match the selected category. Please update the category or the content.",
"channelIds": ["c1d2e3f4-a5b6-7890-cdef-123456789abc"]
}reasonCode and reasonDescription are also populated for paused and disabled templates (not just rejections). For paused templates, reasonCode is typically whatsapp_none and reasonDescription may contain Meta's explanation of the quality issue.
Complete Example
1. Subscribe to WhatsApp template updates
Contoh kode
POST /workspaces/{workspaceId}/webhooks
{
"service": "templates",
"event": "template.updated",
"url": "https://your-server.com/webhooks/wa-templates",
"signingKey": "my-secret-signing-key",
"eventFilters": [
{
"key": "platformId",
"value": "whatsapp"
}
]
}2. Handle incoming webhooks
Your webhook endpoint should:
- Verify the signature using the signingKey you provided
- Parse the payload and iterate through every platformInfo entry
- Detect the state for each WABA/locale and take action:
| Condition | Action |
|---|---|
| All platformInfo entries active, quality GREEN | Template fully approved and healthy -- enable sending on all WABAs |
| Any entry with qualityRating: "YELLOW" and status: "active" | Quality declining -- review template content and targeting before further degradation |
| Any entry with qualityRating: "RED" and status: "active" | Template flagged -- still usable but revise content urgently before Meta pauses it |
| Any platformInfo entry inactive | WABA paused, disabled, or rejected -- fetch details (see step 3) |
| Top-level status: "inactive" | All WABAs rejected, paused, or disabled -- stop sending; fetch details (see step 3) |
| Event is template.deleted | Stop using the template for sending |
Do not rely only on the top-level status. A status of active does not guarantee all WABAs/locales are healthy -- always iterate through platformInfo entries individually.
3. Fetch rejection and status details
When any platformInfo entry shows status: "inactive", call the Get ChannelTemplate API to find out exactly what happened and why:
Contoh kode
GET /workspaces/{workspaceId}/projects/{projectId}/channel-templates/{channelTemplateId}
In the response, find the matching approval in the platformContent[].approvals[] array by matching the platformAccountIdentifier (WABA ID) and check:
- platformStatus -- the exact Meta status (whatsapp_rejected, whatsapp_paused, whatsapp_disabled, or whatsapp_in_appeal)
- reasonCode -- a normalized reason code (e.g., whatsapp_incorrect_category, whatsapp_abusive_content)
- reasonDescription -- a human-readable explanation from Meta
For the full list of reasonCode values, see Rejection reason codes.
Example response (approval for a rejected WABA):
Contoh kode
{
"platformContent": [
{
"locale": "en",
"platform": "whatsapp",
"approvals": [
{
"approvalReference": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"platformAccountIdentifier": "998877665544332",
"platform": "whatsapp",
"status": "rejected",
"platformStatus": "whatsapp_rejected",
"reasonCode": "whatsapp_incorrect_category",
"reasonDescription": "The content of this template does not match the selected category.",
"channelIds": ["c1d2e3f4-a5b6-7890-cdef-123456789abc"]
}
]
}
]
}This follow-up call is also useful for paused and disabled templates. The reasonCode and reasonDescription fields are populated for all status changes, not just rejections.
Comparison with Channel Message Events
Template webhooks and channel message webhooks serve different purposes:
| Use case | Service | Event |
|---|---|---|
| Template approval/quality/lifecycle | templates | template.* |
| Outgoing message delivery status | channels | whatsapp.outbound |
| Incoming message received | channels | whatsapp.inbound |
| Message read/clicked/reaction | channels | whatsapp.interaction |
For message delivery tracking, see Message status and interactions.
Related Resources
- Template Webhooks -- Generic template webhooks guide (all platforms)
- Creating WhatsApp Message Templates -- Full guide for creating and activating templates
- Sending WhatsApp Messages -- How to send messages using approved templates
- Message Status and Interactions -- Track delivery status and recipient interactions
- Webhook Subscriptions -- Create and manage webhook subscriptions