wapit

Mailpit, but for WhatsApp.

Point your app at wapit and every message it sends lands in an inbox that looks like a chat. Each send is checked the way Meta checks it, and you reply as the user.

$ brew install patriciomartinns/tap/wapit
Read the docs View on GitHub

On Windows, download the .zip from the latest release. Also as a container image and with cargo install.

The wapit inbox: a customer asks about an order and the app answers with a template with buttons, with the webhook delivery shown under the message

Tap a button. Your app gets the webhook.

Reply, attach a file, tap a button or mark a message read in the inbox. wapit posts the same payload Meta would, signed with your app secret.

POST localhost:8000/webhook200 in 38 msRequest your app receives
X-Hub-Signature-256: sha256=4f0c9e…b21a

{
  "object": "whatsapp_business_account",
  "entry": [{
    "id": "200000000000002",
    "changes": [{
      "field": "messages",
      "value": {
        "messaging_product": "whatsapp",
        "metadata": { "phone_number_id": "100000000000002", … },
        "contacts": [{ "wa_id": "15550001234",
          "profile": { "name": "Mary Smith" } }],
        "messages": [{
          "type": "interactive",
          "interactive": {
            "type": "button_reply",
            "button_reply": { "id": "hold",
              "title": "Yes, hold it" }
          }
        }]
      }
    }]
  }]
}

Fails where Meta fails, and tells you why.

A bad send gets Meta's error back, so your error handling runs for real. The message stays in the inbox, flagged, with the reason in plain words.

See every check in the docs
Your app sends

POST /v23.0/100000000000002/messages

{
  "messaging_product": "whatsapp",
  "to": "15550001234",
  "type": "template",
  "template": {
    "name": "order_ready",
    "language": { "code": "en_US" },
    "components": [{
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Mary" },
        { "type": "text", "text": "Main Street, 120" }
      ]
    }]
  }
}
The inbox shows

Mary Smith+1 555 000 1234 in Example Shop

1 rejected

order_ready en_US, 2 variables

Rejected with error 13200014:06

Example Shop sent 2 variables in the body, and order_ready expects 3. The app got the error and nothing was delivered.

body: number of localizable_params (2) does not match the expected number of params (3)

Your app gets back

HTTP/1.1 400 Bad Request

{
  "error": {
    "message": "(#132000) Number of parameters
      does not match the expected number of params",
    "type": "OAuthException",
    "code": 132000,
    "fbtrace_id": "A3kQ8x…"
  }
}

Works with no config.

One binary and a local SQLite file. Run it and it serves a default app. It never talks to Meta unless you run wapit sync.

terminal
$ wapit
Graph API mock on 127.0.0.1:8027
Inbox on http://127.0.0.1:8026

$ curl http://127.0.0.1:8027/v23.0/100000000000001/messages \
    -H 'Authorization: Bearer wapit-local-token' \
    -H 'Content-Type: application/json' \
    -d '{"messaging_product":"whatsapp","to":"15550000000",
         "type":"text","text":{"body":"Hello"}}'
{"messaging_product":"whatsapp","contacts":[...],"messages":[{"id":"wamid..."}]}