Skip to content

Updating visuals

If you see any images containing outdated UI, please bear with us.

We are updating all content as quickly as possible to mirror our new UI.

WhatsApp integration

WhatsApp lets your app message customers through the WhatsApp Business Cloud API: send texts, media, approved templates, interactive buttons and lists, locations and contact cards, manage message templates, and react to inbound messages and delivery statuses via webhook triggers.

Use cases

  • Send order updates, alerts and reminders with approved message templates
  • Build a support bot that replies to inbound customer messages
  • Send interactive reply buttons or list menus and react to the customer's pick
  • Deliver invoices, images, locations and contact cards from backend workflows
  • Track whether messages were sent, delivered, read — or failed

Setup

  1. Go to Meta for Developers, click Create app and pick the Connect with customers through WhatsApp use case (a Meta Business portfolio is required).
  2. In the App Dashboard open WhatsApp → API Setup and copy the Phone Number ID and the WhatsApp Business Account ID.
  3. Create a permanent token: in Business Settings open Users → System users and create an Admin system user. Click Assign assets and grant your app with Manage app plus your WhatsApp account, then Generate token with the whatsapp_business_messaging, whatsapp_business_management and business_management permissions.
  4. In WeWeb, open the Data & API tab, then the Integrations subtab. Select WhatsApp, then click Add Connection.
  5. Paste the Access Token, Phone Number ID and WhatsApp Business Account ID. The connection applies to all environments (Editor, Staging, Production); to use different values per environment, override the generated environment variables.
  6. Test with List Templates — if configured, it returns the account's message templates (a fresh WhatsApp Business test setup comes with the pre-approved hello_world template).

Enabling triggers (webhook)

The two triggers (see below) require the connection's webhook:

  1. On the connection, set the App Secret — from App settings → Basic in the App Dashboard. It is a 32-character hex string used only to verify webhook signatures; it is not the access token.
  2. Set WhatsApp Webhook to Enabled, keep (or adjust) the Webhook Path (default /whatsapp/webhook), and set a Verify Token (use the Generate button).
  3. Save the connection and publish your project — the webhook route only exists after publishing.
  4. In your Meta app open WhatsApp → Configuration and register the Callback URL: https://<your-app-domain>/api/<webhookPath> — use the connection's Copy <env> url button. The /api prefix is mandatory, and there must be no trailing slash. Enter the same Verify Token and click Verify and save.
  5. After verification succeeds, subscribe the webhook to the messages field (Webhook fields section). Without that subscription the handshake succeeds but no events are ever delivered.

ONE WEBHOOK FIELD, TWO TRIGGERS

Both triggers arrive through the single messages webhook field: inbound customer messages fire On message received, and delivery-status changes of your outbound messages fire On message status updated.

Triggers

With the webhook enabled, verified and subscribed, two backend triggers become available:

On message received

Runs each time a customer sends a message to your WhatsApp business number — texts, media, and taps on your interactive buttons or list rows all arrive here. Reply with Send Text bound to event.from, or mark it read with Mark as Read bound to event.messageId. event.text is only set for text messages; other types carry their details in event.raw.

Example event

json
{
  "messageId": "wamid.HBgLMTU1NTAxMjM0NTYVAgASGBQzQTBCM0Y3RjA4RjY4N0VCMkE5RQA=",
  "from": "15550123456",
  "senderName": "Jane",
  "timestamp": "1724668800",
  "type": "text",
  "text": "Hello",
  "phoneNumberId": "123456789012345",
  "raw": {
    "from": "15550123456",
    "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgASGBQzQTBCM0Y3RjA4RjY4N0VCMkE5RQA=",
    "timestamp": "1724668800",
    "text": { "body": "Hello" },
    "type": "text"
  }
}

On message status updated

Runs on each delivery-status change of a message you sent: sent, delivered, read or failed. Check event.status and event.errors to log or retry failures (event.errors is only present on a failed status) — this is also where asynchronous media failures surface (see pitfalls).

Example event

json
{
  "messageId": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA",
  "status": "delivered",
  "timestamp": "1724668805",
  "recipientId": "15550123456",
  "errors": [],
  "phoneNumberId": "123456789012345",
  "raw": {
    "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA",
    "status": "delivered",
    "timestamp": "1724668805",
    "recipient_id": "15550123456"
  }
}

Using WhatsApp as a data source for tables

You can create WeWeb tables backed by the account's message templates — a single resource, so no table configuration is required. Each row is one template (one language variant) with the columns id, name, category, language, status, body_text (the flattened text of the template's BODY component) and components (the raw component objects).

Views can filter by Status (APPROVED, PENDING, REJECTED, PAUSED, DISABLED), Category (UTILITY, MARKETING, AUTHENTICATION), Language (locale code, e.g. en_US) and Name or content (text search). Pagination is cursor-based: default 25 rows per page, maximum 100; there is no server-side sort.

Common pitfalls (setup & usage)

Free-form messages fail outside the 24-hour window

Free-form sends (Send Text, Send Media, Send Interactive, Send Location, Send Contacts, Send Reaction) only reach customers who messaged you within the last 24 hours. Outside that window Meta rejects with error 131047 — use Send Template with an APPROVED template to open or reopen the conversation.

New templates cannot be sent until approved

Create Template returns status: "PENDING" — the template goes through Meta review (minutes to 24h+) and cannot be sent until it is APPROVED. The Send Template dropdown only lists approved templates; check the review outcome with List Templates or the templates table.

Body variable count must match exactly

Send Template's Body variables must contain exactly one value per {{n}} placeholder in the template body, in order — Meta rejects a mismatch with error 132000. Header variable is only accepted when the template has a variable TEXT header.

Mark as Read needs an inbound message ID

Mark as Read only works on messages the customer sent you — bind event.messageId from the On message received trigger. Passing an outbound wamid (from a send result) fails with Meta error #100 ("message is outgoing"). In a trigger workflow, a failing action halts the chain: put your reply before Mark as Read, or wrap it in a try/catch block.

Delete Template removes every language variant

Delete Template with only a Name deletes all language variants of that template name. Pass the Template ID (from List Templates) to delete a single variant.

Media URLs are validated asynchronously

Send Media returns a message ID even when the URL doesn't point at a valid media file — Meta downloads and validates the URL after accepting the send. Don't trust the synchronous result for media: watch On message status updated for a failed status.

Test numbers only reach verified recipients

Meta's free test business number can only message up to 5 verified recipient numbers. Sending to any other number fails with error #131030 ("Recipient phone number not in allowed list") — add recipients in WhatsApp → API Setup, or connect a real business number.

Trailing slash breaks the webhook handshake

The callback URL must be exactly https://<your-app-domain>/api/<webhookPath> — a trailing slash makes Meta's verification GET miss the route (404), and omitting the /api prefix does the same. Use the Copy <env> url button on the connection instead of typing the URL.

App Secret is not the access token

Pasting the access token into the App Secret field breaks webhook deliveries silently: the handshake and all actions still work (the secret is only used to verify delivery signatures), but every incoming event is rejected with a 400 and no trigger ever fires. The App Secret is the 32-character hex string from App settings → Basic.

All Actions

This integration provides eleven actions mapped to the WhatsApp Cloud API and the template management API.

ActionDescription
Send TextSend a free-form text message
Send MediaSend an image, audio, video, document or sticker by public URL
Send TemplateSend a pre-approved message template — the only message type allowed outside the 24h window
Send InteractiveSend up to 3 reply buttons, or a list menu
Send ReactionReact to a message with an emoji, or remove a reaction
Send LocationSend a location pin
Send ContactsSend one or more contact cards
Mark as ReadMark an inbound message as read, optionally with a typing indicator
Create TemplateCreate a message template (goes through Meta review)
List TemplatesList the account's message templates with filters and cursor pagination
Delete TemplateDelete a message template by name, or a single language variant by ID

Action details

Send Text

Send a free-form text message to a WhatsApp number. Only works within 24h of the recipient's last inbound message (error 131047 otherwise — use Send Template).

Inputs

Display KeyExample InputDescriptionRestrictions
To"15550123456"Recipient phone number in international format, digits only. Typically bound from a trigger event (event.from).Required; recipient must have written to you in the last 24 hours
Message"Your order has shipped!"Text of the message. URLs are allowed.Required; max 4096 characters
Preview URL
Optional
trueRender a link preview for the first URL found in the message body.Boolean, default false
Reply to message ID
Optional
"wamid.HBgL..."Send this message as a contextual reply to a previous message.A wamid from a send result (messages[0].id) or a trigger event (event.messageId)

Example output

json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15550123456", "wa_id": "15550123456" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA" }]
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Messages (POST /{phone-number-id}/messages)

Send Media

Send a media message (image, audio, video, document or sticker) from a public URL. Same 24h customer-service window as free-form texts. The URL is validated asynchronously — see pitfalls.

Inputs

Display KeyExample InputDescriptionRestrictions
To"15550123456"Recipient phone number in international format, digits only.Required; recipient must have written to you in the last 24 hours
Media type"image"Type of media to send.Required; one of image, audio, video, document, sticker
Media URL"https://example.com/image.jpg"Public HTTPS URL of the media file — WhatsApp downloads it from there. Mind Meta's per-type format and size limits (e.g. images 5MB jpeg/png, stickers webp).Required
Caption
Optional
"Here is your invoice"Text shown under the media.Only for image, video, document media types
File name
Optional
"invoice.pdf"File name shown to the recipient.Only for document media type
Reply to message ID
Optional
"wamid.HBgL..."Send this message as a contextual reply to a previous message.A wamid

Example output

json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15550123456", "wa_id": "15550123456" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA" }]
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Messages (POST /{phone-number-id}/messages)

Send Template

Send a pre-approved message template — the only message type allowed outside the 24h customer-service window.

Inputs

Display KeyExample InputDescriptionRestrictions
To"15550123456"Recipient phone number in international format, digits only.Required
Template"order_confirmation"Approved message template to send — pick from the dropdown (refreshable). Manage templates with the template actions or the table view.Required; the dropdown lists only APPROVED templates
Language code"en_US"Locale of the template — set automatically when picking the template.Required; must match the template's language exactly
Body variables
Optional
["John", "ORD-1234"]Values for the {{1}}, {{2}}… placeholders of the template body, in order.Shown once a template is selected; count must exactly match the template's placeholders (Meta error 132000)
Header variable
Optional
"ORD-1234"Value for the template header's {{1}} placeholder.Shown once a template is selected; only for templates with a variable TEXT header — leave empty otherwise

Example output

json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15550123456", "wa_id": "15550123456" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA" }]
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Template messages (POST /{phone-number-id}/messages)

Send Interactive

Send an interactive message — up to 3 reply buttons, or a list menu. The customer's tap comes back through the On message received trigger. Same 24h window as free-form texts.

Inputs

Display KeyExample InputDescriptionRestrictions
To"15550123456"Recipient phone number in international format, digits only.Required; recipient must have written to you in the last 24 hours
Interactive type"Reply buttons"Reply buttons shows up to 3 tappable buttons; List opens a menu of up to 10 options.Required; default Reply buttons
Header text
Optional
"Choose an option"Text shown at the top of the message.Max 60 characters
Body text"What would you like to do?"Main text of the interactive message.Required; max 1024 characters
Footer text
Optional
"Reply anytime"Smaller text shown under the body.Max 60 characters
Buttons[{ "id": "confirm", "title": "Confirm" }]Reply buttons — each with an ID (returned when tapped, max 256 characters) and a Title (visible label, max 20 characters).Required for the Reply buttons type; 1–3 buttons
List button text"View options"Label of the button that opens the list.Required for the List type; max 20 characters
Sections[{ "title": "Drinks", "rows": [{ "id": "coffee", "title": "Coffee", "description": "Freshly brewed" }] }]List sections, each with rows the user can pick. Row ID (max 200 characters) and Title (max 24 characters) are required per row; Description (max 72 characters) and the section Title (max 24 characters, required by Meta with several sections) are optional.Required for the List type; max 10 rows across all sections

Example output

json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15550123456", "wa_id": "15550123456" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA" }]
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Interactive messages (POST /{phone-number-id}/messages)

Send Reaction

React to a message with an emoji, or remove a previous reaction by sending an empty emoji.

Inputs

Display KeyExample InputDescriptionRestrictions
To"15550123456"Recipient phone number in international format, digits only.Required; recipient must have written to you in the last 24 hours
Message ID"wamid.HBgL..."The wamid of the message to react to — from a send action result or an inbound message.Required; message must be less than 30 days old
Emoji
Optional
"👍"A single emoji.Leave empty to remove a previous reaction

Example output

json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15550123456", "wa_id": "15550123456" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA" }]
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Reaction messages (POST /{phone-number-id}/messages)

Send Location

Send a location pin (latitude/longitude with optional name and address). Same 24h customer-service window as free-form texts.

Inputs

Display KeyExample InputDescriptionRestrictions
To"15550123456"Recipient phone number in international format, digits only.Required; recipient must have written to you in the last 24 hours
Latitude37.4847Latitude in decimal degrees.Required; number between -90 and 90
Longitude-122.1477Longitude in decimal degrees.Required; number between -180 and 180
Name
Optional
"Head office"Name of the location shown on the pin.String
Address
Optional
"1 Hacker Way, Menlo Park, CA"Address shown under the location name.String

Example output

json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15550123456", "wa_id": "15550123456" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA" }]
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Location messages (POST /{phone-number-id}/messages)

Send Contacts

Send one or more contact cards. Same 24h customer-service window as free-form texts.

Inputs

Display KeyExample InputDescriptionRestrictions
To"15550123456"Recipient phone number in international format, digits only.Required; recipient must have written to you in the last 24 hours
Contacts[{ "formattedName": "Jane Doe", "phone": "+15550123456" }]Contact cards to send. Each card has a Formatted name (required), plus optional First name, Last name, Phone, Email and Company.Required; at least one contact, each with a Formatted name

Example output

json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15550123456", "wa_id": "15550123456" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTAxMjM0NTYVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA" }]
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Contacts messages (POST /{phone-number-id}/messages)

Mark as Read

Mark an inbound message as read (blue ticks), optionally showing a typing indicator.

Inputs

Display KeyExample InputDescriptionRestrictions
Message ID"wamid.HBgL..."The wamid of the inbound message to mark as read — bind event.messageId from the On message received trigger.Required; outbound message IDs are rejected by Meta (error #100)
Show typing indicator
Optional
trueAlso display a typing… indicator to the customer (clears after ~25 seconds or when you reply).Boolean, default false

Example output

json
{
  "success": true
}

Documentation of API endpoint that powers action: Meta Cloud API docs – Mark messages as read (POST /{phone-number-id}/messages)

Create Template

Create a message template on the WhatsApp Business account. The template starts in PENDING status and goes through Meta review before it can be sent.

Inputs

Display KeyExample InputDescriptionRestrictions
Name"order_confirmation"Template name.Required; lowercase letters, digits and underscores only, max 512 characters
Category"UTILITY"Meta template category — drives per-conversation pricing and review strictness: UTILITY for transactional, MARKETING for promotional, AUTHENTICATION for OTP codes.Required; one of UTILITY, MARKETING, AUTHENTICATION
Language"en_US"Locale code of the template.Required; a Meta locale code (e.g. en_US, fr, de)
Header text
Optional
"Order update"TEXT header shown at the top of the template.Max 60 characters
Body text"Hi 1, your order 2 has shipped!"Body of the template. Use {{1}}, {{2}}… for variables.Required; max 1024 characters
Footer text
Optional
"Reply STOP to unsubscribe"Footer shown under the body.Max 60 characters
Body example values
Optional
["John", "ORD-1234"]Example values for the {{n}} body placeholders, in order — used by Meta review.Required by Meta when the body contains placeholders

Example output

json
{
  "id": "1234567890123456",
  "status": "PENDING",
  "category": "UTILITY"
}

Documentation of API endpoint that powers action: Meta Business Management API docs – Message Templates (POST /{waba-id}/message_templates)

List Templates

List the message templates of the WhatsApp Business account, with optional filters and cursor pagination.

Inputs

Display KeyExample InputDescriptionRestrictions
Status
Optional
"APPROVED"Only return templates with this review status.One of APPROVED, PENDING, REJECTED, PAUSED, DISABLED
Category
Optional
"UTILITY"Only return templates of this category.One of UTILITY, MARKETING, AUTHENTICATION
Name or content
Optional
"order"Only return templates whose name or content contains this text.String
Limit
Optional
25Maximum number of templates per page.1–100, clamped server-side; when omitted, Meta's Graph default (25) applies
After cursor
Optional
"MjQZD"Pagination cursor — pass paging.cursors.after from the previous page.Note: Graph returns cursors.after even on the last page — paging.next being present is the real "has more" signal

Example output

json
{
  "data": [
    {
      "id": "1234567890123456",
      "name": "order_confirmation",
      "category": "UTILITY",
      "language": "en_US",
      "status": "APPROVED",
      "components": [
        { "type": "HEADER", "format": "TEXT", "text": "Order update" },
        {
          "type": "BODY",
          "text": "Hi {{1}}, your order {{2}} has shipped!",
          "example": { "body_text": [["John", "ORD-1234"]] }
        }
      ]
    }
  ],
  "paging": { "cursors": { "before": "MAZDZD", "after": "MjQZD" } }
}

Documentation of API endpoint that powers action: Meta Business Management API docs – Message Templates (GET /{waba-id}/message_templates)

Delete Template

Delete a message template by name. Without a Template ID, all language variants of that name are deleted.

Inputs

Display KeyExample InputDescriptionRestrictions
Name"order_confirmation"Name of the template to delete.Required; alone it deletes every language variant of this name
Template ID
Optional
"1234567890123456"Template id (from List Templates) — scopes the deletion to that single language variant.String

Example output

json
{
  "success": true
}

Documentation of API endpoint that powers action: Meta Business Management API docs – Message Templates (DELETE /{waba-id}/message_templates)

Error handling

WeWeb validates the connection and a few arguments before calling Meta; everything else is Meta's own error, passed through verbatim.

WeWeb-generated errors — thrown before any call to Meta reaches the network:

Error code and typeReason
400 — connection missing / no access tokenThe WhatsApp connection is missing or not configured — check the connection selected on the action.
400 — no Phone Number ID configuredThe connection has no Phone Number ID (App Dashboard → WhatsApp → API Setup) — check the connection selected on the action.
400 — no Business Account ID configuredTemplate actions and the templates table need the WhatsApp Business Account ID (WABA ID) — check the connection selected on the action.
400 — invalid media typeSend Media with a Media type outside image, audio, video, document, sticker (only possible via binding).
400 — numeric offset on the templates tableTemplates use cursor pagination — a numeric offset > 0 cannot be translated to a cursor; pass the nextOffset from the previous page (or 0 for the first page).

Meta pass-through errors — returned as { "error": { "message", "type", "code", "error_subcode", "fbtrace_id" } }:

Error code and typeReason
401 OAuthExceptionInvalid or expired access token.
400 code 100Invalid parameter — e.g. Mark as Read with an outbound message ID ("message is outgoing, use an incoming message ID"), or a malformed field value.
400 code 131030Recipient phone number not in allowed list — test numbers can only message up to 5 verified recipients.
400 code 131047Re-engagement message — a free-form send outside the 24-hour customer-service window; use Send Template.
400 code 132000Template parameter count mismatch — Body variables doesn't match the template's {{n}} placeholders.
400 code 132001Template not found or not approved — wrong name, wrong Language code, or the template is still PENDING/REJECTED.

FAQs

How do I get a permanent access token?

The token shown in WhatsApp → API Setup is temporary. For a permanent one, create an Admin system user in Business Settings (Users → System users), assign your app (Manage app) and your WhatsApp account to it, then generate a token with the whatsapp_business_messaging, whatsapp_business_management and business_management permissions.

Why do my messages to a new user fail?

Free-form messages only reach customers who wrote to you within the last 24 hours — outside that window Meta rejects with error 131047. Start (or reopen) the conversation with Send Template using an APPROVED template; once the customer replies, the 24-hour window opens and free-form sends work.

How can I test without a real business number?

Meta gives every WhatsApp app a free test business number (WhatsApp → API Setup) with the pre-approved hello_world template. It can only message up to 5 verified recipient numbers — add yours in the API Setup page; other recipients fail with error #131030.

Why don't my triggers fire?

Check, in order: the webhook is Enabled on the connection with App Secret and Verify Token set; the project has been published (the route only exists after publishing); the callback URL registered in Meta is exactly https://<your-app-domain>/api/<webhookPath> (the /api prefix is mandatory, no trailing slash); the webhook is subscribed to the messages field; and the App Secret is the 32-character hex string from App settings → Basic — pasting the access token there makes Meta's deliveries fail silently with 400.

Can I upload a media file directly from a workflow?

No — Send Media sends by public HTTPS URL only: WhatsApp downloads the file from there. Host the file somewhere publicly accessible (e.g. WeWeb storage) and pass its URL. Also note the URL is validated asynchronously — a bad URL still returns a message ID, and the failure only shows up on the On message status updated trigger.

How do I know a message was actually delivered?

A successful send only means Meta accepted the message. Enable the webhook and use the On message status updated trigger: it fires with event.status = sent, delivered, read or failed (with details in event.errors).