Channels

WhatsApp Business

Customers message your WhatsApp Business number and the conversation lands in a Kommio inbox, answered like any other. Built on Meta's WhatsApp Business Cloud API with a Meta app of your own: the ids and tokens to copy, files and reply buttons, the 24-hour window, and what the health check watches.

Last updated

WhatsApp runs on Meta's WhatsApp Business Cloud API with the same kind of setup as Messenger and Instagram: a Meta app of your own, a permanent access token and the app's app secret. Agents, bots, chatbot flows and the AI agent answer WhatsApp conversations like any other. The On-Premises API (your own WhatsApp server) and WhatsApp through a Business Solution Provider other than Meta are not supported.

Set it up

  1. Prepare the number and the app

    You need a WhatsApp Business Account in Meta Business Suite with a phone number registered on the Cloud API (WhatsApp Manager › Phone numbers; a number used by the WhatsApp app must be migrated first, and then stops working in the app), and a Meta app of type Business in Meta for Developers with the WhatsApp product added and the account connected to it (WhatsApp › API Setup). While the app is in development mode Meta gives you a test number and lets you message up to five listed numbers from it, which is enough to try Kommio.

  2. Copy the ids, the token and the secret

    In Meta Business Suite › Settings › Users › System users, add a system user with access to the account and the app and generate a token for the app with the permissions whatsapp_business_messaging and whatsapp_business_management and expiry Never. (The temporary token on the API Setup page works for a day; do not use it for an inbox.) Copy the numeric phone number id (not the number itself) and the WhatsApp Business Account id from WhatsApp Manager or the API Setup page, and the app secret from App settings › Basic › App secret › Show.

  3. Connect

    Settings › Marketplace › WhatsApp Business › Install in this workspace, or Settings › Inboxes › New inbox › WhatsApp. Enter the phone number id, the account id, the access token and the app secret and press Test connection: Kommio asks Meta who the app is (which also proves the secret belongs to the token's app), checks that the token is valid with both permissions, and shows the number (display number, verified name, quality rating) and the account it belongs to.

  4. Point the webhook at Kommio

    Leave Point the app's webhook at Kommio on (the default): when the inbox is created Kommio sets the app's WhatsApp webhook to the inbox's own address and subscribes the account to the app. Meta verifies the address at once, so Kommio must be reachable from the internet. The address that was there before is remembered and shown on the inbox page. Turn it off to set the webhook yourself: the inbox page then shows the Callback URL (https://<your kommio address>/api/public/channel-hooks/<inbox key>), the inbox's Verify token and the field to subscribe (messages) for App Dashboard › WhatsApp › Configuration.

  5. Try it

    Name the inbox and choose its agents. The last step, and the inbox's page later, shows the number's wa.me link with a button: message it from WhatsApp, it arrives in the inbox, and your reply comes back.

Token and secret are stored encrypted and never returned by the API. After a new token or a reset app secret, paste both on the inbox's page (Replace the credentials); saving the inbox sets the webhook and the subscription again. Kommio proves the app secret on every call to Meta, so you may turn on Require App Secret in the app's advanced settings. One account can hold several numbers, but the app's WhatsApp webhook is one address: events for a number the inbox is not connected to are not stored and the inbox shows a notice. Use one Meta app per number, or set the webhook by hand on the other inboxes.

What is supported

KindCustomer to KommioKommio to customer
TextYes, with the customer's WhatsApp profile namePlain text up to 4,096 characters; WhatsApp's own *bold* and _italic_ marks pass through as typed
Long textBot and flow messages split at a paragraph, line or word
Pictures, video, audio, voice notes, documents, stickersYes, up to 100 MB, captions as the text, documents with their file nameJPEG and PNG up to 5 MB as pictures, MP4 and 3GPP up to 16 MB as video, common audio formats up to 16 MB as audio, anything else as a document up to 100 MB; larger files are refused with the reason
Locations, contact cardsAs text, a location with a map link
Replies, forwardsThe quoted message is shown above the reply; a forwarded message says so
Chatbot flow choicesA tap answers the flowUp to 3 choices as reply buttons (titles cut at 20 characters), up to 10 as a list (titles cut at 24); more stay in the text
Click-to-WhatsApp ads and linksThe ad's address or headline in the conversation attribute whatsapp_referral
Reactions, system notices, catalog ordersAn activity line, or a line saying what arrived
Read and typingOpening a conversation marks the customer's newest message read (blue ticks); an agent typing shows as typing
DeliverySent, delivered, read, or failed with Meta's reason

Customers are contacts with a phone identity (+14155550100), the same kind SMS uses, so a person who texts and messages on WhatsApp is one contact. Every message is keyed by its WhatsApp message id, so Meta's retries and replays store nothing twice.

The 24-hour window

Meta lets a business answer a customer within 24 hours of the customer's last message, and the composer shows when the window closes. After that, and for a customer who has not written yet, only an approved template message reaches them. A reply outside the window is refused before it is queued, with the reason, and a reply still queued when the window closes fails for good; satisfaction surveys are sent only while the window is open. Bots and flows cannot start conversations: the customer writes first.

Pausing, deleting, health

A paused inbox refuses Meta's deliveries; Meta retries for a while and then drops them. Pausing leaves Meta as it is, and resuming sets the webhook and the subscription again. After deleting an inbox, remove the webhook or the account from the app in the App Dashboard if nothing else uses it.

Every five minutes Kommio asks Meta about the token (still valid, with a warning a week before it expires), the number's quality rating (a warning when Meta rates it red), the account's subscription to the app (subscribed again when it is gone) and, when Kommio set it, the app's webhook (pointed elsewhere, or turned off by Meta after too many failed deliveries). A token Meta refuses marks the inbox and its installation under Settings › Marketplace › Installed for a reconnect. If nothing arrives, check App Dashboard › WhatsApp › Configuration (the callback is the inbox's address and messages is subscribed) and, in development mode, that the customer's number is listed on the API Setup page; 401s in Meta's webhook log mean the app secret on the inbox is not the app's. 131030 means the number is not on the development allow list, 131026 that the number is not on WhatsApp.

API

# check the credentials: the app, the number and the account
curl -X POST "$KOMMIO_URL/channels/whatsapp/test" -H "Authorization: Bearer $KOMMIO_KEY" -H "Content-Type: application/json" \
  -d '{"config": {"phone_number_id": "106540352817429", "waba_id": "102938475610234", "access_token": "EAAGm0PX4ZCpsBAJZCZB1pgMlxuvYkn8Q1b5f", "app_secret": "<app secret>"}}'

# create the inbox
curl -X POST "$KOMMIO_URL/inboxes" -H "Authorization: Bearer $KOMMIO_KEY" -H "Content-Type: application/json" \
  -d '{"name": "WhatsApp", "channel_kind": "whatsapp", "config": {"phone_number_id": "106540352817429", "waba_id": "102938475610234", "access_token": "EAAGm0PX4ZCpsBAJZCZB1pgMlxuvYkn8Q1b5f", "app_secret": "<app secret>"}}'

The inbox's config then carries display_phone_number, verified_name, quality_rating, waba_name, app_id, app_name, the webhook address Kommio set and the inbox's verify_token; configure_webhook: false leaves the app's webhook alone. Replying outside the window returns 422 with the reason.

Every claim on these pages is covered by a test in the repository, and the API reference is generated from the same contract every server serves at /api/openapi. Corrections to [email protected].