Interactive messages with the WhatsApp Cloud API

Meta's official WhatsApp API lets you send native buttons, lists, and carousels — no middleware BSP required. Here's what you need to integrate and the ID pattern that keeps conversation state off the server.

The 4 message types

All types use POST /messages with "messaging_product": "whatsapp". The 24h window starts from the user's last message.

Plain textSupports *bold*, _italic_, `code`Any time
ButtonsMax 3 buttons, title up to 20 chars24h
List4-10 options in sections with modal24h
Carousel2-10 cards with image + button24h, ≥ 2 cards
POST https://graph.facebook.com/v23.0/{PHONE_NUMBER_ID}/messages
Authorization: Bearer {ACCESS_TOKEN}
Content-Type: application/json

Interactive buttons

The reply.id field is returned exactly as sent in the webhook — use it to identify the action without any server-side state.

{
  "messaging_product": "whatsapp",
  "to": "5511999999999",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "body": { "text": "Como prefere receber o pedido?" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "delivery", "title": "🛵 Entrega" } },
        { "type": "reply", "reply": { "id": "pickup",   "title": "🏠 Retirada" } }
      ]
    }
  }
}

IDs as a state machine

The cleanest way to manage conversation flow is to encode the action directly in the button id with prefixes. In the webhook, parse the prefix and execute the action — the button carries the intent, no server state needed.

restaurant:<uuid>    → selecionar restaurante
category:<uuid>      → filtrar cardápio
item:<uuid>          → adicionar ao carrinho
cart:view            → mostrar carrinho
cart:checkout        → iniciar checkout
cart:remove:<n>      → remover item n
confirm:yes          → confirmar ação
order:<uuid>         → status do pedido

Essential checklist

  • Credentials in environment variables, never in code
  • Respond to the webhook with HTTP 200 immediately — Meta retries if it doesn't receive a response in 20s
  • Deduplicate by message.id — Meta may deliver the same message more than once
  • Wrap interactive sends in try/catch with plain text fallback
  • Carousel: minimum 2 cards, all with the same number of buttons, JPEG 1:1 images

Full reference

The file covers all payloads with exact character limits, webhook structure for each type (including the carousel quick_reply difference), fallback strategy, and carousel image rules.

Download reference (.md)