# Template Approval Process

> Understand how WhatsApp templates are reviewed and approved by Meta

All WhatsApp templates must be approved by Meta before they can be used to send messages. This guide explains the approval process, common rejection reasons, and how to track template status.

## Approval Workflow

```mermaid
stateDiagram-v2
    [*] --> draft: Create template
    draft --> pending: Submit for approval
    pending --> approved: Meta approves
    pending --> rejected: Meta rejects
    rejected --> draft: Edit and fix
    approved --> [*]: Ready to use
```

## Submitting for Approval

### Via Dashboard

1. Go to **Senders** > Select sender > **Templates** tab
2. Find the template with "Draft" status
3. Click the **...** menu and select **Submit for Approval**
4. The status will change to "Pending"

### Via API

```typescript
// Submit a draft template for approval
await zavu.templates.submit({
  templateId: 'tmpl_abc123',
});
```

```bash
curl -X POST https://api.zavu.dev/v1/templates/tmpl_abc123/submit \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY"
```

## Sample Values for Variables

Meta reviews a template against a sample value for each body variable. Send them as `variableExamples` when you submit: key a named variable (`{{customer_name}}`) by its name, and a positional one (`{{1}}`, `{{2}}`) as `var1`, `var2`.

```bash
curl -X POST https://api.zavu.dev/v1/templates/tmpl_abc123/submit \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "senderId": "sender_abc123",
    "category": "UTILITY",
    "variableExamples": { "var1": "Maria", "var2": "ORD-12345" }
  }'
```

Any variable you leave out gets a generic placeholder such as `Sample var1`, which Meta reviews less favourably. Send a realistic value for every variable. The SDKs do not include this field yet, so use the REST endpoint for it.

## Template Status

| Status | Description | Can Send? |
|--------|-------------|-----------|
| `draft` | Not yet submitted to Meta | No |
| `pending` | Submitted, awaiting Meta review | No |
| `approved` | Approved by Meta | Yes |
| `rejected` | Rejected by Meta | No |

## Approval Timeline

- **Typical review time:** 24-48 hours
- **Complex templates:** Up to 72 hours
- **Peak periods:** May take longer during high volume

> **Info:**
> Meta reviews templates for compliance with their [Business Messaging Policy](https://www.whatsapp.com/legal/business-policy/). Ensure your templates follow these guidelines before submitting.

## Tracking Status Changes

### Webhooks

Configure webhooks to receive real-time notifications when template status changes:

```json
{
  "id": "evt_1702000000000_abc123",
  "type": "template.status_changed",
  "timestamp": 1702000000000,
  "senderId": "sender_xyz",
  "projectId": "proj_abc",
  "data": {
    "templateId": "tmpl_123",
    "name": "order_confirmation",
    "previousStatus": "pending",
    "currentStatus": "approved",
    "rejectionReason": null
  }
}
```

**Enable the webhook event:**
1. Go to **Senders** > Select sender > **Webhooks** tab
2. Add `template.status_changed` to your webhook events

A template belongs to the WhatsApp Business Account, not to one sender, so when
several senders share that account the event is delivered **once**, to a single
sender: the first one, by creation date, that has an active webhook configured.
Configure the webhook on the sender you use and it will arrive there. If two
senders on the same account point at different endpoints, only the older of the
two receives it — split them across accounts if both need to know.

### Polling

Check template status via API:

```typescript
const template = await zavu.templates.retrieve({
  templateId: 'tmpl_abc123'
});

console.log('Status:', template.whatsappStatus);
if (template.rejectionReason) {
  console.log('Rejection reason:', template.rejectionReason);
}
```

### Syncing from WhatsApp

Two cases the webhook does not cover: a template created outside Zavu (in Meta
Business Manager, or by another tool) never appears in Zavu, and a missed
`template.status_changed` webhook leaves a template stuck in `pending`.

`POST /v1/templates/sync` reconciles both. It imports Meta templates Zavu does
not have — or links them to an existing template with the same name — and
refreshes the status of the ones it already knows about.

```bash
curl -X POST https://api.zavu.dev/v1/templates/sync \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json
{
  "accountsSynced": 1,
  "imported": 2,
  "linked": 1,
  "updated": 3,
  "skipped": 12,
  "errors": []
}
```

Every WhatsApp sender in the project is synced by default; pass
`{"senderId": "sender_xyz"}` to sync just one. The call waits for Meta, so it
takes a few seconds per account — list your templates right after it returns.
Templates that Meta reports as rejected or disabled are not imported and are
counted in `skipped`.

## Common Rejection Reasons

### Content Policy Violations

> **Warning:**
> Templates that violate Meta's policies will be rejected. Common violations include:
> - Misleading content
> - Prohibited products/services
> - Spammy language
> - Missing opt-out information for marketing

### Formatting Issues

| Issue | Solution |
|-------|----------|
| Invalid variable format | Use `{{1}}`, `{{2}}` format |
| Too many variables | Limit to 10 variables per component |
| Variable in wrong position | Variables can't be at start of template |
| Button URL without variable | Dynamic URLs must include variables |

### Category Mismatch

Templates may be rejected if the category doesn't match the content:

- **UTILITY** templates shouldn't contain promotional content
- **MARKETING** templates need clear opt-out language
- **AUTHENTICATION** templates should only contain OTP/verification content

Rejection is not the only outcome. More often Meta **recategorizes** the
template instead — typically `UTILITY` to `MARKETING` — and approves it under
the new category. It can also recategorize a template months after approval.
Either way the template keeps working and every send on it is billed at the new
category's rate.

`template.category` reports the category Meta currently assigns, not the one
you submitted. A recategorization is delivered as a `template.status_changed`
webhook carrying `data.category`, and it fires even when the approval status
did not move — so `previousStatus` and `currentStatus` can be identical on an
event that changes what you pay. Branch on `category`, not only on
`currentStatus`.

To reconcile templates that were recategorized before you were listening, run
`POST /v1/templates/sync`; it refreshes the category alongside the status.

### Language Issues

- Template body must match the declared language
- Special characters may cause issues in some languages
- Emojis should be used sparingly

## Fixing Rejected Templates

When a template is rejected:

1. **Check the rejection reason** in the dashboard or API response
2. **Edit the template** to fix the issues
3. **Resubmit** for approval

> **Note:**
> A template's content cannot be edited in place. Meta reviews a name plus a
> body, so fixing a rejection means creating a new template and submitting that.
> Delete the rejected one once the replacement is approved.

```typescript
// Create the corrected template
const fixed = await zavu.templates.create({
  name: 'order_confirmation_v2',
  language: 'en',
  body: 'Updated message content that addresses the rejection reason',
  whatsappCategory: 'UTILITY',
});

// Submit it to the WhatsApp Business Account on this sender
await zavu.templates.submit(fixed.id, { senderId: 'sender_abc123' });

// Once the replacement is approved, drop the rejected one
await zavu.templates.delete('tmpl_abc123');
```

> **Note:**
> Editing an approved template will reset its status to "draft" and require re-approval.

## Best Practices for Approval

  - **Be Clear and Specific**: Vague templates are more likely to be rejected. Be specific about the purpose.
  - **Match Category to Content**: Ensure your template category accurately reflects the message type.
  - **Avoid Promotional Language in UTILITY**: UTILITY templates should be purely transactional without marketing content.
  - **Include Opt-Out for MARKETING**: Marketing templates should include clear unsubscribe instructions.

## Template Examples

### Good UTILITY Template

```
Hi {{1}}, your order #{{2}} has shipped!

Tracking number: {{3}}
Estimated delivery: {{4}}

Track your package: https://example.com/track/{{2}}
```

### Good MARKETING Template

```
Hi {{1}}!

We have a special offer just for you: {{2}}% off your next purchase!

Use code: {{3}}
Valid until: {{4}}

Reply STOP to unsubscribe.
```

### Good AUTHENTICATION Template

```
Your verification code is {{1}}.

This code expires in 10 minutes. Do not share this code with anyone.
```

## Rate Limits

| Action | Limit |
|--------|-------|
| Submit templates | 100/minute |
| Templates per WABA | 250 total |

## Next Steps

  - [Send Template Messages](https://www.zavu.dev/docs/guides/whatsapp/templates/sending): Learn how to send messages using approved templates
  - [Configure Webhooks](https://www.zavu.dev/docs/guides/receiving-messages/webhooks): Set up webhooks to receive template status updates
