plugins/maccing-growth/skills/whatsapp/SKILL.md
WhatsApp Business Platform (Cloud API) production reference. Covers setup, message dispatch, templates, per-message pricing, webhooks, bulk sending, Flows, BSP routing, compliance, Node.js/TypeScript SDK, error codes, and 2025-2026 platform changes. Triggers on "whatsapp", "whatsapp api", "cloud api", "WABA", "BSP", "whatsapp template", "whatsapp marketing", "message template".
npx skillsauth add andredezzy/maccing whatsappInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Naming: Meta's umbrella is WhatsApp Business Platform; the current API is the Cloud API. "WhatsApp Business API" is legacy phrasing (the On-Premises API sunset Oct 23, 2025). "WABA" (WhatsApp Business Account) is an account entity, not an API.
Last researched: May 2026. API current version: v23.0+. All conversation-based pricing deprecated July 1, 2025.
MANDATORY: Read project context BEFORE any action.
ALWAYS read: .maccing/growth/README.md (if exists)
.maccing/growth/meta/<bm>/whatsapp/<waba>/README.md (if exists)
These contain current state: BM status, pipeline day, checklist progress.
Without reading them, you WILL operate on stale data.
MANDATORY: ALWAYS load the `meta-ads` skill before ANY WhatsApp operation.
WhatsApp Cloud API runs on Meta Business Manager (BM) infrastructure.
BM health, verification, account quality, payment hygiene, and classifier
signals from meta-ads apply directly to WhatsApp messaging.
A BM ban kills BOTH your ads AND your WhatsApp number.
Why this is mandatory: WhatsApp Business Account (WABA) lives inside the BM. Same BM verification, same account quality score, same payment methods, same cascade ban mechanics. The meta-ads skill covers: BM defensive intelligence (classifier signals, black hat ecosystem awareness), CTWA ad attribution (ctwa_clid), CAPI conversion tracking (action_source: business_messaging), 72-hour free messaging window, remarketing via WhatsApp contacts as Custom Audiences, WhatsApp Marketing Messages placement in Ads Manager, and the full defense playbook for legitimate companies in sensitive niches.
| Dimension | WhatsApp Business App | WhatsApp Cloud API | |---|---|---| | Target | Small business, 1-2 agents | Enterprise, automation, scale | | Access | Free mobile app | REST API (Meta-hosted) | | Volume | Manual, limited | Programmatic, unlimited | | Automation | None | Full (chatbots, CRM, flows) | | Templates | Basic | Full template library | | Multi-agent | No | Yes (via inbox platform) | | Analytics | None | Full metrics API | | Cost | Free | Per-message charges apply |
The On-Premises API was permanently shut down on October 23, 2025. Cloud API is the only path for all new and existing integrations. Any code referencing on-prem endpoints must be migrated. Error code 1005 is returned when a number is still registered on on-prem.
https://graph.facebook.com/{version}/{phone_number_id}/messagesGo to business.facebook.com. Provide legal business name, address, phone, website, tax ID. Submit business verification documents (takes 2-10 business days).
Visit developers.facebook.com, register using the same Facebook account linked to your business. Accept developer terms.
developers.facebook.com → My Apps → Create AppRestrictions:
Temporary token (testing only, 24-hour expiry): Generated in Meta Developers panel → WhatsApp → API Setup.
Permanent token (production, never expires unless revoked):
whatsapp_business_messaging, whatsapp_business_management, catalog_managementhub.challenge valuemessages fieldWebhook verification handler:
app.get('/webhook', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.WEBHOOK_VERIFY_TOKEN) {
res.status(200).send(challenge);
} else {
res.sendStatus(403);
}
});
WhatsApp Manager → Account Tools → Phone Numbers → request verified badge. Requirements: WABA at least 30 days old, business verification complete, 2FA enabled, display name approved, notable media coverage. As of August 2025, badge color changed from green to blue (aligned with Facebook/Instagram).
https://graph.facebook.com/v23.0/{phone_number_id}/messagesv23.0 not v{latest} — pin to a specific version in productionPOST https://graph.facebook.com/v23.0/{PHONE_NUMBER_ID}/messages
Authorization: Bearer {ACCESS_TOKEN}
Content-Type: application/json
All requests share this base structure:
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "15551234567",
"type": "<message_type>",
"<message_type>": { ... }
}
Response format:
{
"messaging_product": "whatsapp",
"contacts": [{ "input": "15551234567", "wa_id": "15551234567" }],
"messages": [{ "id": "wamid.abc123", "message_status": "accepted" }]
}
message_status values: accepted, held_for_quality_assessment, paused
The API response only confirms the message was accepted. Actual delivery is tracked via webhooks.
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "15551234567",
"type": "text",
"text": {
"preview_url": true,
"body": "Hello! Your order #1234 has shipped."
}
}
preview_url: true enables link preview*text*), italic (_text_), strikethrough (~text~), code (`text`){
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "image",
"image": {
"link": "https://example.com/image.jpg",
"caption": "Optional caption text"
}
}
Or using a media ID (preferred for performance):
{
"image": {
"id": "1234567890",
"caption": "Optional caption"
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "video",
"video": {
"id": "MEDIA_ID",
"caption": "Check out this product demo"
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "document",
"document": {
"id": "MEDIA_ID",
"filename": "invoice_2026_001.pdf",
"caption": "Your invoice"
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "audio",
"audio": {
"id": "MEDIA_ID"
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "sticker",
"sticker": {
"id": "MEDIA_ID"
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "location",
"location": {
"longitude": -122.425332,
"latitude": 37.758056,
"name": "Our Store - Mission District",
"address": "123 Valencia St, San Francisco, CA"
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "contacts",
"contacts": [{
"name": { "formatted_name": "John Doe", "first_name": "John", "last_name": "Doe" },
"phones": [{ "phone": "+1 555-123-4567", "type": "WORK", "wa_id": "15551234567" }],
"emails": [{ "email": "[email protected]", "type": "WORK" }]
}]
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "reaction",
"reaction": {
"message_id": "wamid.originalMessageId",
"emoji": "👍"
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "interactive",
"interactive": {
"type": "button",
"header": {
"type": "text",
"text": "Order Status"
},
"body": {
"text": "Your order #1234 is ready. What would you like to do?"
},
"footer": {
"text": "Reply within 24 hours"
},
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "confirm_pickup", "title": "Pick Up Now" } },
{ "type": "reply", "reply": { "id": "schedule_later", "title": "Schedule Later" } },
{ "type": "reply", "reply": { "id": "cancel_order", "title": "Cancel Order" } }
]
}
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "interactive",
"interactive": {
"type": "list",
"header": { "type": "text", "text": "Choose Department" },
"body": { "text": "How can we help you today?" },
"footer": { "text": "We reply within 2 hours" },
"action": {
"button": "View Options",
"sections": [
{
"title": "Sales",
"rows": [
{ "id": "new_order", "title": "Place New Order", "description": "Start a new purchase" },
{ "id": "track_order", "title": "Track Order", "description": "Check delivery status" }
]
},
{
"title": "Support",
"rows": [
{ "id": "technical", "title": "Technical Help", "description": "Device or app issues" },
{ "id": "billing", "title": "Billing Query", "description": "Invoice and payment" }
]
}
]
}
}
}
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "interactive",
"interactive": {
"type": "flow",
"header": { "type": "text", "text": "Book Appointment" },
"body": { "text": "Select a time slot that works for you" },
"footer": { "text": "Powered by Our Booking System" },
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_token": "UNIQUE_FLOW_TOKEN",
"flow_id": "FLOW_ID",
"flow_cta": "Open Booking",
"flow_action": "navigate",
"flow_action_payload": {
"screen": "APPOINTMENT_SCREEN",
"data": { "customer_name": "João Silva" }
}
}
}
}
}
Standalone typing_indicator message type. Shows "typing…" for up to 25 seconds or until you send the reply; only works inside an active conversation (the recipient must have messaged you). POST /<PHONE_NUMBER_ID>/messages:
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "15551234567",
"type": "typing_indicator",
"typing_indicator": { "type": "text" }
}
Via a BSP, use its wrapper — e.g. YCloud exposes POST /v2/whatsapp/inboundMessages/{id}/typingIndicator (the inbound wamid in the path, no body).
{
"messaging_product": "whatsapp",
"status": "read",
"message_id": "wamid.messageId"
}
Templates are the only message type that can be sent to users outside a 24-hour customer service window, or to users who haven't messaged you first. They must be pre-approved by Meta.
| Category | Use Case | Cost | Notes | |---|---|---|---| | Marketing | Promotions, newsletters, offers | Charged per delivery | Highest cost; no volume discounts | | Utility | Order updates, shipping, account alerts | Charged outside CSW; free inside CSW | Volume discounts apply | | Authentication | OTPs, login codes, 2FA | Charged; lowest domestic rate | Charged even inside CSW (unlike Utility which is free inside CSW); auth-international rates apply cross-border |
Service messages (non-template free-form replies within a CSW) are NOT a template category — they are any message type (text, image, interactive, etc.) sent to a user who has an open CSW. They are free since November 1, 2024 and can only be sent within an open window. Service messages do not require template approval.
Critical: Marketing templates to US phone numbers are not delivered as of April 1, 2025. Meta describes this as a temporary pause (not a permanent policy) — the stated intent is to assess when the US market is ready. As of mid-2026, no end date has been announced and the pause remains fully in effect. Error returned: 131049. Do not plan on US marketing sends until Meta formally lifts it.
Header (optional): text | image | video | document
Body (required): up to 1024 chars, variables as {{1}}, {{2}}, ...
Footer (optional): static text, max 60 chars
Buttons (optional): up to 3 buttons
- URL button: opens a link (supports dynamic URLs with variable)
- Phone call button: dials a number
- Quick reply button: sends a predefined text
- Copy code button (authentication only)
- One-tap autofill button (authentication only; **Android-only** — iOS users see a copy code button fallback automatically)
POST https://graph.facebook.com/v23.0/{WABA_ID}/message_templates
{
"name": "order_shipped_v2",
"language": "pt_BR",
"category": "UTILITY",
"components": [
{
"type": "HEADER",
"format": "TEXT",
"text": "Pedido enviado!"
},
{
"type": "BODY",
"text": "Olá {{1}}, seu pedido #{{2}} foi enviado. Previsão de entrega: {{3}}. Rastreie em: {{4}}",
"example": {
"body_text": [["João", "ORD-4521", "5 de maio", "https://track.example.com/abc"]]
}
},
{
"type": "FOOTER",
"text": "Responda PARAR para cancelar notificações"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Rastrear pedido",
"url": "https://track.example.com/{{1}}",
"example": ["https://track.example.com/abc123"]
},
{
"type": "QUICK_REPLY",
"text": "Falar com suporte"
}
]
}
]
}
Template name rules: lowercase, numbers, underscores only. Example: order_confirmation_v2
{
"messaging_product": "whatsapp",
"to": "5511999999999",
"type": "template",
"template": {
"name": "order_shipped_v2",
"language": { "code": "pt_BR" },
"components": [
{
"type": "header",
"parameters": [
{ "type": "text", "text": "Pedido enviado!" }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "João" },
{ "type": "text", "text": "ORD-4521" },
{ "type": "text", "text": "5 de maio" },
{ "type": "text", "text": "https://track.example.com/abc" }
]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "text": "abc123" }
]
}
]
}
}
{
"template": {
"name": "promo_with_image",
"language": { "code": "en_US" },
"components": [
{
"type": "header",
"parameters": [
{
"type": "image",
"image": { "link": "https://example.com/promo.jpg" }
}
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Maria" },
{ "type": "text", "text": "30" }
]
}
]
}
}
{
"type": "body",
"parameters": [
{
"type": "currency",
"currency": {
"fallback_value": "R$ 150,00",
"code": "BRL",
"amount_1000": 150000
}
},
{
"type": "date_time",
"date_time": {
"fallback_value": "5 de maio de 2026",
"day_of_month": 5,
"month": 5,
"year": 2026
}
}
]
}
{
"name": "verification_code",
"language": { "code": "pt_BR" },
"components": [
{
"type": "body",
"parameters": [{ "type": "text", "text": "847291" }]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [{ "type": "text", "text": "847291" }]
}
]
}
| Stage | Detail |
|---|---|
| Submission | Via WhatsApp Manager UI or Graph API |
| Initial review | Meta automated + human review |
| Time | Typically minutes to 24 hours; complex content up to 48 hours |
| Status | PENDING → APPROVED / REJECTED; post-approval quality states: ACTIVE (High/Medium/Low quality) → PAUSED → DISABLED; after appeal: APPEAL_REQUESTED |
| Appeal | Edit and resubmit (up to 1×/day, 10×/month for Approved templates; rejected/paused templates have unlimited edits). Or file appeal in WhatsApp Manager → rejected template → 'Appeal'. Meta responds in 24–72h. Template name cannot be reused for 30 days after rejection if creating a new one. |
| Required before sending | Template must have APPROVED status |
Common rejection reasons:
Pro tips for approval:
Reply STOP to unsubscribe)DISABLEDDISABLED templates can be edited and resubmitted (returns to In Review, can be restored to Active — you do NOT need to create a brand-new template)GET /{WABA_ID}/message_templatesMeta uses two overlapping quality-control mechanisms:
Template Pacing (proactive, at campaign start):
held_for_quality_assessment status) for up to 30 minutesTemplate Pausing (reactive, accumulating quality failures):
DISABLEDKey distinction: pacing drops queued messages silently within the first 30 minutes; pausing halts the template for hours/permanently based on accumulated feedback over days.
Effective July 1, 2025: Meta replaced Conversation-Based Pricing (CBP) with Per-Message Pricing (PMP).
| Category | Inside CSW | Outside CSW | |---|---|---| | Marketing | Charged | Charged | | Utility | Free | Charged | | Authentication | Charged | Charged | | Service (free-form) | Free | Not allowed |
24-hour Customer Service Window (CSW): Opens when a user sends you any message or makes/receives a call through the Calling API. All free-form messages and utility templates sent within this window are free. Window resets with each new inbound message or call from the user. Service conversations have been free with no monthly cap since November 1, 2024 (Meta removed the previous 1,000 service conversation/month cap on that date).
72-hour Entry Point Window: When a user on Android or iOS initiates contact via a Click-to-WhatsApp (CTWA) ad or Facebook Page CTA button, and the business responds within 24 hours, all messages to that user (including marketing templates) are free for 72 hours. Note: desktop and web WhatsApp clients do not trigger this free entry point window.
| Market | Marketing | Utility | Authentication | |---|---|---|---| | India | $0.0094–$0.0118 | $0.0014–$0.0015 | $0.0014–$0.0015 | | Indonesia | $0.0283 | $0.022 | $0.022 | | Brazil | $0.0625 (base; some sources show ~$0.0719 effective April 2026 — verify against current Meta rate card) | $0.0068 (Tier 1/base; volume discounts reduce incrementally) | $0.0068 (Tier 1/base) | | Mexico | $0.0305 | $0.004–$0.0085 | $0.0085 | | United States | $0.025–$0.029 | $0.004–$0.0088 | $0.004–$0.0088 | | United Kingdom | $0.0382–$0.048 | $0.020 | $0.020 | | Germany | $0.1131–$0.1365 | $0.0456–$0.050 | $0.0456–$0.050 | | UAE | $0.0499 | $0.0157 | $0.0157 |
Rates vary slightly by BSP and are updated periodically. Check the official Meta pricing page and your BSP's rate card.
Cross-border OTP delivery (sending auth templates to users in a different country than your business number) has higher rates in some markets. UAE, for example, has an auth-international rate of ~$0.051 vs. $0.0157 domestic.
Marketing messages receive no volume discounts. Utility and authentication qualify for monthly tiered discounts:
| Tier | Volume (monthly) | Discount | |---|---|---| | 1 | Standard | 0% | | 2 | 10,000–100,000 | 5% | | 3 | 100,000–1,000,000 | 10% | | 4 | 1,000,000+ | 15-20% |
Tiers reset monthly. Discounts are applied automatically by Meta.
Regional authentication discounts (July 2025):
| Level | Speed | Upgrade Trigger | |---|---|---| | Standard | 80 messages/second (MPS) | Default for all verified numbers | | Coexistence | 20 MPS | When number also uses WhatsApp Business App | | High Volume | Up to 1,000 MPS | Automatic when eligible |
Auto-upgrade to 1,000 MPS requires:
Upgrade is automatic (takes ~1 minute) and triggers a THROUGHPUT_UPGRADE webhook event. No cost for higher throughput.
Since October 7, 2025: limits apply per Business Portfolio, not per phone number.
| Tier | Daily Unique Users | Notes | |---|---|---| | 0 (Unverified) | 250 | Before business verification | | 1 | 2,000 | After business verification (raised from 1,000 on Oct 7, 2025); empirically confirmed in production on a live WABA | | 2 | 10,000 | First scaling milestone | | 3 | 100,000 | Established brands | | 4 | Unlimited | Enterprise |
Upcoming (Q1-Q2 2026 rollout, not yet universal): Meta announced removal of the 2K and 10K intermediate tiers for verified businesses. Once fully deployed, business verification would jump a portfolio directly to 100K/day. Our own WABA (verified BM, May 2026) was at 2,000/day — treat as a pending rollout, not a completed change.
Tier upgrade logic:
| Rating | Color | Impact | |---|---|---| | High | Green | Full scaling clearance | | Medium | Yellow | Warning; monitor closely | | Low | Red | Tier upgrades blocked |
Quality is computed from the last 7 days of user feedback: blocks, spam reports, opt-outs, engagement rates.
Key change (October 2025): Red rating no longer triggers automatic tier downgrade (unless accompanied by policy violations). It only blocks advancement. The Flagged phone-number status was also fully eliminated in this update — previously, a number with Red quality for 7 days entered a Flagged state that triggered tier downgrade. Under the new system there is no Flagged state: Red quality only blocks advancement and does not reduce an existing tier (absent policy violations).
Pair Rate Limiting: Sending too many messages to the same recipient in a short period triggers error 131056. Space out messages to individual users; WhatsApp limits how fast you can message a single number.
import PQueue from 'p-queue';
// Respect 80 MPS default throughput
const queue = new PQueue({
concurrency: 10,
interval: 1000,
intervalCap: 80
});
async function sendBulkMessages(recipients: string[], template: TemplateMessage) {
const results = await Promise.allSettled(
recipients.map(phone =>
queue.add(() => sendWhatsAppMessage(phone, template))
)
);
return results;
}
WhatsApp Flows are native, app-like interactive experiences embedded directly in a WhatsApp conversation. Users complete multi-step forms, appointments, surveys, or purchases without leaving the chat. Results: 8x+ higher conversion vs. redirecting to a website (vendor-claimed, not independently verified).
Available in: WhatsApp Manager (no-code builder) or via API (JSON definition).
Supported components: TextInput, TextArea, Dropdown, DatePicker, RadioButtonsGroup, CheckboxGroup, TextHeading, TextBody, Image, EmbeddedLink, Footer with action buttons.
Limitations: Once published, a flow cannot be edited (only deprecated and re-created). Max 50 components per screen. Only layout type is SingleColumnLayout.
{
"version": "7.0",
"data_api_version": "3.0",
"routing_model": {
"WELCOME": ["SELECT_DATE", "SKIP"],
"SELECT_DATE": ["CONFIRM"],
"CONFIRM": []
},
"screens": [
{
"id": "WELCOME",
"title": "Book Appointment",
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "TextHeading",
"text": "Schedule Your Visit"
},
{
"type": "TextBody",
"text": "Choose a service and preferred date"
},
{
"type": "Dropdown",
"name": "service",
"label": "Service Type",
"required": true,
"data-source": [
{ "id": "haircut", "title": "Haircut" },
{ "id": "coloring", "title": "Coloring" }
]
},
{
"type": "DatePicker",
"name": "appointment_date",
"label": "Preferred Date",
"required": true
},
{
"type": "Footer",
"label": "Continue",
"on-click-action": {
"name": "navigate",
"next": { "type": "screen", "name": "CONFIRM" },
"payload": {
"service": "${form.service}",
"date": "${form.appointment_date}"
}
}
}
]
}
},
{
"id": "CONFIRM",
"title": "Confirmation",
"terminal": true,
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "TextBody",
"text": "Appointment booked for ${data.date}"
},
{
"type": "Footer",
"label": "Done",
"on-click-action": { "name": "complete" }
}
]
}
}
]
}
POST /{WABA_ID}/flows # Create flow
GET /{FLOW_ID} # Get flow details
POST /{FLOW_ID} # Update flow JSON
POST /{FLOW_ID}/publish # Publish draft flow
POST /{FLOW_ID}/deprecate # Deprecate published flow
DELETE /{FLOW_ID} # Delete draft flow only
Create a Flow:
{
"name": "Appointment Booking",
"categories": ["APPOINTMENT_BOOKING"]
}
Update Flow JSON:
POST /{FLOW_ID}
{
"flow_json": "<escaped JSON string>",
"name": "Appointment Booking"
}
Publish a Flow:
POST /{FLOW_ID}/publish
Set data_channel_uri in the flow for real-time backend communication:
{
"data_api_version": "3.0",
"routing_model": { ... },
"screens": [ ... ]
}
Meta sends POST requests to your data_channel_uri when screens require data, with the payload containing the screen ID and user inputs. Your server responds with the data to populate the next screen.
WhatsApp Payments via Messages API is available in Brazil (Pix, Boleto, Payment Links) and India. Not yet globally available. Businesses receive payment confirmations via delivery reports/webhooks.
You receive two types of webhook notifications:
{
"object": "whatsapp_business_account",
"entry": [{
"id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
"changes": [{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "15551234567",
"phone_number_id": "PHONE_NUMBER_ID"
},
"contacts": [{
"profile": { "name": "João Silva" },
"wa_id": "5511999999999"
}],
"messages": [{
"id": "wamid.abc123",
"from": "5511999999999",
"timestamp": "1704067200",
"type": "text",
"text": { "body": "Where is my order?" }
}]
},
"field": "messages"
}]
}]
}
{
"messages": [{
"id": "wamid.xyz789",
"from": "5511999999999",
"type": "image",
"image": {
"id": "MEDIA_ID_123456",
"mime_type": "image/jpeg",
"sha256": "HASH_VALUE",
"caption": "Here is the receipt"
}
}]
}
Media payloads contain only a
media_id, not the actual file. You must call the media API to get the download URL, which expires in ~5 minutes.
{
"messages": [{
"type": "interactive",
"interactive": {
"type": "button_reply",
"button_reply": {
"id": "confirm_pickup",
"title": "Pick Up Now"
}
}
}]
}
{
"messages": [{
"type": "interactive",
"interactive": {
"type": "list_reply",
"list_reply": {
"id": "track_order",
"title": "Track Order",
"description": "Check delivery status"
}
}
}]
}
{
"entry": [{
"changes": [{
"value": {
"messaging_product": "whatsapp",
"statuses": [{
"id": "wamid.abc123",
"status": "delivered",
"timestamp": "1704067300",
"recipient_id": "5511999999999",
"conversation": {
"id": "CONVERSATION_ID",
"origin": { "type": "utility" }
},
"pricing": {
"billable": true,
"pricing_model": "PMP",
"category": "utility"
}
}]
}
}]
}]
}
Status progression: sent → delivered → read (or failed)
Starting June 2026, the from field in webhooks may contain a Business-Scoped User ID (BSUID) instead of a phone number when users have opted into usernames. BSUID format: CC.alphanumeric (e.g., BR.1A2B3C4D5E6F7G8H9I0J).
Action required: Store both phone number AND BSUID in your CRM. Update all webhook handlers to accept both formats in the from/wa_id fields.
As of March 31, 2026, a new identity.user_id field is included in all message webhooks — this is the BSUID.
import crypto from 'crypto';
function verifyWebhookSignature(
rawBody: string,
signature: string | undefined,
appSecret: string
): boolean {
if (!signature) return false;
const expectedSig = 'sha256=' + crypto
.createHmac('sha256', appSecret)
.update(rawBody, 'utf8')
.digest('hex');
try {
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSig)
);
} catch {
return false;
}
}
// Usage in Express (MUST use raw body — before json() middleware):
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-hub-signature-256'] as string;
const rawBody = req.body.toString();
if (!verifyWebhookSignature(rawBody, sig, process.env.APP_SECRET!)) {
return res.sendStatus(401);
}
// Return 200 IMMEDIATELY, process async
res.status(200).send('OK');
const payload = JSON.parse(rawBody);
processWebhookAsync(payload).catch(console.error);
});
Critical March 2026 change: Meta switched the Certificate Authority for mTLS webhooks on March 31, 2026. Update your trust store with meta-outbound-api-ca-2025-12.pem to maintain delivery.
receive webhook → enqueue → 200 OK → process from queueentry, changes, messages, statuses are all arrays; never assume single elements.JSON.parse(req.body) for signature verification — use raw buffer before parsing.// Queue-first architecture (BullMQ example)
import { Queue } from 'bullmq';
const whatsappQueue = new Queue('whatsapp-webhooks');
app.post('/webhook', express.raw({ type: '*/*' }), async (req, res) => {
if (!verifyWebhookSignature(req.body.toString(), req.headers['x-hub-signature-256'] as string, process.env.APP_SECRET!)) {
return res.sendStatus(401);
}
await whatsappQueue.add('process', JSON.parse(req.body.toString()), {
attempts: 3,
backoff: { type: 'exponential', delay: 2000 }
});
res.status(200).send('OK');
});
| Type | Formats | Max Size | Notes | |---|---|---|---| | Image | JPEG, PNG | 5 MB | WebP only for stickers | | Video | MP4, 3GP | 16 MB | H.264 video + AAC audio required | | Audio | AAC, MP4, MPEG, AMR, OGG | 16 MB | OGG with OPUS codec | | Document | PDF, DOC(X), XLS(X), PPT(X), TXT | 100 MB | | | Sticker | WebP | 100 KB (static), 500 KB (animated) | 512x512px, transparent bg |
Upload limit to media API: 64 MB (but post-processing enforces type-specific limits above).
POST https://graph.facebook.com/v23.0/{PHONE_NUMBER_ID}/media
Authorization: Bearer {ACCESS_TOKEN}
Content-Type: multipart/form-data
curl -X POST "https://graph.facebook.com/v23.0/PHONE_NUMBER_ID/media" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-F "file=@./image.jpg;type=image/jpeg" \
-F "type=image/jpeg" \
-F "messaging_product=whatsapp"
Response:
{ "id": "1234567890123456" }
TypeScript upload example:
import FormData from 'form-data';
import fs from 'fs';
import axios from 'axios';
async function uploadMedia(filePath: string, mimeType: string, phoneNumberId: string): Promise<string> {
const form = new FormData();
form.append('file', fs.createReadStream(filePath), { contentType: mimeType });
form.append('type', mimeType);
form.append('messaging_product', 'whatsapp');
const response = await axios.post(
`https://graph.facebook.com/v23.0/${phoneNumberId}/media`,
form,
{
headers: {
...form.getHeaders(),
Authorization: `Bearer ${process.env.WA_ACCESS_TOKEN}`
}
}
);
return response.data.id;
}
GET https://graph.facebook.com/v23.0/{MEDIA_ID}
Authorization: Bearer {ACCESS_TOKEN}
Response:
{
"url": "https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=...",
"mime_type": "image/jpeg",
"sha256": "hash",
"file_size": 102400,
"id": "MEDIA_ID",
"messaging_product": "whatsapp"
}
The URL returned expires quickly (~5 minutes). Download immediately.
curl -OJ \
-H "Authorization: Bearer ACCESS_TOKEN" \
"https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=..."
The official Meta Node.js SDK (WhatsApp/WhatsApp-Nodejs-SDK) was archived in June 2023. Use the active community fork instead:
npm install @great-detail/whatsapp
# or
bun add @great-detail/whatsapp
Compatible with Node.js v22+, Deno v2.4+, Bun v1.2+. Built for Cloud API v23.
import Client from '@great-detail/whatsapp';
const sdk = new Client({
request: {
headers: { Authorization: `Bearer ${process.env.WA_ACCESS_TOKEN}` }
}
});
const message = await sdk.message.createMessage({
phoneNumberID: process.env.WA_PHONE_NUMBER_ID!,
to: '5511999999999',
type: 'text',
text: { body: 'Hello from TypeScript!' }
});
console.log('Message ID:', message.messages[0].id);
const message = await sdk.message.createMessage({
phoneNumberID: process.env.WA_PHONE_NUMBER_ID!,
to: '5511999999999',
type: 'template',
template: {
name: 'order_shipped_v2',
language: { code: 'pt_BR' },
components: [{
type: 'body',
parameters: [
{ type: 'text', text: 'João' },
{ type: 'text', text: 'ORD-4521' },
{ type: 'text', text: '5 de maio' },
{ type: 'text', text: 'https://track.example.com/abc' }
]
}]
}
});
import fs from 'fs';
// Upload
const fileBuffer = fs.readFileSync('./invoice.pdf');
const upload = await sdk.media.upload({
phoneNumberID: process.env.WA_PHONE_NUMBER_ID!,
mimeType: 'application/pdf',
file: fileBuffer
});
// Send using media ID
await sdk.message.createMessage({
phoneNumberID: process.env.WA_PHONE_NUMBER_ID!,
to: '5511999999999',
type: 'document',
document: {
id: upload.id,
filename: 'invoice.pdf',
caption: 'Your invoice is attached'
}
});
import express from 'express';
import Client from '@great-detail/whatsapp';
const app = express();
const sdk = new Client({
request: { headers: { Authorization: `Bearer ${process.env.WA_ACCESS_TOKEN}` } }
});
// Webhook verification
app.get('/webhook', (req, res) => {
const reg = sdk.webhook.register({
method: req.method,
query: req.query as Record<string, string>,
body: req.body,
headers: req.headers as Record<string, string>
});
if (reg.verifyToken !== process.env.WEBHOOK_VERIFY_TOKEN) {
return res.end(reg.reject());
}
return res.end(reg.accept());
});
// Webhook event handling
app.post('/webhook', express.raw({ type: '*/*' }), async (req, res) => {
const event = sdk.webhook.eventNotification({
method: req.method,
query: req.query as Record<string, string>,
body: req.body.toString(),
headers: req.headers as Record<string, string>
});
event.verifySignature(process.env.APP_SECRET!);
// Return 200 immediately
res.end(event.accept());
// Process async
processEvent(event).catch(console.error);
});
async function processEvent(event: unknown) {
// Handle incoming messages, status updates, etc.
}
const BASE_URL = `https://graph.facebook.com/v23.0/${process.env.WA_PHONE_NUMBER_ID}`;
async function sendMessage(payload: object): Promise<{ id: string }> {
const response = await fetch(`${BASE_URL}/messages`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.WA_ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const error = await response.json();
throw new WhatsAppError(error.error.code, error.error.message);
}
const data = await response.json();
return data.messages[0];
}
WA_PHONE_NUMBER_ID= # Phone number ID (not the phone number itself)
WA_BUSINESS_ACCOUNT_ID= # WhatsApp Business Account ID
WA_ACCESS_TOKEN= # System user permanent token
APP_SECRET= # App secret for webhook signature verification
WEBHOOK_VERIFY_TOKEN= # Your custom token for webhook verification
| Code | Meaning | Fix | |---|---|---| | 0 | Auth exception | Generate new access token | | 3 | API method / capability | Verify permissions | | 10 | Permission denied | Check token permissions; re-add phone to allowlist | | 190 | Access token expired | Generate new system user token |
| Code | Meaning | Fix | |---|---|---| | 4 | Too many calls (200/hr default) | Throttle requests | | 130429 | Throughput limit hit (80 MPS) | Reduce sending speed | | 131048 | Spam rate limit | Improve content quality | | 131056 | Pair rate limit (too many to same recipient) | Space out messages to one recipient | | 133016 | Register/deregister rate limit (10 per 72h) | Wait 72 hours |
| Code | Meaning | Fix | |---|---|---| | 131021 | Recipient cannot be sender | Use separate test number | | 131026 | Message undeliverable | 4 possible causes (Meta does not identify which): (1) Number not registered on WhatsApp; (2) Recipient has not accepted the latest WhatsApp ToS/Privacy Policy; (3) Recipient is using an outdated WhatsApp client; (4) Sending an authentication template to a +91 India number (not supported). Remove from list; do not retry. | | 131047 | Re-engagement message: free-form (non-template) send attempted outside the 24h customer-service window | Use an approved template instead (templates are allowed any time; only free-form text is gated by the 24h window) | | 131049 | Per-user marketing frequency cap (also: US number marketing block since April 2025) | Recipient has hit Meta's per-user daily marketing cap across all businesses. Do NOT retry immediately — wait at least 24h. Also returned for ALL marketing template sends to US (+1) numbers since April 1, 2025 (permanent pause, still in effect mid-2026). Switch to utility template (exempt from cap) or wait 24h and retry. | | 131051 | Unsupported message type | Check API docs for supported types | | 131052 | Media download error | Verify media URL/ID accessibility | | 131053 | Media upload error | Check format, size, configuration | | 130472 | Experiment holdout (~1% of users per region, since June 2023) | Marketing template blocked for experiment participants. Not billed. Do not retry directly. Exceptions where delivery IS allowed even for experiment participants: (1) user messaged your business in last 24h (CSW open), (2) active marketing conversation ongoing, (3) user arrived via CTWA/free-entry-point ad. Affects marketing templates only. |
| Code | Meaning | Fix | |---|---|---| | 132000 | Parameter count mismatch | Verify param count matches template | | 132001 | Template not found | Check name, language, approval status | | 132005 | Hydrated text too long | Shorten variables | | 132007 | Policy violation | Review and revise template content | | 132012 | Parameter format mismatch | Verify format matches template specs | | 132015 | Template paused (low quality) | Auto-resumes after 3h (1st pause) or 6h (2nd pause). Stop active campaigns immediately. After auto-resume, evaluate content/targeting. A 3rd trigger moves template to DISABLED (132016). Can also appear when portfolio pacing drops remaining messages mid-campaign. | | 132016 | Template disabled (repeated low quality) | Edit template content and resubmit for review (status returns to In Review; if approved, restored to Active). Creating a new template is an option but not required. |
| Code | Meaning | Fix | |---|---|---| | 132068 | Flow blocked | Fix missing inputs or logic errors | | 132069 | Flow throttled (10 msg/hr) | Improve endpoint health and nav metrics |
| Code | Meaning | Fix |
|---|---|---|
| 368 | WABA policy violation (account restricted/disabled) | The WhatsApp Business Account has been restricted or disabled for violating Messaging, Commerce, or ToS policy. Can be temporary (1-30 days) or indefinite. Not retryable. Appeal via Business Support Home → select violation → Request Review. Common causes: spam reports, restricted content, excessive blocks. Distinct from 131031 (number-level lock). |
| 131031 | Account locked | Two distinct causes: (1) Policy violation — WABA restricted/disabled. Appeal via Business Support Home. (2) 2FA PIN mismatch — Meta cannot verify the two-step PIN in the request. Fix: disable 2FA on the number, re-register, re-enable 2FA. Check the WhatsApp Manager healthcheck for diagnostic detail. |
| 131042 | Business eligibility / payment issue | One of: (1) payment account not linked to WABA; (2) credit limit exceeded; (3) credit line inactive; (4) WABA suspended/deleted; (5) timezone/currency settings missing or wrong; (6) pending MessagingFor request. Fix: check each condition in WhatsApp Manager billing settings. |
| 130497 | Business account restricted from messaging users in this country | Brazil (+55) and Indonesia (+62): permanently restricted for foreign-number senders since September 2025 — the tier scaling path does NOT help here; use a locally-registered number. For other markets, two causes: (1) Cross-border restriction — your WABA number's registered country does not match the recipient's; may clear by completing the messaging tier scaling path (up to 30 days); (2) Restricted content — sending prohibited goods/services to a country where they're not allowed. Fix: a locally-registered number for the target market, or review the WhatsApp Commerce Policy. |
| 133005 | 2FA PIN mismatch | Verify PIN; reset via WhatsApp Manager |
| 133010 | Phone not registered | Complete registration |
| 1005 | Number on deprecated on-premises API | Migrate to Cloud API (on-prem API shut down October 23, 2025) |
| 131000 | Unknown error (something went wrong) | Transient server-side error. Retryable — implement exponential backoff. If persists >5 minutes, check Meta status page. |
| 131005 | Permission denied | Token missing required permission (whatsapp_business_messaging or whatsapp_business_management). Re-generate token with correct permissions. |
| 131008 | Required parameter missing | API request is missing a required field. Fix the request payload. Not retryable. |
| 131009 | Invalid parameter value | A parameter value does not meet requirements. Fix the request payload. Not retryable. |
| 131016 | Service temporarily unavailable | Transient. Retryable with exponential backoff. Check Meta status page if persistent. |
| Code | Meaning | Interpretation / Fix | |---|---|---| | 131026 | Message undeliverable | Recipient can't receive: number not on WhatsApp, hasn't accepted WhatsApp ToS, or invalid. LIST-QUALITY signal — a BSP upload marking a row "valid" checks FORMAT only, NOT WhatsApp-registration. Remove these numbers; do not retry. | | 131049 | "Not delivered to maintain healthy ecosystem engagement" | Meta's marketing-message throttle / per-user frequency cap. Common on NEW numbers (low trust) and recipients with marketing fatigue. NOT a block/report and NOT a hard penalty — it eases as the number's quality/engagement builds. Mitigate with high engagement (warm contacts) + slow ramp, not by resending. |
Real-world broadcast baselines (illustrative, directional):
async function sendWithRetry(
payload: object,
maxAttempts = 3
): Promise<{ id: string }> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await sendMessage(payload);
} catch (error) {
if (error instanceof WhatsAppError) {
// Non-retryable errors
const nonRetryable = [131021, 131026, 130472, 130497, 132001, 132007, 132016, 133010, 368, 131031];
// Delayed retryable (after 24h+): [131049]
// Transient retryable (immediate backoff): [131000, 131016, 2]
if (nonRetryable.includes(error.code)) {
throw error;
}
// Rate limit: exponential backoff
if ([4, 130429, 131048, 131056].includes(error.code)) {
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
if (attempt === maxAttempts) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
throw new Error('Max retry attempts reached');
}
Mandatory: Businesses must obtain explicit WhatsApp opt-in before sending any proactive messages.
Valid opt-in sources:
Opt-in language must include:
Invalid: Pre-ticked checkboxes, prior SMS consent, implied consent.
Store for each contact:
Retention under LGPD (Brazil): Retain consent records (timestamp, source, exact consent language, IP) for a minimum of 5 years after consent revocation. Retain opt-out records for a minimum of 5 years. ANPD active enforcement since 2025 — audits request these records as evidence of compliance.
Brazil LGPD channel separation: Email/SMS consent does not cover WhatsApp. ANPD guidance requires separate, explicit WhatsApp-specific consent naming the channel, the business, and message type. Re-obtaining consent from email-only lists before WhatsApp outreach is mandatory for LGPD compliance.
Recognize and honor: STOP, UNSUBSCRIBE, CANCEL, OPT OUT, NO, PARAR, SAIR.
Required actions upon opt-out:
Platform-level opt-out (2025-2026): WhatsApp exposes an 'Offers and Announcements' toggle on business profiles. Users can disable marketing messages without sending a keyword. Subscribe to the MARKETING_SUBSCRIPTION_UPDATE webhook event to receive these opt-out signals and suppress affected contacts immediately.
Account protection:
| Region | Key Requirement | |---|---| | EU/EEA | Data Processing Agreement (DPA) with BSP; EU data residency option; explicit GDPR consent | | Germany | BSP must have EU data residency and DPA | | India | Comply with DPDPA (Digital Personal Data Protection Act) | | Brazil | LGPD compliance; explicit consent required per ANPD guidance (active enforcement since 2025). Key obligations: (a) WhatsApp consent must be obtained separately from email/SMS — different channels require separate consents; (b) Consent records must be retained for 5 years after revocation; (c) Marketing messages require explicit consent — transactional messages may use contract-execution basis; (d) Opt-out must be processed within 48 hours (ANPD interpretation); (e) Fines up to 2% of Brazil annual revenue, capped at R$50 million per infraction, now actively enforced. | | US | TCPA consent separate from WhatsApp opt-in; marketing templates blocked to US numbers since April 2025 |
2026 AI restriction: General-purpose AI chatbots are prohibited on WhatsApp. Only task-oriented automation with predictable, business-specific outcomes is allowed (support, booking, order processing).
Voice calls directly within WhatsApp conversations. GA on July 15, 2025.
CSW interaction: A user-initiated call (via the Calling API) opens and resets the 24-hour Customer Service Window, identical to an inbound message. After a call, businesses can send free-form service messages and free utility templates for 24 hours.
Two call types:
whatsapp_business_messaging permissioncalls fieldEarly adopters: conversion rate from 2% to 45% when adding calling to the conversation flow (vendor/early-adopter-claimed, not independently verified).
Launched April 2025. A dedicated API for marketing broadcasts that uses Meta's ads AI to optimize delivery timing and recipient selection. Not a replacement for Cloud API — they run in parallel.
| Aspect | MM Lite API | Cloud API | |---|---|---| | Purpose | Marketing broadcasts | All message types | | AI optimization | Yes (Meta ads AI) | No | | Delivery rate | Up to 9% higher | Standard | | US users | Not delivered | Not delivered (since April 2025) | | Interaction | One-way (templates only) | Two-way | | Integration | Meta Ads Manager | Standard REST API |
| Scenario | Use | |---|---| | Marketing broadcast to large list | MM Lite API | | Customer support / service replies | Cloud API | | Utility messages (order updates) | Cloud API | | Two-way conversation / chatbot | Cloud API | | High-volume promotional campaigns | MM Lite API |
Phone Number Management:
GET /{WABA_ID}/phone_numbersTemplate Management:
POST /{WABA_ID}/message_templatesGET /{WABA_ID}/message_templatesPOST /{TEMPLATE_ID}DELETE /{TEMPLATE_ID}status=APPROVED or status=PENDINGQR Code Management:
POST /{PHONE_NUMBER_ID}/message_qrdlsAnalytics:
GET /{WABA_ID}/analyticsAnalytics query example:
GET /{WABA_ID}/analytics?start=1704067200&end=1706745600&granularity=DAY&metric_types=SENT,DELIVERED,READ
Update profile:
POST https://graph.facebook.com/v23.0/{PHONE_NUMBER_ID}/whatsapp_business_profile
{
"messaging_product": "whatsapp",
"about": "Your tagline here",
"address": "123 Main St",
"description": "Your business description",
"email": "[email protected]",
"websites": ["https://example.com"],
"vertical": "RETAIL"
}
Required for: messaging tier upgrades beyond 250 messages/day, Official Business Account application, paid Meta ad integrations.
Documents accepted: Tax ID/business registration, incorporation documents, utility bill showing business name and address.
Timeline: 2-10 business days.
Order Placed
→ [Utility Template] Order confirmation with order number
→ [Utility Template] Shipping notification with tracking link
→ [Utility Template] Delivery confirmation
→ [Service] Handle any customer replies (free in CSW)
→ [Marketing Template] Post-delivery review request (after 3 days)
User clicks CTWA ad
→ 72-hour free window opens
→ [Marketing Template] Welcome + catalog link
→ [Interactive Buttons] Quick responses (pricing, demo, help)
→ [Flow] Qualification form
→ Human handoff or automated follow-up
User sends message
→ 24-hour CSW opens (free window)
→ Bot handles FAQ, order lookup, returns
→ [Interactive Lists] Department routing
→ Human agent for complex issues
→ [Utility Template] Follow-up summary (if within CSW, free)
// On incoming message webhook
async function handleIncomingMessage(message: WaMessage) {
// Upsert contact in CRM
await crm.upsertContact({
phone: message.from,
bsuid: message.identity?.user_id, // Store BSUID alongside phone
name: message.contacts?.[0]?.profile?.name
});
// Record interaction
await crm.addInteraction({
contactPhone: message.from,
channel: 'whatsapp',
direction: 'inbound',
content: message.text?.body,
timestamp: new Date(parseInt(message.timestamp) * 1000)
});
// Trigger automation
await automationEngine.trigger('whatsapp_message_received', {
contact: message.from,
message: message
});
}
| Change | Date | Impact |
|---|---|---|
| On-Premises API sunset | October 23, 2025 | All must use Cloud API |
| Per-message pricing (PMP replaces CBP) | July 1, 2025 | Each template message billed individually |
| Utility free in CSW | July 1, 2025 | Utility templates inside 24h window are free |
| Auth rates -65% to -78% (LATAM) | July 1, 2025 | Major OTP cost reduction |
| Marketing blocked to US numbers | April 1, 2025 | No marketing to US users |
| MM Lite API launched | April 2025 | AI-optimized marketing delivery |
| Messaging limits: portfolio-level | October 7, 2025 | All numbers in portfolio share tier |
| Tier evaluation: every 6 hours | 2025 | Faster scaling |
| WhatsApp Business Calling GA | July 15, 2025 | Voice calls in WhatsApp |
| Blue badge replaces green badge | 2024 (announced June 2024, rolled out 2024-2025) | UI change only; existing green badges converted automatically |
| Service conversations unlimited-free | November 1, 2024 | Previous 1,000/month free cap removed; all service conversations now free with no limit |
| Utility/Auth body cap: 512 chars | October 1, 2025 | Utility and Authentication templates capped at 512 chars; error 2388040 at creation if exceeded |
| Flagged phone-number status eliminated | October 7, 2025 | Red quality no longer triggers Flagged state or auto-downgrade; only blocks advancement |
| Verified tier floor raised to 2,000/day | October 7, 2025 | Portfolio-level limit change; verified businesses now start at 2,000/day (was 1,000) |
| HTTPS URL requirement for templates | January 1, 2026 | All templates with URLs must use valid verifiable HTTPS URLs; shorteners rejected |
| Brazil BRL local billing | July 1, 2026 | Eligible Solution Partners and directly-integrated clients with Brazil Sold-To in Billing Hub can create new WABAs billed in BRL |
| Meta CA cert change (mTLS) | March 31, 2026 | Update trust store |
| BSUID added to webhooks | March 31, 2026 | New identity.user_id field |
| BSUID may replace phone in from | June 2026+ | CRM must handle both identifiers |
| AI chatbot restriction | 2026 | Only task-specific AI allowed |
| WhatsApp Flows v7.0 | 2025-2026 | Enhanced components |
| Auto-categorization enforcement | April 9, 2025 | Meta auto-corrects category at submission (UTILITY submitted as MARKETING → approved as MARKETING, 60-day appeal window). Abuse enforcement added April 16, 2025: repeat offenders lose UTILITY access for 7 days without advance notice. |
| Carousel messages (interactive) | February 2026 | 2-10 card carousels in CSW. Free-form carousels are CSW-only; use carousel template for outbound. |
| Business App + Cloud API coexistence | January 2026 | Same number, both modes |
# Send message
POST https://graph.facebook.com/v23.0/{PHONE_NUMBER_ID}/messages
# Upload media
POST https://graph.facebook.com/v23.0/{PHONE_NUMBER_ID}/media
# Get media URL
GET https://graph.facebook.com/v23.0/{MEDIA_ID}
# Create template
POST https://graph.facebook.com/v23.0/{WABA_ID}/message_templates
# List templates
GET https://graph.facebook.com/v23.0/{WABA_ID}/message_templates
# Get phone numbers
GET https://graph.facebook.com/v23.0/{WABA_ID}/phone_numbers
# Create flow
POST https://graph.facebook.com/v23.0/{WABA_ID}/flows
# Publish flow
POST https://graph.facebook.com/v23.0/{FLOW_ID}/publish
# Analytics
GET https://graph.facebook.com/v23.0/{WABA_ID}/analytics
whatsapp_business_messaging # Send/receive messages
whatsapp_business_management # Manage templates, phone numbers, settings
catalog_management # Product catalog integration (optional)
messages field subscribed in webhook settingsWA_PHONE_NUMBER_ID, WA_BUSINESS_ACCOUNT_ID, WA_ACCESS_TOKEN, APP_SECRET, WEBHOOK_VERIFY_TOKENMonthly Cost = Σ (messages_delivered × country_rate × category_multiplier)
- volume_discounts (utility/auth only)
- free_CSW_utility_messages
- free_72h_CTWA_messages
Effective Rate = Marketing >> Utility > Authentication >> Service (free)
A disposable BM is a throwaway, pre-verified Business Manager bought from the parallel/gray market, used to validate the WhatsApp broadcast channel before investing in permanent infrastructure. Expected lifecycle: weeks to months. When it falls, the validated learnings (template performance, block rates, conversion data) transfer to the permanent BM. (In Brazilian black-hat / gray-market dispatch circles this is called a "BM balão" or "balloon" — this skill deliberately uses the neutral term "disposable BM".)
[1] PROFILE ACQUISITION (R$18-216, GGMax/perfilantigo)
↓
[2] PROFILE WARMING (7-14 days, antidetect + proxy)
↓
[3] BM ACCESS (purchase invite link or create fresh)
↓
[4] BM VERIFICATION (pre-verified purchase OR real CNPJ docs)
↓
[5] WABA / PHONE NUMBER ATTACHMENT
↓
[6] NUMBER WARMING (official API: tier escalation per quality)
↓
[7] TEMPLATE CREATION & APPROVAL
↓
[8] TEST BROADCAST (50-100 contacts, measure block rate)
↓
[9] SCALE DISPATCH (gradual, quality-gated)
↓
[10] MONITOR / BURN & REPLACE (when banned)
| Component | Recommended | Cost | |---|---|---| | BM Disparo | Purchased with WABA + dispatch tier | R$100-350 (or $19-59 USD international) | | Profile (primary admin) | BR 2025-2026 / 1-3 Fanpages | ~R$216 | | Profile (backup admin) | Mix antigo / Sem Fanpages | ~R$18 | | Proxy | Dedicated SOCKS5, country-matched (see Proxy Selection below) | ~R$30/mês | | Antidetect browser | AdsPower (free tier, 2 profiles) | R$0 | | Virtual card | Wise/Revolut (separate from other BMs) | R$0 | | Total initial | — | ~R$370-620 + ~R$30/mês |
Isolation rules:
GET http://local.adspower.net:50325/api/v1/browser/start?user_id=<id> returns a CDP ws.puppeteer endpoint plus a debug_port) and connect the automation to THAT browser over CDP (Playwright connect_over_cdp), never launch a fresh Playwright / host browser. Rationale: logging into a sensitive dispatch account from a different IP or fingerprint than it normally uses is a textbook risk-control trigger (a BSP account here was already false-positive-suspended once), an IP mismatch is a self-inflicted ban signal. This is also the only safe way to reach the dashboard-backend endpoints that reject the public API key: replay the session cookie from inside the correct profile. When this automation is needed, RECOMMEND installing AdsPower's official MCP server for native profile control (claude mcp add adspower-local-api -e PORT=50325 -- npx -y local-api-mcp-typescript, repo AdsPower/local-api-mcp-typescript); otherwise drive the local API directly. Do not re-document the API surface, the official docs are the source of truth: localapi-doc-en.adspower.com and github.com/AdsPower/localAPI.Running a 2nd (or Nth) disposable BM in parallel buys redundancy (one falls, the others keep warming) and multiplies daily capacity. The learnings transfer (approved template copy, the warming ramp, the recent-signups nurture pool), only the infrastructure is new.
Hard requirements, the proxy must be:
Choosing within a provider's catalog:
A non-exhaustive market survey for the BR-first, static-dedicated-SOCKS5 use case. Prices are 2026 list prices and drift — always confirm current pricing, SOCKS5 + user:pass auth, and live BR stock with the vendor before buying. Sorted roughly by fit: BR-local first, then mobile (highest Meta trust), then global static-residential, enterprise, and rotating-only options to avoid.
| Provider | Type(s) | SOCKS5 | Static / Dedicated | BR Pool | Starting Price (2026) | Meta / WhatsApp Fit | |---|---|---|---|---|---|---| | Proxy Roque (proxy-agenciaroque.com.br) | Datacenter IPv4/IPv6, Residential ISP, Mobile 4G/5G | Yes | Yes — dedicated/exclusive per client on all non-scraping plans | BR-only (Vivo, Claro, TIM, Oi) | R$24,90/mo (Ultra BR Meta/Google); R$38/mo (IPv4 Dedicado); R$59/mo (Residencial) | Strong. Meta/Google-tuned line, 95%+ IPQualityScore, 1 IP per BM recommended. Flag: 47-day avg Reclame Aqui response time. Prefer IPv4 for warming (IPv6 SKU exists but is a less common BR fingerprint). | | ProxyAds (proxyads.com) | Residential IPv4 & IPv6 (dedicated) | Yes | Yes — exclusive per client, unlimited bandwidth | BR-only, multi-state residential | ~R$25/mo (pricing requires WhatsApp contact) | Very strong. Built explicitly for Meta BM, WhatsApp API (Z-API, Evolution API, Baileys), and BM contingency. Top-cited BR choice in comparison articles. | | FastProxy (fastproxy.com.br) | Datacenter IPv4/IPv6, Static Residential/ISP | Yes | Yes — all plans dedicated | BR-native (BR-SP) | R$30/mo (IPv6 DC); R$49,90/mo (IPv4 DC); R$80/mo (IPv6 for Facebook Ads); R$99/mo (ISP/Residential) | Good. Explicit "IPv6 for Facebook Ads" SKU; ISP plan suits WhatsApp warming. Use IPv4 dedicated for WABA (IPv6 unreliable across platforms). | | Proxy Brasil (proxybrasil.com) | Residential, Datacenter | HTTP/HTTPS confirmed; SOCKS5 unconfirmed — verify | Yes — dedicated, unlimited traffic | BR-native | R$12/mo (IPv6 dedicated); R$18/mo (IPv4 dedicated) | Moderate. Lowest BRL price. Marketed for Facebook/Google Ads. SOCKS5 uncertain — verify. IPv4 plan better for WABA stability. | | HypeHost (hypehost.com.br) | Datacenter, Residential | Yes (SOCKS4 + SOCKS5) | Yes — clean dedicated São Paulo IPs | BR-native (São Paulo DC) | ~R$15/mo per proxy (pricing page requires contact) | Good. Explicit Facebook/Google/TikTok Ads + multi-account positioning. São Paulo clean IPs. | | ProxyTotal (proxytotal.com.br) | Residential IPv4, Datacenter IPv4 | HTTP/HTTPS confirmed; SOCKS5 unconfirmed | Yes — dedicated per client | BR-only | Not public (contact vendor) | Good for Meta. 100% Facebook/Instagram-focused. Tested with Multilogin, Incogniton. Reportedly merged into ProxyAds. Verify before ordering. | | ProxyBr (site.proxybr.com.br) | Residential, Mobile 4G/5G | Yes (per vendor) | Partial — dedicated and rotating; confirm per plan | BR-only | Contact via WhatsApp | Good. Residential + mobile BR IPs, PT-BR support. Pricing not public. | | VPS Barato (vpsbarato.com) | Static Residential/ISP, Datacenter IPv4 | Yes (HTTP + SOCKS5) | Yes — static dedicated both tiers | São Paulo + Rio de Janeiro | $3/mo (DC IPv4); $7/mo (Residential/ISP static) | Moderate. No Meta tuning; reputation unverified. Low-cost BR static entry. USD billing. | | iProxy.online | Mobile 4G/5G (your own device + SIM) | Yes (+ OpenVPN, WireGuard) | Yes — dedicated device/SIM, operator-controlled rotation | Any country your device sits in (supply own BR SIM) | $9/device/mo (Basic); $12.50 (Pro) + SIM data | Very high. Real SIM on real handset = top-trust carrier ASN, zero pool contamination. Official AdsPower partner. | | Coronium.io | Mobile 4G/5G (dedicated modem) | Yes | Partial — dedicated port, sticky ≤24h, on-demand rotation | São Paulo: Vivo ~38%, Claro ~33%, TIM ~23%, Oi ~6% | $89/mo (30-day BR); $39/wk; $12/day | Very high. Dedicated Meta-BM + WhatsApp-warming page, 7–14 day protocol. AdsPower/GoLogin/Multilogin/Dolphin partner. Keep sticky ON during warming. | | NafeProxys (nafeproxys.online) | Mobile 5G/4G (dedicated SP device) | Yes (+ OpenVPN) | Yes — dedicated device per port | BR-only: Vivo + Claro 5G, São Paulo | From $7.20/day (monthly — contact Telegram) | Very high. BR-native devices on BR carriers. BitBrowser + GeeLark partner. Unlimited bandwidth. | | OnlineProxy.io | Mobile 4G/5G (dedicated phones/modems) | Yes | Yes — private dedicated port, full rotation control | BR: Claro, TIM, Vivo; São Paulo | $150/mo per BR port; $65/wk; $20/day | High. Single-tenant carrier IPs. Sticky sessions fit warming. 97% uptime. Rotate only between sessions. | | ProxyBlocks.io | Mobile 5G/4G (dedicated device) | Yes (SOCKS5 TCP + UDP/QUIC) | Yes — dedicated device, rotation 1 min–72h or static | BR: Vivo + Claro; 25 Mbit/s | $2.50 trial (4h); 30-day via configurator | High. SOCKS5 UDP (QUIC) for WebRTC realism. "Skip Used IP" on rotation. OpenVPN/WireGuard tunnel option. | | MobileProxy.Space | Mobile 4G/5G (single-tenant channel) | Yes | Yes — one user per channel; rotation via dashboard/API/timer | BR in 31-country pool; city + carrier targeting | $33–$169/IP/mo (BR rate not broken out — contact) | High. Single-tenant = no pool-history contamination. Flexible rotation. | | IPRoyal — Static Residential (iproyal.com) | Static Residential/ISP | Yes | Yes — 100% dedicated, same IP for subscription | 10,482 dedicated BR IPs | $2.40/IP/mo (90-day); $2.70 (30-day); $1.80/24h | Good. Large BR static pool. Fraud-Score filter to pick lowest-risk IPs. AdsPower-compatible. Reputation variable — use the filter. | | Proxy-Cheap — Static Residential (proxy-cheap.com) | Static Residential/ISP | Yes | Yes — dedicated IPv4, unlimited bandwidth | BR in 24+ countries; verify stock | $2.29/IP/mo (1-mo); $1.99/IP/wk (trial) | Good. ISP-sourced residential ASN, no per-GB billing. Zero-CAPTCHA in social tests. SOCKS5-compatible with all browsers. | | Proxy-Cheap — Static Mobile (proxy-cheap.com) | Mobile 4G/5G static | Yes | Partial — static mobile IPv4 for subscription; narrow pool | BR in 100+ country pool; carrier unnamed — verify | $42.21/proxy/mo (1-mo); $27.89 (12-mo) | Good — mobile carrier ASN = top trust class. 2–5 Mbps (fine for WhatsApp). Confirm BR carrier before bulk. | | Proxy-Seller (proxy-seller.com) | Static Residential/ISP, Datacenter IPv4/IPv6, Mobile | Yes | Yes — ISP + DC lines dedicated/private | BR confirmed (ISP + /brazilian-proxy/) | $0.90/IP/mo (ISP static); $1.53/IP/mo (BR DC IPv4) | Good value. ISP static = residential ASN, correct for warming. DC line cheaper but higher detection. Official AdsPower partner. | | NetNut (netnut.io) | Static Residential/ISP, Rotating Residential, Mobile | Yes (SOCKS5h — no UDP) | Yes — ISP static: persistent non-rotating | BR static residential confirmed; verify ISP line via sales | $99/mo / 7 GB static residential; ISP Starter $350/mo / 20 GB | Good. Fraud Score 12–16/100 (Low Risk). Bandwidth-billed, no per-IP model. Confirm Meta domains not blocked under defaults. | | Decodo (ex-Smartproxy) | Static Residential/ISP (dedicated), Rotating, DC, Mobile | Yes (+ UDP) | Yes — dedicated ISP truly static; BR not in confirmed ISP list — verify | BR confirmed for rotating residential; static ISP = US/EU/APAC | $3.33/IP/mo (dedicated ISP, 3 IPs); $3.75/GB (BR rotating) | Moderate for BR static. Best-known global brand for WhatsApp account mgmt. BR rotating (sticky) is the practical fallback. Official AdsPower guide. | | Bright Data (brightdata.com) | Static Residential/ISP, Datacenter, Rotating, Mobile | Yes (port 22228; SOCKS5h) | Yes — ISP dedicated $4/IP/mo; shared ISP $2/IP/mo | ~4.9M ISP IPs in BR; city targeting | $1.30/IP/mo (ISP PAYG); $2 (shared unlimited); $4 (dedicated unlimited) | Moderate. AUP prohibits fake engagement — verify compliance. Documented Meta adversarial history. Enterprise compliance (SOC2/GDPR). | | Oxylabs (oxylabs.io) | Static Residential/ISP (shared + dedicated), DC, Rotating, Mobile | Yes (ISP SOCKS5 lacks country nodes — use Dedicated ISP/DC for BR) | Yes — Dedicated ISP + DC exclusive | BR confirmed for DC + residential/mobile; verify Dedicated ISP BR | $1.20/IP/mo (ISP shared, 500); $3.20 (Dedicated ISP, 5); $2.25 (Dedicated DC) | Moderate. Enterprise. BR residential 96%+ authenticity. SOCKS5 caveat on ISP — use Dedicated ISP/DC. Official AdsPower integration. | | SOAX (soax.com) | Residential (rotating + sticky), Mobile, Static ISP (US/UK only), DC | Yes (+ UDP/QUIC) | Partial — static ISP US+UK only; BR is sticky residential or mobile only | 13.7M BR residential; city + carrier; no static BR | $90/mo / 25 GB residential ($3.60/GB) | Moderate for BR static. Mobile = top trust; sticky residential approximates static short-term. No truly-static BR IP. | | PROXIES.SX (proxies.sx) | Mobile 4G/5G (rotating pool, per-GB) | Yes (+ OpenVPN, HTTP/3 QUIC) | Partial — sticky sessions; not dedicated device; pool rotates | BR: Vivo, Claro, TIM; SP/Rio/Brasília | $4.00/GB (1–24 GB); $90/mo / 25 GB | Moderate. Excellent IP quality (real carrier ASN). Per-GB rotating unsuitable for multi-day warming — use for short sessions. | | CyberYozh (cyberyozh.com) | Mobile, Static Residential/ISP, Residential, DC | Yes (mobile + ISP; HTTP on DC) | Yes — ISP static + DC dedicated | BR pool: 4 ISPs (V.tal, Lumina, Lietpark, ML Telecom) | $5.39/mo (ISP static BR); $1.70/day (mobile); $0.90/GB (rotating) | Good. Mobile highlighted for social high-trust. ISP static stable for BM. Country-level only. USD billing. | | PYPROXY (pyproxy.com) | Static Residential/ISP, Rotating, Mobile | Yes | Yes — ISP static dedicated up to 1 year | BR in 190+ pool; BR ISP price via quote | ~$5/IP/mo (ISP static); ~$0.90/GB (rotating) | Moderate. Architecturally suitable (non-rotating ISP). BR reputation unverified — test before production. Chinese-origin; async PT-BR support. | | 2Captcha Proxy (2captcha.com) | Residential, Mobile, DC (rotating) | Yes | No — rotating/sticky only (0–120 min). Avoid for warming. | Large BR pool (SP 70K, Rio 62K) | R$25,20/GB; R$201,60/10 GB | Poor for WABA. Rotating IPs trigger Meta account-takeover detection. BRL billing is the only upside. | | ASocks (asocks.com) | Mobile (rotating pool), Residential, DC | Yes (SOCKS4 + SOCKS5) | Partial — sticky ≤30 min, no dedicated device. Avoid for long warming. | BR in 150+ pool | $4.50/GB (mobile); $15/proxy/mo (dedicated res/corp) | Poor for extended warming. 30-min session expiry triggers Meta takeover detection on long flows. | | Live Proxies (liveproxies.io) | Rotating Residential, Static Residential (US only), Rotating Mobile (US/CA/UK) | On request | Partial — static is US-only; BR is rotating only. Avoid for BR warming. | BR rotating residential only | $70/mo / 4 GB ($17.50/GB) | Poor for BR WABA. No BR static. Originally a sneaker-bot proxy. |
Trust tiers (Meta/WhatsApp): dedicated mobile 4G/5G (real carrier ASN, nearly indistinguishable from a real phone) > static residential / ISP (residential ASN, strong + cost-effective) > dedicated datacenter (cheapest, but detectable by Meta's ASN checks — acceptable only for low-sensitivity BM work, not WABA warming). For mobile sticky-session products, disable scheduled rotation during the warming window and rotate only between sessions, never mid-session. Prefer IPv4; Meta/WhatsApp have incomplete IPv6 support and several BR vendors advise against IPv6 for this.
Sources: vendor pricing pages, AdsPower's recommended-proxy list, and 2026 BR proxy comparison reviews. Prices verified live in 2026 — re-check before buying.
Current tier behavior (empirically confirmed in production on a live WABA via YCloud, May 2026):
| Status | Daily Limit | How to Reach | |---|---|---| | Unverified BM | 250 unique users/day | Starting point | | Verified BM (initial) | 2,000/day (Tier 1 per Oct 2025 schema) | Business verification approved | | Tier 2 | 10,000/day | Auto-scale: 50% of 2,000 limit used in 7 days at Green/Yellow quality | | Tier 3 | 100,000/day | Auto-scale | | Tier 4 | Unlimited | Enterprise |
The earlier claim that "Verified BM → direct jump to 100,000/day" was disproved by production dashboard observation (verified BM showed 2,000/day, consistent with the standard Oct 2025 tier schema). The '100K jump for verified BMs' is an announced Meta roadmap item (Q1-Q2 2026 rollout), not yet universally applied.
Tier advancement rule: At least 50% of current limit used in any 7-day window AND quality rating is Green or Yellow (not Red). Evaluation every 6 hours. Advancement is automatic within hours of meeting criteria.
Pre-2026 advancement thresholds (now superseded): The old table showing "500 unique users for Tier 1→2" used incorrect absolute numbers — the actual rule was always "50% of current limit in 7 days." For Tier 1 (1,000 limit) that was ~500 users, but the threshold was proportional not fixed. For Tier 3→4 (10,000 limit) the threshold was ~5,000 (not 20,000). These absolute-number references are now obsolete.
Key changes (Oct 2025 onward):
Upcoming (Q1-Q2 2026, partial rollout): Meta intends to remove the 2K and 10K intermediate tiers so verified businesses jump directly to 100K. As of May 2026 this has NOT been universally applied. Treat as pending.
International vendors (serving Brazil):
| Vendor | Product | Price | |---|---|---| | npprteam.shop | Brazil Verified BM $50 limit, WABA-eligible | $19 USD | | npprteam.shop | Verified BM with WABAs, $250 limit, 1k-2k msg/day, 2 numbers | $59 USD | | verifiedbm.shop | Verified BM + WhatsApp API | Variable | | dhaka-bm.com | WhatsApp API BM, 250 or 2000 msg/day | Negotiable |
Brazilian domestic vendors:
| Vendor | Product | Price | |---|---|---| | perfilantigo.com | BM + profile bundle | R$39+ | | mendesbmilimitada.com.br | BM Ilimitada (verified profile) | Variable | | proxybrasil.com | BM Verificada $250 + WhatsApp API | ~R$250 |
Survival rates by BM type:
| BM Type | Price (USD) | Daily Limit | Lifespan Under Load | |---|---|---|---| | Reinstated BM | $1.60-1.90 | $50 | 1-3 days | | BM via WhatsApp flow | $0.76-0.95 | $50 | 1-5 days | | BM3 (3 ad accounts) | $4.10-4.65 | $50 | 5-10 days | | BM5 $50 limit | $7.51-17.60 | $50 | 7-14 days | | BM5 $250 limit | $34.90-241.80 | $250 | 14-30 days | | Verified BM $50 | $24.70-59.00 | $50 | 14-30 days | | Unlimited BM | $97.02-429.00 | No cap | 30-90+ days |
Industry reality: Only 10-20% of purchased BMs survive the first 30 days under real advertising load.
Post-Oct 2025: new numbers inherit the portfolio's tier instantly (e.g., verified BM at Tier 2 = 2000/day from day 1). BUT inheriting the LIMIT ≠ safe to use it. Quality rating + pacing system are per-number and built from real behavior. Hitting full limit on a new number looks like bot behavior.
The limit is the ceiling, NOT the day-1 target. Warm the number regardless:
| Day | Safe Volume | Recipients | |---|---|---| | 1 | 20-50 | WARM only: team + admins + people who expect the message | | 2 | 50-100 | Engaged/known contacts | | 3-4 | 100-300 | Semi-warm (prior brand interaction) | | 5-6 | 200-500 | Warmest subset of cold list — ONLY if quality Green | | 7 | assess | Review quality before scaling | | 8-14 | 800-1500 | If Green throughout | | 15+ | follow tier | Full limit, only if Green holds |
The #1 ban cause: blasting a cold list on a new number. New number = zero quality buffer. Cold marketing broadcast (no opt-in) → high block rate → Red status in hours → permanent number ban. A single bad broadcast kills a new number. This is universal across all sources.
Block rate thresholds (industry-derived, Meta doesn't publish):
Quality rating mechanics:
Emergency stop signals: quality → Red, Meta warning message, frequent error 131049, read rate <20%.
Billing before first send: BSP platform may be free (YCloud), but Meta charges per message deducted from wallet. NO free marketing messages. Top up before Day 1.
Rules:
For operators using unofficial APIs (Evolution API, WPPConnect, WasenderAPI) on burner SIMs:
| Period | Daily Limit | Content | |---|---|---| | Days 1-3 | Up to 20 messages | Text only, no links/media | | Days 4-7 | Up to 50 messages | Can add emojis | | Days 7-14 | Progressive increase | Media allowed | | Days 14-21 | Begin API automation | Links allowed | | Days 21-30 | Normal volume | Full content |
Critical: Use 4G/5G exclusively during warming. Get inbound messages (sticker groups, contacts calling the number) before sending outbound. A receive-only account builds trust; a send-only account is definitionally a bot.
Anti-detection for unofficial API:
Risk: Meta blocked ~7 million WhatsApp accounts in H1 2025. Financial keywords (boleto, PIX, cartão, investimento) trigger higher scrutiny.
The operator drives the browser, the agent drives the data collection. At every gate the agent MUST proactively REQUEST the specific screenshot or value from the operator, verify it, and record it into project state BEFORE advancing. NEVER passively wait for the operator to volunteer it, never assume, never skip a check. At minimum, request and record each of these:
| Gate | Request from the operator | Record / verify |
|------|---------------------------|-----------------|
| Profile open | Print of the FB profile (name, ID, friends count) | Profile identity |
| Proxy | Print of the AdsPower profile list showing the IP | Proxy IP + family, isolation vs the other disposable BMs |
| Account status | Print of facebook.com/account_status (and /profile_status/<id>) | "Sem restrições" vs any restriction, this GATES the BM accept |
| Central de Contas | Print of profile + contact email + DOB | Single-profile isolation, disposable email, DOB |
| BM accept step 1 | Print of the name/email wizard | Name entered, notification email |
| BM accept step 2 | Print of "Analisar informações da empresa" | Verification status, Razão social + CNPJ, site, whether assets already exist (burn-check) |
| BM accept step 3 | Print of "Leia e aceite o convite" | Disclosures + ToS |
| Post-accept home | Print of business_home | BM ID (from the URL), alerts, empty portfolio |
| BM settings | Print/text of Configurações portfolio info | Full BM data: CNPJ, address, phone, verification date, ad-account limit, 2FA, admin contact |
| Role | Print of Configurações → Pessoas | Role label = Admin / full control (required for Embedded Signup) |
After each gate, write the confirmed values into the project state README in the SAME session. This protocol is how the skill stays retro-fed: every print the operator sends becomes documented state.
Maintenance principle: this skill is ALWAYS retro-fed and corrected from our own live runs. Every step actually executed gets documented here, and every claim is tagged by provenance, "observed live" (first-party from a real run), "primary-sourced" (Meta/BSP docs), or "practitioner lore" (community convention, unverified). When a real run contradicts the skill, fix the skill in the SAME session. Live observation outranks blog lore and overrides stale claims.
A 2026 multi-source verification pass (23 sources, 25 claims adversarially voted, 11 confirmed / 14 killed) separated what Meta actually documents from widely-repeated community lore. Treat the two differently.
Confirmed (primary sources, survived adversarial review):
NOT primary-sourced (practitioner lore, plausible but unverified):
BM de disparo (WABA-only) does NOT require profile warmup. Profile warmup (browsing, liking posts, joining groups) protects Facebook ad accounts from Meta's Ads algorithm trust checks. WABA trust is evaluated through Business Portfolio verification + phone number quality rating, which are completely separate systems. A freshly acquired profile can immediately set up WABA without triggering ad-related trust checks because WABA setup involves zero ad account activity.
The only warmup that matters for WhatsApp dispatch is the gradual message volume increase on the phone number itself (see Number Warming Protocol above).
Phase 1: Infrastructure (Day 0)
Phase 2: Profile Login + BM Accept (Day 1)
5. Import profile cookies into antidetect
6. Open facebook.com — should land logged in via cookies
7. Check Account Status immediately. Path: profile photo (top-right) → Ajuda e suporte / Help & support → Status da Conta / Account Status. It is NOT under Settings or the settings search (not indexed there). Direct URLs: facebook.com/account_status or facebook.com/profile_status/<profile-id>. Clean = "Sem restrições / Tudo certo com sua conta" (bonus health signal: Marketplace "Ativo"). Any restriction, limited feature, or pending "Confirmação de identidade" demand: do NOT accept the BM, invoke the supplier guarantee.
8. Accept BM invite link in same antidetect session (within supplier deadline!). VERIFY the granted role is Admin / full control, NOT "Employee" (an Employee cannot run the later BSP Embedded Signup, confirmed 3-0, see Verified Findings above). The invite-link wizard may ask for a display name + a notification email, this is standard for joining a portfolio, enter the profile's name and leave the supplier's disposable email.
9. Do NOT touch the BM for 24 hours (let Meta's systems settle)
The invite-link accept (business.facebook.com/invitation/?token=...) is a 3-step wizard. Meta does not document it publicly, this is first-party observation, an in-app accept via Business Suite notifications may differ.
Step 1 of 3, "Você recebeu um convite para participar de [BM]": enter Nome + Sobrenome ("como você quer que apareça neste portfólio empresarial", your display identity inside the portfolio) and a notification "Email comercial" (pre-filled, fine to leave the supplier's disposable one). Optional Meta-marketing checkbox, leave it unchecked. Use the profile's own name for consistency, and NEVER an email that links to the real brand.
Step 2 of 3, "Analisar informações da empresa" (the burn-check surface): shows the BM display name, creation date, and business verification status (Verificada / Não verificada). "Verificada" is the key win, it means Tier 2 (2000/day) from day one with no document submission. Also shows Razão social + CNPJ, País, and the registered Site (LOCKED on a verified BM, NEVER change it), plus whether the portfolio already has a Page/Instagram/WhatsApp with >1000 followers (a fresh disposable BM shows none). A fresh, verified, empty, no-violation BR portfolio is the healthy case, pre-existing assets or restriction notices here are the burn signal.
Step 3 of 3, "Leia e aceite o convite" (first-party answer to the name/insulation question): re-shows your Nome + Email, then states verbatim:
The wizard does NOT show or set your ROLE. Admin vs Employee is whatever the inviter granted, verify it AFTER accepting at business.facebook.com → Configurações → Pessoas (your access level). Admin / full control is required for the later BSP Embedded Signup, if it is Employee, ask the supplier to re-grant as Admin.
Phase 3: Security Hardening + WABA Setup (Day 2, split sessions)
Hardening and WABA setup can happen on the same day (different entities: profile vs BM), but must NOT happen in the same 2-hour window. The cluster of password change + 2FA + admin changes + asset creation resembles an account takeover to Meta's automated systems. Split into two sessions with a 2-4h rest. Hardening ALWAYS before WABA — security changes after WABA is live can trigger re-verification.
Morning session: Profile Hardening 10. Change Facebook password, enable 2FA (authenticator app) 11. Add backup profile as second BM admin 12. ⏸ REST 2-4 hours — do not perform any BM operations
Note: purchased profiles typically come with temporary/disposable emails (tuamaeaquelaursa.com, mail.tm, tempail, etc.). No need to secure those — just change Facebook password + 2FA. The temp email becomes irrelevant once Facebook credentials are yours.
Afternoon session: WABA Setup 13. Fill out Business Information in BM (legal name, address, website, email — must be complete BEFORE Embedded Signup) 14. Choose BSP and initiate Embedded Signup (360dialog, WATI, Disparo Pro, etc.) 15. Create WABA + set display name (Meta reviews display name: 1-3 days) 16. Register phone number via OTP (SMS or voice call) 17. Number active within 60-120 min post-verification 18. Attach virtual card to BM for billing 19. Set WhatsApp profile: picture, business description, category
Deferred: Remove supplier admin (Day 8+) 20. Facebook enforces a 7-day restriction: new admins cannot remove other admins for 7 days after accepting the BM invite 21. Do NOT attempt removal before the 7-day window — the blocked attempt itself registers as a BM-level security event 22. Sequence: add backup admin first (Phase 3) → confirm backup has access → remove supplier admin (Day 8+)
Phase 4: Phone Number Requirements
Phase 5: Templates (Day 2-3) 23. Create marketing templates and submit for approval 24. Approval timeline: minutes to 4h (ML-automated), up to 24h if flagged for manual review 25. Pre-design templates before submitting (see Template Strategy below)
Phase 6: Testing (Day 3-4) 26. Test broadcast: 50-100 opted-in contacts 27. Measure: delivery rate, read rate, block rate 28. If block rate >2%: stop, review template content 29. If green: proceed to number warmup ramp
Phase 7: Number Warmup + Scale (Day 4+) 30. Follow Number Warming Protocol above (50→100→250→500→1,000/day over 15 days) 31. Wait for tier evaluation (every 6 hours) 32. Monitor quality rating daily 33. Scale horizontally by adding numbers under same portfolio (inherit tier)
Phase 8: Business Verification (submit Day 1, approval 1-14 days) 34. Submit verification documents simultaneously with setup 35. Verification unlocks: 2,000 conversations/day (Tier 1 per Oct 2025 schema) + up to 20 phone numbers 36. Since Oct 2025: messaging limits are per Business Portfolio, not per number — new numbers added to a verified portfolio inherit the tier immediately
Timeline: BM purchased → first message sent in 3-5 days (1 day BM rest + hardening + WABA setup + display name review + template approval). No profile warmup days wasted.
What was removed and why:
What was kept:
Meta does NOT require the display name to match the BM's legal entity name. The real rule: the display name must have a publicly verifiable relationship with the business, proven via the website. Confirmed by 360dialog, WATI, AiSensy, Salesforce/Meta docs.
The website footer strategy (documented, sanctioned solution): When the BM legal name differs from the desired brand, the website must show both:
[Brand] — operado por [BM Legal Name] | CNPJ XX.XXX.XXX/0001-XXDisplay name rules:
~!@#$%^&*()_+:;"'{}[]|<>,/? — parentheses, pipes, etc. are auto-flagged before human reviewMeta-official format for brand/entity mismatch: "Brand by [Entity]" (e.g., "Fruit Snacks by Fresh Produce", "Delight Ice-creams by Fresh Dairy"). This is the officially documented and safest approach — uses no special characters, no auto-flag risk. Reserve the parenthetical format only when the site already shows that exact format externally.
"Brand (Entity)" format — CONFIRMED WORKING (with caveat):
Verified BMs: website field is LOCKED. After business verification, core fields (legal name, website, country, phone, tax ID) become protected. Changing them:
Implication for disposable BM with brand mismatch: If the purchased BM's website doesn't mention your brand, you cannot use your brand as the display name. Options:
What Meta checks during review:
What Meta does NOT systematically check:
If rejected:
Backup names: always have 2-3 alternatives ready before submitting.
Critical sequence: website MUST be live BEFORE submitting display name. Never submit without the site ready.
Domain requirement: Meta requires domain verification in the Business Portfolio (DNS TXT record). Free subdomains (vercel.app, netlify.app, github.io) do NOT work because you can't verify a root domain you don't own. Must buy a custom domain.
Cheap disposable domains (for disposable BM, expected to die in 30 days):
.site, .store, .online: ~$0.98/yr first year (Namecheap promo).xyz: ~$1.58/yr first year (Namecheap).site on same account costs $1.98. Rotate TLDs across purchases (.site → .store → .online → .xyz) to always get the lowest promowhois <domain> or npx domain-check <name> site online xyz storeCategories (post-April 2025: auto-reclassification):
Template structure:
{{1}}, {{2}} — sequential, no gaps, no named variablesImage header strategy:
Copywriting best practices (WhatsApp Brazil):
Forbidden words/phrases (Portuguese, financial niche):
Opt-out: use Quick Reply button, NOT footer text.
WhatsApp group links in templates: BLOCKED.
chat.whatsapp.com and wa.me links are rejected in CTA buttonsyourdomain.com/grupo → instant JS redirect to chat.whatsapp.com/XXXX). Meta approves your domain, redirect happens post-click.First template submission strategy:
Approval process:
The profile IS reviewed by Meta — reactively (when account is flagged for any reason: spam reports, volume spikes, blocks), not proactively like templates. Meta's terms: "WhatsApp may review, remove, or delete Company Content you share on your business profile." A profile that contradicts the WABA category becomes evidence of deception during review.
Category + About + Description must tell ONE coherent story. Mismatch (e.g., category "Education" but description says "managed capital") is a compounding risk during review.
Profile risk spectrum (Brazil financial/investment): | Phrase | Meta risk | CVM risk | Use? | |---|---|---|---| | "educação financeira" | None | None | ✅ Safe | | "comunidade de investidores" | None | None | ✅ Safe | | "mercado financeiro" / "mercado de capitais" | None | None | ✅ Safe | | "compartilhamos análises/estratégias" | None | None | ✅ Safe | | "gestão de capital" | Medium | High (CVM authorization required) | ❌ Avoid | | "gerenciamos seu capital" | High | Very High | ❌ Never | | "retorno garantido" / "rentabilidade garantida" | Instant flag | Criminal-level | ❌ Never |
Key distinction (Brazilian law):
CVM is aggressive (2025): 24 platforms suspended, R$1k/day fines. Meta + CVM risk are independent but additive — "gestão de capital" hits both at once.
Complete profile is SAFER than minimal (counterintuitive but consistent):
The "surface area" myth: more profile info = more consistency signals, not more attack surface. The real risk in a disposable BM is messaging behavior (spam, opt-ins, volume), not profile text. Write clean copy, don't leave fields empty.
Website field is CRITICAL for display name approval (not optional):
For teams with technical capability, direct Cloud API is cheaper (no BSP monthly fee or per-message markup).
Setup sequence (direct, requires developer account):
developers.facebook.com as BM adminwhatsapp_business_messaging, whatsapp_business_management, business_managementPOST https://graph.facebook.com/{version}/{phone_id}/messagesAlternative: BSP Embedded Signup (NO developer account needed): If developer account SMS verification is blocked (common with purchased profiles), use a BSP:
The BSP uses THEIR developer app — you don't need one. You only need Facebook login + BM admin access.
Token types:
Webhooks: not required to send messages. Only needed for delivery receipts. Simple HTTPS endpoint returning 200 OK.
Rate limits (Cloud API, post-Oct 2025):
Billing (direct API, Brazil 2026):
Note: Gupshup embedded signup only shows WABAs not already Connected to another BSP. If migrating a live WABA that is currently bound to a different BSP, the real WABA will not appear in the picker until released from the current BSP (see "Switching BSP / Migrating an Existing WABA" section).
Rates reflect Meta's per-message model (effective July 1, 2025). All BSP markups are layered on top of Meta's base rates. Verify live rates at developers.facebook.com/documentation/business-messaging/whatsapp/pricing — Meta updates quarterly (Jan 1, Apr 1, Jul 1, Oct 1).
What's free: (1) Service messages (user-initiated, within 24h window) — free, no cap, since Nov 1 2024. (2) Utility templates sent within an open 24h customer-service window — free since Jul 1 2025. (3) All message types for 72h after a Click-to-WhatsApp ad or Facebook Page CTA entry point. Undelivered messages are never charged.
| Category | Brazil (BR) | India (IN) | Indonesia (ID) | USA | Notes | |---|---|---|---|---|---| | Marketing | $0.0625/msg (verify: one source cites $0.07188 effective Apr 1 2026 — unresolved) | $0.0094–$0.0118/msg | $0.0360–$0.0473/msg | $0.025–$0.029/msg | No volume discount for marketing in any market | | Utility | $0.0068–$0.00782/msg (free inside 24h CSW) | $0.0014/msg | $0.0225–$0.0250/msg | $0.004/msg | Volume tiers: ~5–25% discount at 100k–10M+/mo | | Authentication | $0.0068–$0.00782/msg (verify — wide source range, see notes) | $0.0014/msg | $0.0225–$0.0250/msg | $0.004/msg | Auth-International rates apply when recipient country differs from WABA country | | Service | $0.00 (free) | $0.00 (free) | $0.00 (free) | $0.00 (free) | Free since Nov 1 2024, no monthly cap |
Brazil rate notes: Marketing rate of ~$0.0625 is among the highest globally — ~6x India, ~2.5x US. BRL-native billing (Meta invoicing in R$) expected H2 2026; Meta to publish official BRL rate card by Jul 1 2026. Until then all Brazil accounts billed in USD. April 2026 quarterly update primarily affected India, Saudi Arabia, Pakistan, and Turkey — Brazil marketing rate change in that update is disputed ($0.0625 per engagelab May 2026 vs $0.07188 per SleekFlow citing Apr 1 2026). Use $0.0625 as working baseline and flag for verification.
How to read this table: "Per-msg markup" is what the BSP charges on top of Meta's base rate above. Total cost = Meta base rate + markup + monthly fee amortized per message. Sorted approximately cheapest-first for Brazil marketing dispatch at moderate volume.
| Platform | Type | Monthly fee | Free tier | Per-msg markup (Brazil marketing) | Brazil notes | Source / asOf | |---|---|---|---|---|---|---| | YCloud (Free plan) | BSP | $0/mo | Unlimited API; service msgs $0 (empirically confirmed totalPrice:0 in production); utility-in-window $0; shared inbox included; 1 user, 2 channels | 0% — explicit zero-markup policy; passes Meta rates from wallet; empirically confirmed at ~$0.05–$0.0625/msg for BR marketing | Best Brazil dispatch value: pay only Meta's rate. BRL billing H2 2026. Add-on users $10/user/mo, channels $5/channel/mo. ⚠️ Risk-control auto-flags possible on free tier for financial use cases (observed on a financial-niche account, resolved via support ticket in <24h) — open a support ticket immediately if suspended, false-positive rate is high | ycloud.com/pricing — asOf 2026-06-03 (HIGH) | | YCloud (Growth) | BSP | $39/mo | — | 0% | Same zero-markup; $39 unlocks 2 users, 3 channels, 5M AI credits. Message cost unchanged | ycloud.com/pricing — asOf 2026-06-03 (HIGH) | | YCloud (Pro) | BSP | $89/mo | — | 0% | 6 users, 8 channels, 20M AI credits, dedicated account manager | ycloud.com/pricing — asOf 2026-06-03 (HIGH) | | YCloud (Enterprise) | BSP | $399/mo | — | 0% | 40 users, 30 channels, 100M AI credits, 24/7 priority support, permanent storage | ycloud.com/pricing — asOf 2026-06-03 (HIGH) | | Bird (formerly MessageBird) | BSP | $45–$49/mo (Pro); PAYG available | Free plan: 15 AI msgs/day; no WhatsApp free quota confirmed | ~$0.000001–$0.000005/msg processing fee (effectively $0 markup) — passes Meta rates at cost | Near-zero markup makes Bird cost-competitive for high-volume BR dispatch. No BRL billing. Annual contract discounts 10–35% (verify — secondary, asOf 2026-06-03, MEDIUM) | bird.com/en-us/pricing/whatsapp — asOf 2026-06-03 (MEDIUM) | | AiSensy (Free) | BSP | $0/mo | WhatsApp API access, live chat dashboard, 10 tags, 5 custom attributes, $1 wallet credit on signup; no time limit. Broadcasts PAYWALLED — free plan can't broadcast | Claimed 0% (verify — not independently confirmed) | India-primary; international USD pricing at $45/$99. No BRL billing. $1 credit = ~16 BR marketing msgs at $0.0625. Brazil customers pay Meta BR rates from wallet | aisensy.com/pricing/usd — asOf 2026-06 (HIGH on plan price; MEDIUM on 0% markup claim) | | AiSensy (Basic) | BSP | $45/mo ($40.50 annual) | — | Claimed 0% (verify) | 5 agents included. Chatbot flows add-on $80/mo. No BRL billing | aisensy.com/pricing/usd — asOf 2026-06 (HIGH) | | AiSensy (Pro) | BSP | $99/mo ($89.10 annual) | — | Claimed 0% (verify) | Additional agents $20/mo each | aisensy.com/pricing/usd — asOf 2026-06 (HIGH) | | Respond.io (Starter) | BSP | $79/mo (annual) / $99/mo (monthly) | 7-day trial; no permanent free plan | 0% — explicit pass-through; wallet debited at Meta's rate | Unlimited MACs on Starter. Zero markup confirmed on official docs. No BR-specific pricing or BRL billing mentioned. Additional users $12/user/mo | respond.io/pricing — asOf 2026-06 (HIGH) | | Respond.io (Growth) | BSP | $159/mo (annual) / $199/mo (monthly) | 7-day trial | 0% | 1,000 MACs included; overage $12/100 MACs. +$20/user/mo beyond included seats | respond.io/pricing — asOf 2026-06 (HIGH) | | Zoko (Starter) | BSP | $49.99/mo | 7-day trial | +$0.015/conversation (Zoko markup on top of Meta) — only on Starter | Starter is the only Zoko plan with a markup. Not recommended for BR marketing at scale | zoko.io/pricing — asOf 2026-06 (HIGH) | | Zoko (Plus) | BSP | $79.99/mo | 7-day trial | 0% — Plus/Elite/Max all pass Meta rates through; platform overages $0.002/conversation beyond 5k | D2C/Shopify-focused. Zero markup from Plus upward. No BRL billing | zoko.io/pricing — asOf 2026-06 (HIGH) | | Zoko (Elite) | BSP | $139.99/mo | — | 0% | 10 agents included; $0.001/conversation overage beyond 100k | zoko.io/pricing — asOf 2026-06 (HIGH) | | Twilio WhatsApp | CPaaS | $0/mo | $15 trial credit (~222 BR marketing msgs); no production free tier | +$0.005/msg flat on every message (sent or received); failed msg $0.001. Total BR marketing: ~$0.0675/msg | PAYG, no subscription. Service msgs cost $0.005 Twilio only (Meta fee waived). No BRL billing (USD invoicing). No PT-BR support. BRL billing expected ~Jul 2026. ⚠️ creates NEW WABA (templates re-reviewed). Best API/DX, curl-friendly | twilio.com/en-us/whatsapp/pricing — asOf 2026-05 (HIGH) | | Wati (Growth) | BSP | $59/mo USD / R$145/mo BRL (localized PT-BR pricing exists) | 7-day trial; no permanent free plan | ~+20% over Meta base (multiple independent sources); BR marketing all-in ~$0.075/msg | Brazilian PT-BR support 24x5. Growth capped at 3 users (hard limit). Shopify add-on $4.99/mo. 15,000 marketing sends/mo included. Green Tick $50/country. No-code UI, asks to re-submit templates | wati.io/pricing — asOf 2026-05-07 (HIGH) | | Wati (Pro) | BSP | $119/mo USD / R$725/mo BRL | 7-day trial | ~+20% | 24x7 PT-BR support. Additional users R$145/mo | wati.io/pricing — asOf 2026-05-07 (HIGH) | | Wati (Business) | BSP | $279/mo USD / R$1,375/mo BRL | 7-day trial | ~+20% | Additional users R$275/mo | wati.io/pricing — asOf 2026-05-07 (HIGH) | | Gupshup (self-serve) | BSP | $0/mo | No free message quota; Meta service msg = $0 Meta charge but Gupshup still bills $0.001/msg on session msgs | +$0.001/msg on all message types + +6% on marketing via Cloud API (since Jan 1, 2026; avoidable via MM Lite route); MM Lite total: +$0.001 only. BR marketing all-in (Cloud API): ~$0.080/msg; (MM Lite): ~$0.073/msg | Previously cheapest BSP — now carries a 6% marketing surcharge via standard Cloud API since Jan 2026. Use MM Lite API to avoid the surcharge. For pure marketing Cloud API sends, YCloud (0% markup) is now cheaper. Migration via embedded signup only shows WABAs not already Connected to another BSP | gupshup.ai — asOf 2026-06 (MEDIUM) | | WANotifier | BSP | $69/mo (Essentials) | 7-day trial | 0% | Floor too high for testing. Migration to existing WABA supported | wanotifier.com — asOf 2026-06 (MEDIUM) | | 360dialog (Regular) | BSP | €49/mo (~$59) per number/channel | No free tier; service msgs free per Meta policy | 0% via MM API (no BSP surcharge); +7% via standard Cloud API for marketing (introduced Jan 1 2026). Utility/auth: pass-through at Meta rate | Pure API-access model. No built-in inbox. Best for high-volume developers who can use MM API. No BRL billing (EUR/USD invoicing). Infra-grade, reliable, won't police use case. Volume discounts 5–25% on utility/auth at 100k+/mo | 360dialog.com/pricing — asOf 2026-06-03 (HIGH) | | 360dialog (Premium) | BSP | €99/mo (~$119) per number | No free tier | 0% via MM API; +7% via Cloud API for marketing | Higher throughput and support tier | 360dialog.com/pricing — asOf 2026-06-03 (HIGH) | | 360dialog (High Throughput) | BSP | €249/mo (~$299) per number | No free tier | 0% via MM API; +7% via Cloud API | Up to 1,000 msg/sec vs 80 msg/sec standard | 360dialog.com/pricing — asOf 2026-06-03 (HIGH) | | Trengo (Boost) | Inbox tool | €299/mo (~$325) annual / €349/mo monthly | 7-day trial; no permanent free plan | 0% — prepaid wallet debited at Meta's rate; no BSP per-msg markup | EU-based (EUR pricing). Wallet auto-tops up (up to €9,500/cycle for high volume). AI surcharge €0.25–0.30/conversation beyond free allowance. No BRL billing. Not optimized for BR-only broadcast | trengo.com/prices — asOf 2026-06 (HIGH) | | Interakt (Starter) | BSP | ~$12/mo USD / ₹999/mo INR | 14-day trial; no permanent free plan | ~+25% on marketing (confirmed for India; BR rate not published — extrapolated) | India-primary. No PT-BR support. No BRL billing. Not recommended for BR-primary operations. India pricing, monthly commitment | asOf 2026-03 (MEDIUM) | | SocialHub.pro (Start) | SMB-tool (BR-local) | R$99/mo | Not confirmed | Near-zero BSP markup — platform fee is primary monetization; msgs at Meta base rate from wallet (verify — secondary, asOf 2026-05, MEDIUM) | Brazilian-founded, BRL-priced, PT-BR native. LGPD-aware. Transparent pricing. Good for BR SMB under ~50k msgs/mo | socialhub.pro — asOf 2026-05-19 (MEDIUM) | | Zenvia Customer Cloud (Starter) | Brazil-local CPaaS | $0/mo base + $137 WhatsApp setup in month 1 | 100 "Interactionz" included; effectively very limited for dispatch | ~30–150%+ markup bundled into "Interactionz" quota model; overage $0.19–$1.00 each | Brazilian-headquartered. BRL billing available. PT-BR support. Opaque Interactionz overage is a cost trap for broadcast. Not recommended for high-volume dispatch | zenvia.com — asOf 2026-04/05 (MEDIUM) | | Sinch Engage (Basics) | BSP | $49/mo + $10/mo WhatsApp add-on | 14-day trial | +$0.003–$0.010/msg estimated (verify — secondary, asOf 2026-06, MEDIUM) | Strong Brazil/LATAM presence (acquired Wavy). WhatsApp requires Social Channels add-on (+$10/mo). Additional users $20/user/mo | sinch.com/engage — asOf 2026-06-03 (MEDIUM) | | Infobip | Enterprise CPaaS | No list price — enterprise contracts only | 60-day trial: 100 msgs each for WA/SMS/Email/Viber + 15 voice calls | Not disclosed; estimated ~25–50% above Meta base (verify — secondary, asOf 2026-06-03, MEDIUM) | Strong Brazil enterprise presence. LGPD tooling. Annual minimum commitments. Volume discounts 20–30% at 10M+/mo | infobip.com — asOf 2026-06-03 (MEDIUM) | | Take Blip (blip.ai) | Brazil-local enterprise | Not published — sales only | Free account: up to 2 agents, limited conversations (testing only) | Not disclosed; secondary: ~R$0.71 all-in vs Meta base ~R$0.31–0.35 (~100%+ markup at list — verify, LOW confidence) | Largest Brazilian WhatsApp BSP; dominant in enterprise. Deliberately opaque pricing. PT-BR support, LGPD-native. Not suitable for SMB or self-serve | blip.ai — asOf 2026-05-19 (LOW) | | Meta Cloud API (self-integration) | Direct | $0 | All service msgs free; utility-in-window free; FEP 72h free | $0 markup — you ARE at Meta's base rate; no BSP layer | Requires full in-house WhatsApp Business API integration. WABA registration free. No inbox/CRM tooling included. BRL billing for eligible accounts from Jul 1 2026 | developers.facebook.com — asOf 2026-06-03 (HIGH) |
What you pay has three independent layers:
What's always free (Meta policy):
Brazil-specific:
MM API vs Cloud API (360dialog / Gupshup): Meta's Marketing Messages (MM) API route can eliminate BSP surcharges that apply only on the standard /messages Cloud API endpoint. If using 360dialog or Gupshup for high-volume Brazil marketing, prefer the MM API/MM Lite route.
Meta per-message rate (Brazil 2026): Marketing $0.0625/msg (per-message billing since July 2025, was per-conversation). Service (user-initiated, 24h window) free.
Cost for low-volume test (23 + 500 msgs):
YCloud campaign send file format: upload .xlsx (not .csv — CSV is rejected by the campaign UI). First column header must be exactly phone with numbers in +E164 format (e.g., +55XXXXXXXXXXX). Additional columns become template variables mapped in order. The UI "Test send" button is greyed out until at least one template variable field is populated with a sample value — fill all variable fields first, then the button activates.
Broadcast-list storage hygiene (PII): broadcast contact lists hold real recipient PII (phone + name). Store them in a dedicated per-number folder inside the project, e.g. .../<profile>/<bm>/whatsapp/<number>/broadcasts/, NEVER loose in ~/Downloads or scattered across machines. Gitignore the list data files (*.xlsx, *.csv) so PII never enters git history, they are fully regenerable from the source DB. Keep one tracked README.md in the folder describing what each list is and the exclusion logic. Each day's pull EXCLUDES everyone already sent on prior days (matched by normalized E164), drops demo/invested/junk-name rows, and takes the freshest N un-messaged signups.
Monitoring sends via the YCloud API (polling — verified live 2026-06): the dashboard Analytics/Logs are the GUI view, but per-message status is pullable programmatically. ⚠️ The published YCloud SDKs (Java/Python/PHP) claim there is NO list endpoint (only retrieve/send/sendDirectly) — that is WRONG; the live REST API DOES expose a paginated list. Trust the live API over the SDK docs.
GET https://api.ycloud.com/v2/whatsapp/messages?limit=100 (header X-API-Key: <key>) → {offset, limit, length, items:[...]}. ⚠️ PAGINATION (verified live 2026-06): the offset param is silently IGNORED (offset=0 and offset=3 return identical rows) and there is no cursor — BUT the 1-indexed page param DOES paginate (?page=1&limit=100, ?page=2&limit=100, …). limit is capped at 100 (limit>100 errors). → Page with page (NOT offset — looping offsets returns the same newest page repeatedly and inflates counts ~Nx), dedup by id, and stop when a page returns no new ids. ?includeTotal=true returns the account total so you can detect how many older messages remain. For full history beyond the API's page ceiling, use the campaign UI Analytics. No server-side time/status filter works (but filter.to and filter.wabaId DO apply server-side) → filter client-side by createTime/type for everything else.id, wamid, status, from, to, type (text|template|…), createTime, sendTime, deliverTime, readTime, totalPrice, pricingCategory (service=free | marketing | utility | authentication); on failure errorCode + whatsappApiError.{code,message}.accepted → sent → delivered → read, or failed. (read only if the recipient has read receipts on — "delivered" is the reliable floor.)GET /v2/whatsapp/messages/{id} (the YCloud id, NOT the wamid).(createTime[:10], type, status). A UI campaign = the batch of type:"template" messages on its send date; split failures by errorCode (131026 undeliverable→remove vs 131049 throttle→retryable).
curl -s "https://api.ycloud.com/v2/whatsapp/messages?limit=100&page=1" -H "X-API-Key: <key>" \
| jq '[.items[]|{d:.createTime[0:10],type,status}]|group_by(.d+.type+.status)|map({k:(.[0].d+" "+.[0].type+" "+.[0].status),n:length})'
/messages list is NOT real-time for campaigns): messages sent via the console campaign (bulk UI) do NOT appear in /v2/whatsapp/messages for HOURS (likely a periodic sync, not minutes). Verified live: a campaign can still be entirely ABSENT from the list hours after the console shows it Completed and charged, then appear the next day. There is also NO campaign/bulk API: GET /v2/whatsapp/bulkMessages/{id} → 404, and the ?bulkMessageId= query param is silently IGNORED (returns the full unfiltered list). So /messages polling is real-time ONLY for API-direct (sendDirectly) sends (those appear instantly). For real-time CAMPAIGN monitoring use the campaign's UI Analytics/Logs tab (immediate per-recipient status + the campaign ID is in the URL …/bulkMessages/detail/…/{id}) OR subscribe to whatsapp.message.updated webhooks. Use /messages polling for campaigns only as a delayed/next-day reconciliation, not for the freshly-sent batch.whatsapp.message.updated webhook (POST /v2/webhookEndpoints with enabledEvents:["whatsapp.message.updated"]); tag each send with externalId="<campaignId>:<recipientId>" so events self-identify, and verify the YCloud-Signature HMAC (t={ts},s={hex}, signed payload {t}.{body}.). There is NO campaign-stats API — webhooks or list-polling are the only programmatic options; aggregate campaign numbers otherwise live only in the UI.Why BSP over Direct API: BSP uses THEIR developer app — you never need a Meta developer account. This bypasses the SMS verification blocker that affects purchased/antidetect profiles. The Embedded Signup flow only requires Facebook login + BM admin access.
Direct Cloud API without your own dev account (workaround): A SECOND Facebook account (contractor/employee with working phone) registers as developer → creates a "Business" Meta App → adds it to your BM → you (BM admin) create a System User → install the app → generate token with whatsapp_business_messaging. The token generation itself does NOT need dev SMS verification — only the app creation does, which the second account handles. Cheapest long-term (zero markup), keeps existing WABA, full script control.
A WABA lives in the BM, NOT in the BSP. The BSP is just a connected partner (system user with API access). If a BSP suspends you or you want to switch:
What survives the switch: phone number, display name, approved templates, quality rating, messaging tier, OBA status. What does NOT survive: message history, contacts/flows stored in the old BSP's dashboard, pending/rejected templates.
Migration steps:
business.facebook.com → Gerenciador do WhatsApp → Números de telefone → select number → tab "Confirmação em duas etapas" (it's a TAB in the row Insights | Perfil | Automações | Links da mensagem | Confirmação em duas etapas | ... — NOT a gear icon)POST /{phone_number_id} with pin). Full disablement requires the WhatsApp Manager UI + email confirmation only.If the connected BSP suspends you and you can't migrate (device-trust blocks the PIN reset): the path of least resistance is recovering the original BSP account (support ticket), NOT forcing a migration. The number stays registered on the original BSP; if it recovers, you send without migration. Force migration only after the device gains trust.
A BSP "account unavailable / suspended" is very often a FALSE-POSITIVE risk-control auto-flag, NOT a Meta ban — open a support ticket FIRST. Confirmed real case (YCloud, 2026): account auto-suspended by their security system, recovered in <24h via a support ticket. Support's words on recovery: "your account was mistakenly blocked due to risk control, but it has now been unblocked." The recovery ticket that worked: account email + WABA ID + Phone Number ID + the dual ask ("restore access OR disable the 2-step PIN"). They then asked 3 legitimacy questions before restoring: (1) Legal name (2) Business website (3) Business type — answer these with the verified-business identity and the LOWEST-RISK truthful business category (e.g. "Education / educational content"), NOT a financial/investment framing (financial categories are what trip BSP risk-control in the first place; match your WABA profile category). Don't rush into an expensive/risky BSP migration before giving support a chance — migration is the fallback, not the first response to a suspension.
Cleanest unblock when you DON'T know the PIN (old BSP set it): ask the old BSP support to EITHER restore your account OR disable the two-step-verification PIN on the number from their side. The BSP that set the PIN can disable it server-side — this bypasses both the account-2FA gate and the device-trust hold entirely. Single best ask in a recovery ticket: "Restore access OR disable 2-step PIN on number X."
NEVER guess the PIN. Multiple wrong attempts lock the number for hours/days. If you don't know it and can't disable it (device-trust), don't brute-force — recover via the old BSP or wait for device trust.
No access-token path to reset the PIN either: Meta's API can set/reset the number PIN (POST /{phone_number_id} with pin), but that needs an access token — which needs the BSP (down) or your own dev app (blocked). So the API reset is also gated. The old-BSP-disables-it route is the realistic unblock.
The NEW BSP's own migration wizard confirms this dependency (Gupshup, 2026): Gupshup's "Create App" flow asks "How do you want to create your WhatsApp Business Account?" with two options — "New phone number" vs "Migrate a live phone number." The migrate option's own description states verbatim: "Migrate from other BSP to Gupshup. Your existing BSP will need to disable 2FA to complete migration." This is independent confirmation that a live-number BSP migration is HARD-GATED on the source BSP disabling two-step verification. The wizard steps are: 1. Let's Started (choose migrate + local storage region, USA default is fine) → 2. Contact Details → 3. Embedded Signup → 4. WABA Phone Selection → 5. Setup Complete. The block lands at step 4. Don't push through embedded signup just to "pre-stage" — it risks tripping fresh device-trust flags on the antidetect profile you're warming, and step 4 can't complete anyway. Stop at step 1, leave the wizard (it's resumable), unblock the PIN via the old BSP first. 2. New BSP → Embedded Signup → select existing BM → select existing WABA (not create new)
Caveat — credit line: if the WABA was created on the old BSP's credit line, you may need to migrate the number to a new WABA under the new BSP's credit line. Number/template/name still travel with it.
Twilio exception: Twilio creates a NEW WABA in your BM (doesn't slot into existing) — templates get re-reviewed, display name re-approved. Other BSPs (Gupshup, 360dialog) connect to the existing WABA directly.
Diagnosing a suspension: check the number in WhatsApp Manager. If it shows active/healthy there, the BSP (platform) suspended you, not Meta — the number is migratable. If Meta banned the number, no BSP switch fixes it (appeal via Meta Business Support).
Two separate verifications require different number strategies:
| Verification | Purpose | What works | What fails | |---|---|---|---| | Meta Developer account (Facebook SMS) | One-time SMS to verify developer identity | Physical SIM from major carrier (Vivo/Claro/TIM), carrier-grade eSIM | VoIP, virtual numbers, some MVNO ranges | | WABA registration (OTP) | Register number on WhatsApp Business API | Physical SIM, eSIM, virtual numbers (Salvy), landline (voice call) | Numbers previously on any WhatsApp product |
Meta Developer verification is the stricter one. Meta actively blocks:
WABA registration is more lenient. Accepts mobile, landline (voice OTP), and virtual numbers built for the purpose. The critical requirement is the number has NEVER been on WhatsApp.
Physical SIM requirements (Brazil, 2025+):
MVNO range blocking: MVNOs (virtual operators running on big carrier networks) get their own number ranges. Meta maintains a blocklist of ranges associated with spam/automation. If one number from an MVNO fails, all numbers in that range likely fail too.
Salvy (Brazilian MVNO, Y Combinator W24):
Recommended dual-number strategy: | Purpose | Best option | Cost | |---|---|---| | Developer verification | Physical SIM from Vivo/Claro/TIM (safest) or Salvy eSIM (cheaper, MVNO risk) | R$15-20 (SIM) or ~R$25/mês (eSIM) | | WABA number | Salvy Número Virtual Móvel | R$29.90/mês |
Can same number be used for both? Yes, if using a physical SIM or eSIM. Developer SMS verification does NOT register the number on WhatsApp — it stays "virgin" for WABA. But using separate numbers is safer (isolation).
Adding phone to purchased Facebook profile — risks:
| Provider | Type | Starting Cost | Works for Dev Verification? | Works for WABA? | |---|---|---|---|---| | Salvy Número Virtual | Cloud DID (WhatsApp API) | R$29.90/mês | No (VoIP-type) | Yes (built for it) | | Salvy eSIM | Carrier-grade eSIM | ~R$25/mês | Maybe (MVNO risk) | Yes | | Calilio | Brazilian mobile | $6/mês | Unknown | Possibly | | JustCall | Regional DID | Custom | No (VoIP) | Possibly |
Rule: Numbers must have valid Brazilian DDD (area code). Many SMS-PVA services provide pre-flagged numbers. Always prefer carrier-grade (SIM/eSIM) over VoIP for any Meta verification.
| Metric | Healthy | Warning | Critical | |---|---|---|---| | Delivery rate | >95% | 90-95% | <90% | | Read rate | >70% | 50-70% | <50% | | Block/report rate | <2% | 2-5% | >5% | | Quality rating | Green | Yellow | Red (tier downgrade) | | Open rate | >96% | 90-96% | <90% | | CTR (link in template) | >15% | 5-15% | <5% |
Industry benchmarks (2026):
If quality drops to yellow: stop broadcast, review template content, reduce volume, wait 6h evaluation cycle. If quality drops to red: pause all marketing, switch to utility-only for 7+ days, may need to ramp up tier again.
| Item | Cost | |---|---| | New disposable BM | $19-59 USD | | New profile (primary) | R$216 | | New profile (backup) | R$18 | | Proxy (ongoing) | ~R$30/mês | | Setup time lost | 2-4 days (no profile warmup needed for dispatch BMs) | | Total replacement | ~R$400-600 + time |
Key insight: Template performance data, contact segment insights, and timing optimizations survive the burn. Only the infrastructure dies.
"Disposable" means ISOLATED (a ban cannot cascade to the real business or to other disposable BMs), NOT "burn fast". With the same discipline as a primary number, a disposable-isolated number is a long-lived asset, burn-and-replace above is the FALLBACK when one dies, not the goal. Longevity reduces to one thing: keeping the rolling 7-day block + report rate low.
Recovery timelines (rule of thumb): Yellow to Green in ~48-72h, Red to Green in ~7-14 days, IF you stop marketing and resume only to engaged opt-ins. Quality held low for 7 straight days can cut the messaging limit.
Cold lists vs longevity, the WARM-FIRST rule. Marketing to cold (non-opted-in) numbers is incompatible with preserving quality: cold blocks at ~15-40% vs ~2-5% for opted-in signups, which directly degrades the per-number quality window. No "dilution" fixes this on a number you want to keep, mixing cold into warm only lowers the AGGREGATE rate while still adding absolute blocks to the rolling 7-day window, a slow tax on the asset. To use a cold list WITHOUT killing a preserved number:
Cold numbers also carry no first name, so a personalized template ({{1}} = name) renders broken on
them, use a no-variable template or a generic greeting for any cold / un-named send. (Researched 2025-06
across Meta docs + BSP deliverability guidance.)
tools
YCloud — a multi-channel communications provider (CPaaS: WhatsApp, SMS, Voice, Email), not a Meta-only BSP. This skill covers its WhatsApp Business operations: console navigation, plans/pricing, Embedded Signup, campaigns/inbox/journeys, auto-unsubscribe chatbot, the public-API-vs-dashboard-backend distinction, BSP migration, and read-only CDP automation. Use when operating YCloud for WhatsApp dispatch: plans, embedded signup, campaign sends, campaign analytics, inbox, auto-unsubscribe chatbot, opt-out attribution, dashboard automation, or comparing YCloud to other providers. Triggers on: 'ycloud', 'CPaaS', 'BSP', 'bulk campaign', 'whatsapp dashboard', 'embedded signup', 'auto-unsubscribe', 'opt-out chatbot', 'campaign analytics', 'dispatch automation', 'provider comparison', 'ycloud free plan', 'zero markup'.
development
YCloud v2 REST API reference for WhatsApp messaging via the BSP layer. Covers every callable endpoint: sending and listing messages (async and sendDirectly), template CRUD, phone number and WABA metadata, wallet balance, webhook management, contacts, unsubscribers/opt-outs, and media upload. Includes live-verified behavior deviations, pagination gotchas, and filter limitations. Use when calling the YCloud v2 REST API for WhatsApp — sending/listing messages, templates, phone numbers/WABA, wallet/balance, webhooks, contacts, unsubscribers, media, pagination gotchas. Triggers: 'ycloud api', 'X-API-Key', '/v2/whatsapp/messages', 'ycloud webhook', 'ycloud pagination', 'ycloud balance', 'sendDirectly', 'unsubscribers endpoint'.
development
WhatsApp Business Platform (Cloud API) production reference for the maccing growth stack. Covers Cloud API setup, message types, templates (creation, approval, pacing, strategy), per-message pricing, webhooks, bulk sending at scale, WhatsApp Flows, media handling, Node.js/TypeScript SDK, error codes, compliance (LGPD, opt-in/opt-out), Calling API, MM Lite, Business Management API, business profile fields, dispatch operations (chip warming, direct API setup, BSP migration, number longevity), and 2025-2026 platform changes. Use when the user asks about whatsapp, whatsapp api, cloud api, WABA, BSP, whatsapp template, whatsapp marketing, message template, WhatsApp dispatch, WhatsApp number quality, WhatsApp Brazil, WhatsApp opt-in, WhatsApp webhook, WhatsApp flows, WhatsApp pricing, or anything related to sending or receiving WhatsApp messages via the Cloud API.
testing
Parent skill for the Meta platform — Business Manager (BM) fundamentals and types, business verification, account quality, Meta enforcement classifier, Brazilian defensive intelligence (niche classification, black hat ecosystem awareness, payment hygiene), asset isolation, proxy/antidetect/CDP setup, disposable BM strategy, ban mechanics, ban/cascade/appeal, and Official Business Account. Use when working with ANY Meta platform (Ads, WhatsApp, Instagram) — BM health, verification, isolation, classifier, disposable-BM ops. Triggers on "business manager", "BM", "business verification", "account quality", "meta classifier", "asset isolation", "disposable BM", "proxy meta", "payment hygiene", "ban appeal", "cascade ban", "AdsPower", "CNPJ".