WhatsApp Business

Send and receive WhatsApp messages through the official WhatsApp Business Platform (Cloud API) — templates, the 24-hour customer service window, media, and interactive messages

Blooio connects to the official WhatsApp Business Platform (Cloud API) so you can message your customers on WhatsApp from the same multi-channel API you already use for iMessage, SMS, and RCS. Onboarding uses Meta's Embedded Signup, so your customers connect their own WhatsApp Business Account (WABA) and phone number in a few clicks — nothing to hand-enter.

Note

WhatsApp Business is a v4 channel (channel_type: "whatsapp_business"), served by api.blooio.com/v4. It is a separate channel from peer-to-peer WhatsApp (coming later) and shares no data with it.

Overview

The WhatsApp Business integration lets you:

  • Send template messages to start conversations (the only messages allowed outside the 24-hour window).
  • Send free-form text, media (image / video / audio / document), and interactive (reply buttons, list, CTA URL) messages inside the customer service window.
  • Receive inbound messages and delivery/read receipts as message.* webhook events.
  • Track the full sent → delivered → read lifecycle, mapped from Meta's status webhook.

Note

Blooio relays directly to Meta and does not queue, pace, or retry WhatsApp messages. Meta's messaging limits, throughput, and errors are surfaced straight back to you (see Limitations & rate limits).

When to use WhatsApp Business

Use case Why WhatsApp Business
International audiences WhatsApp is the dominant messaging app across most of the world
Notifications & reminders Approved utility templates deliver order, appointment, and shipping updates
Marketing opt-ins Approved marketing templates reach opted-in users with rich content
Two-way support Free-form replies for 24 hours after each inbound customer message

WhatsApp complements Blooio's iMessage (rich 1:1 in the US) and Twilio SMS (broadcast scale). Choose the channel per audience and message type.

Onboarding (Embedded Signup)

Connect WhatsApp from the Blooio dashboard Integrations → WhatsApp Business → Connect. This launches Meta's Embedded Signup, where your customer:

  1. Logs in with Facebook and selects (or creates) their WhatsApp Business Account.
  2. Selects (or adds) the business phone number to use.
  3. Grants Blooio the whatsapp_business_management and whatsapp_business_messaging permissions.

Blooio then exchanges the returned code for a business access token, subscribes the app to the WABA's webhooks, registers the phone number, and provisions a whatsapp_business channel. The WABA id and phone number are pulled dynamically from the customer's Meta account — there is nothing to type in.

Warning

In the WhatsApp Business Platform's Tech Provider model, the onboarded business adds their own payment method to their WABA (Meta bills the business directly). Blooio only relays messages on their behalf.

Once connected, the channel's display_address is the connected phone number, and you send to it just like any other Blooio number.

Sending

Send with the v4 POST /v4/messages endpoint. Set to to the recipient in E.164 format and from to your connected WhatsApp number (or pass channel_type: "whatsapp_business" to let Blooio resolve the channel).

Text

Free-form text is allowed inside the 24-hour customer service window (see below).

cURL

curl -X POST 'https://api.blooio.com/v4/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "+15551234567",
    "from": "+15559876543",
    "text": "Thanks for reaching out! How can we help?"
  }'
Try it

Node.js

await fetch('https://api.blooio.com/v4/messages', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.BLOOIO_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '+15551234567',
    from: '+15559876543',
    text: 'Thanks for reaching out! How can we help?'
  })
})

Python


requests.post('https://api.blooio.com/v4/messages',
  headers={
    'Authorization': f"Bearer {os.environ['BLOOIO_API_KEY']}",
    'Content-Type': 'application/json'
  },
  json={
    'to': '+15551234567',
    'from': '+15559876543',
    'text': 'Thanks for reaching out! How can we help?'
  }
)

Media

Attach an image, video, audio file, or document by public HTTPS URL. An optional caption travels in text (images, videos, and documents support captions).

cURL

curl -X POST 'https://api.blooio.com/v4/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "+15551234567",
    "from": "+15559876543",
    "text": "Your receipt is attached.",
    "attachments": [
      { "url": "https://example.com/receipt.pdf", "fileName": "receipt.pdf" }
    ]
  }'
Try it

Node.js

await fetch('https://api.blooio.com/v4/messages', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.BLOOIO_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '+15551234567',
    from: '+15559876543',
    text: 'Your receipt is attached.',
    attachments: [
      { url: 'https://example.com/receipt.pdf', fileName: 'receipt.pdf' }
    ]
  })
})

Python


requests.post('https://api.blooio.com/v4/messages',
  headers={
    'Authorization': f"Bearer {os.environ['BLOOIO_API_KEY']}",
    'Content-Type': 'application/json'
  },
  json={
    'to': '+15551234567',
    'from': '+15559876543',
    'text': 'Your receipt is attached.',
    'attachments': [
      { 'url': 'https://example.com/receipt.pdf', 'fileName': 'receipt.pdf' }
    ]
  }
)

Blooio infers the WhatsApp media type from the file extension. To force it, add "media_type": "image" | "video" | "audio" | "document" alongside attachments.

Template

Templates are the only messages allowed outside the 24-hour window. Reference an approved template by name and language, and pass positional body variables in variables.

cURL

curl -X POST 'https://api.blooio.com/v4/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "+15551234567",
    "from": "+15559876543",
    "template": {
      "name": "order_confirmation",
      "language": "en_US",
      "variables": ["Ada", "#A1234"]
    }
  }'
Try it

Node.js

await fetch('https://api.blooio.com/v4/messages', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.BLOOIO_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '+15551234567',
    from: '+15559876543',
    template: {
      name: 'order_confirmation',
      language: 'en_US',
      variables: ['Ada', '#A1234']
    }
  })
})

Python


requests.post('https://api.blooio.com/v4/messages',
  headers={
    'Authorization': f"Bearer {os.environ['BLOOIO_API_KEY']}",
    'Content-Type': 'application/json'
  },
  json={
    'to': '+15551234567',
    'from': '+15559876543',
    'template': {
      'name': 'order_confirmation',
      'language': 'en_US',
      'variables': ['Ada', '#A1234']
    }
  }
)

Note

Create and sync templates from the dashboard (or POST /organizations/:id/integrations/whatsapp/:channelId/templates). Templates must be approved by Meta before they can be sent. For advanced templates (header media, buttons), pass a raw Cloud API components array instead of variables.

Interactive

Inside the window, send reply buttons (up to 3), a list (up to 10 rows total), or a call-to-action URL button.

Reply buttons

curl -X POST 'https://api.blooio.com/v4/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "+15551234567",
    "from": "+15559876543",
    "interactive": {
      "kind": "button",
      "body": "Did this resolve your issue?",
      "buttons": [
        { "id": "yes", "title": "Yes, thanks" },
        { "id": "no", "title": "Still stuck" }
      ]
    }
  }'
Try it

List

curl -X POST 'https://api.blooio.com/v4/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "+15551234567",
    "from": "+15559876543",
    "interactive": {
      "kind": "list",
      "body": "Pick a time that works for you",
      "button": "View times",
      "sections": [
        {
          "title": "Morning",
          "rows": [
            { "id": "9am", "title": "9:00 AM" },
            { "id": "10am", "title": "10:00 AM" }
          ]
        }
      ]
    }
  }'
Try it

CTA URL

curl -X POST 'https://api.blooio.com/v4/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "+15551234567",
    "from": "+15559876543",
    "interactive": {
      "kind": "cta_url",
      "body": "Your order has shipped.",
      "display_text": "Track package",
      "url": "https://example.com/track/A1234"
    }
  }'
Try it

Customer service window & templates

WhatsApp only allows free-form messages within a rolling 24-hour customer service window that opens (and resets) each time the customer messages you. Outside that window you may send only approved template messages.

Blooio enforces this locally, before relaying to Meta, so you fail fast with a clear error instead of burning your messaging quota on a rejected send:

Situation Result
Inside the window Any content type is allowed
Outside the window, non-template content 422 template_required_outside_customer_service_window
Template not approved on the WABA 422 template_not_approved

Note

A template send is allowed at any time (in or out of the window), as long as the template is approved. Sending a template re-engages a customer whose window has closed.

Receiving

Inbound WhatsApp messages are delivered to your registered webhooks as message.received events, with channel_type: "whatsapp_business" and protocol: "whatsapp":

{
  "id": "evt_019fd421-b020-7a63-8f8b-152ea9c99333",
  "type": "message.received",
  "created_at": 1706640000000,
  "organization_id": "org_abc123",
  "data": {
    "message_id": "msg_019fd421-af64-7040-aeae-3e8df24a8e89",
    "chat_id": "chat_019fd421-af64-7040-aeae-3e8df24a8e89",
    "channel_id": "ch_019f959b-ff70-7b1e-9b42-e457bf848fd7",
    "channel_type": "whatsapp_business",
    "kind": "received",
    "direction": "inbound",
    "status": "received",
    "protocol": "whatsapp",
    "message_type": "text",
    "text": "Hi, is my order on its way?",
    "provider_message_id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgAR...",
    "sender": "+15551234567",
    "contact": { "identifier": "+15551234567" },
    "attachments": []
  }
}

WhatsApp-specific fields:

  • provider_message_id is the WhatsApp message id (wamid).
  • Interactive replies carry the tapped option id in the message metadata (interactive_reply_id).
  • Replies to a specific message carry the quoted message's wamid (context_wamid).
  • Media messages re-host the file to a servable Blooio URL and appear under attachments.

Each inbound message opens or resets the 24-hour customer service window for that chat.

Message status & webhook events

Outbound WhatsApp messages progress through the standard v4 lifecycle, mapped from Meta's status webhook and delivered as message.* events:

Status Event Meaning
sent message.sent Meta accepted the message (a wamid was issued)
delivered message.delivered Delivered to the recipient's device
read message.read The recipient opened the message
failed message.failed Delivery failed — inspect error (see below)

Status is forward-only: a late delivered never walks back a read. Reactions arrive as message.reaction events.

Limitations & rate limits

Blooio relays directly to Meta and does not queue or retry. Meta's limits apply as-is:

  • Messaging limits (tiers) — cap the number of unique users you can open business-initiated (template) conversations with in a rolling 24 hours: TIER_250TIER_2KTIER_10KTIER_100KTIER_UNLIMITED. Set at the business-portfolio level and shared across all numbers in it. In-window replies and user-initiated chats don't count.
  • Throughput — up to 80 messages/second per number by default, auto-upgrading toward 1,000 mps for high-volume, high-quality numbers. Exceeding it returns error 130429 (retriable).
  • Pair rate limit — roughly 1 message every 6 seconds to the same user (error 131056, retriable). Batch consecutive lines to one user into a single message.
  • Content caps (enforced locally before send): text body ≤ 4,096; media caption ≤ 1,024; reply buttons ≤ 3 (title ≤ 20); list ≤ 10 sections / 10 rows total; hydrated template body ≤ 1,024.
  • Media sizes: image 5 MB, video/audio 16 MB, document 100 MB, sticker 100 KB (static) / 500 KB (animated).

Warning

When Meta rejects a send synchronously, Blooio marks the message failed and surfaces the reason. Retriable errors carry retriable: trueyour application decides whether to resend; Blooio never buffers or auto-retries.

Best practices

  1. Lead with templates, continue free-form. Open with an approved template, then converse freely within the 24-hour window the customer's reply opens.
  2. Keep templates approved and synced. Sync templates from the dashboard so template_not_approved never surprises you at send time.
  3. Monitor status events. Handle message.delivered / message.read / message.failed to track deliverability, and watch quality signals in Meta Business Manager.
  4. Respect the window. Check for template_required_outside_customer_service_window and fall back to a template (or another channel) when the window has closed.
  5. Handle failures yourself. WhatsApp sends fail immediately with no Blooio retry — implement your own retry for retriable: true errors.

Troubleshooting

template_required_outside_customer_service_window (422)

The 24-hour window has closed for this chat. Send an approved template to re-engage, or wait for the customer to message you again.

template_not_approved (422)

The template name / language you referenced isn't approved on this WABA. Create or sync the template and wait for Meta's approval (up to 24 hours).

130429 — throughput limit (retriable)

You're sending faster than the number's messages-per-second limit. Slow down and resend; capacity auto-upgrades for high-quality, high-volume numbers.

131047 — re-engagement required

Meta's version of the closed-window error, if a template send slips through. Send an approved template to re-open the conversation.

131048 / 131049 — quality / per-user marketing cap

131048 is a spam/quality restriction; 131049 means the user hit their cross-business marketing cap — wait at least 24 hours before retrying that user.

132005 — hydrated template body too long

Your template variables produced a body over 1,024 characters. Shorten the variable values or the template body.

100 / 131009 — invalid or malformed content

A payload/parameter was rejected. Check the recipient format (E.164), media URL/type, and template variable count.

Next steps