WhatsApp Business API onboarding
Meta setup, phone verification, template approval, opt-in.
The end-to-end path
1. Meta Business Manager — you (the customer) create/verify your Business account and give us the Business ID. 2. WhatsApp Business Account (WABA) — we create a WABA under your Business ID via Embedded Signup, so tokens live in your assets, not ours. 3. Phone verification — a dedicated number (mobile, landline, or hosted). SMS/voice verification, then registration. 4. Display name approval — Meta reviews the sender display name; usually approved same-day. 5. Template messages — for anything outside a 24h session window. We draft them; Meta approves in 24–48h. 6. Opt-in capture — you need explicit opt-in for every recipient before sending template messages. We wire this into your forms.
What we handle for you
- Embedded Signup flow
- Webhook subscription (X-Hub-Signature verification, retry queue)
- Template drafting + submission
- Quality-rating monitoring (auto-pauses sends if rating drops to yellow)
- Rate-limit backoff and 24h window logic
Timeline
From kickoff to first production message: 5–7 business days for a standard setup.
Step-by-step: getting a template approved
1. Draft in our template studio — pick name, category (UTILITY / MARKETING / AUTHENTICATION), language, and components (header, body, footer, buttons).
2. Peer review against Meta's policy checklist: no promotional wording in UTILITY, every variable has a sample value, no URL shorteners in the body, media headers are hosted on a stable CDN, buttons stay within category limits.
3. Submit via the WhatsApp Business Management API: POST https://graph.facebook.com/v20.0/{waba-id}/message_templates.
4. Wait on the status webhook (message_template_status_update). Typical times: UTILITY ~2h, AUTHENTICATION ~4h, MARKETING 12–48h.
5. On APPROVED, tag the version in Git (templates/<name>/vN.json) and start routing new sends to it. The previous version stays live for a 7-day grace window.
6. On REJECTED, the webhook returns a rejection reason (policy category + free text). Fix it and resubmit as a new version — Meta does not allow editing a rejected template in place.
Example template payloads
1. Order update (UTILITY, header + body + footer)
{
"name": "order_update_v3",
"language": "en_US",
"category": "UTILITY",
"components": [
{ "type": "HEADER", "format": "TEXT", "text": "Order {{1}} update", "example": { "header_text": ["A1042"] } },
{ "type": "BODY", "text": "Hi {{1}}, your order {{2}} is now *{{3}}*. Estimated delivery: {{4}}.", "example": { "body_text": [["Nadia", "A1042", "out for delivery", "today, 6–8 pm"]] } },
{ "type": "FOOTER", "text": "Reply HELP for support." }
]
}
2. Appointment reminder (UTILITY, quick-reply buttons)
{
"name": "appointment_reminder_v2",
"language": "en_US",
"category": "UTILITY",
"components": [
{ "type": "BODY", "text": "Hi {{1}}, this is a reminder for your appointment tomorrow at {{2}}.", "example": { "body_text": [["Rifat", "4:30 pm"]] } },
{ "type": "BUTTONS", "buttons": [
{ "type": "QUICK_REPLY", "text": "Confirm" },
{ "type": "QUICK_REPLY", "text": "Reschedule" }
] }
]
}
3. Marketing broadcast (MARKETING, media header + CTA URL)
{
"name": "weekly_drop_v1",
"language": "en_US",
"category": "MARKETING",
"components": [
{ "type": "HEADER", "format": "IMAGE", "example": { "header_handle": ["<uploaded-handle>"] } },
{ "type": "BODY", "text": "New drop is live, {{1}} — 20% off for the next 24 hours.", "example": { "body_text": [["Nadia"]] } },
{ "type": "BUTTONS", "buttons": [
{ "type": "URL", "text": "Shop the drop", "url": "https://shop.example.com/drop/{{1}}", "example": ["autumn-2026"] }
] }
]
}
4. Authentication OTP (AUTHENTICATION, one-tap autofill)
{
"name": "login_otp_v1",
"language": "en_US",
"category": "AUTHENTICATION",
"message_send_ttl_seconds": 600,
"components": [
{ "type": "BODY", "add_security_recommendation": true },
{ "type": "FOOTER", "code_expiration_minutes": 10 },
{ "type": "BUTTONS", "buttons": [
{ "type": "OTP", "otp_type": "ONE_TAP", "text": "Copy code", "autofill_text": "Autofill", "package_name": "com.example.app", "signature_hash": "K8a...==" }
] }
]
}
Versioning & lifecycle notes
- Every template is a file in Git:
templates/<name>/vN.json. The name deployed to the WABA is<name>_vNso multiple versions can co-exist during a rollout. - Quality rating (Green → Yellow → Red) is tracked per template on the webhook
message_template_quality_update. A drop to Yellow automatically pauses new sends for that template until a human reviews. - Messaging tier limits cap how many unique users you can start conversations with per 24h: Tier 1 = 1,000 · Tier 2 = 10,000 · Tier 3 = 100,000 · Tier 4 = unlimited. Tier upgrades happen automatically as volume + quality hold.
- Deprecating a template: mark
paused: truein config → wait for in-flight sends to drain →DELETE /{waba-id}/message_templates?name=<name_vN>on day 7. Old versions stay auditable in Git either way. - Rejections are structural, not tuneable — if Meta rejects a MARKETING template as UTILITY-shaped (or vice versa), the fix is a category change, which is always a new version.