Quick start

From docker run to a signed webhook and the user's answer. Every call below runs as written against a fresh container.

  1. Run it

    Tell it where your application listens for webhooks, and the secret to sign them with.

    docker run --rm -p 3010:3010 \
      -e WEBHOOK_URL=http://host.docker.internal:4000/webhooks/whatsapp \
      -e WEBHOOK_SECRET=my-app-secret \
      --add-host host.docker.internal:host-gateway \
      ghcr.io/instituto-ama/whatsapp-mock:latest

    It boots with a business, the WhatsApp Business Account 100000000000001 and the number 1234567890, registered and with calling on. The panel is at http://localhost:3010.

  2. Point your application at it

    Same paths, same bodies, same answers, same error codes. Any bearer token is accepted, as long as there is one.

    - WHATSAPP_GRAPH_URL=https://graph.facebook.com/v23.0
    + WHATSAPP_GRAPH_URL=http://localhost:3010/v23.0
      WHATSAPP_PHONE_NUMBER_ID=1234567890
      WHATSAPP_TOKEN=test
  3. Create a template

    It goes through a review, approved in 2 seconds by default, and the verdict arrives as the message_template_status_update webhook.

    curl -X POST localhost:3010/v23.0/100000000000001/message_templates \
      -H 'Authorization: Bearer test' -H 'Content-Type: application/json' \
      -d '{"name":"appointment_reminder","language":"pt_BR","category":"UTILITY",
           "components":[{"type":"BODY","text":"Olá {{1}}, sua consulta é amanhã às {{2}}.",
             "example":{"body_text":[["Maria","14:30"]]}},
           {"type":"BUTTONS","buttons":[{"type":"QUICK_REPLY","text":"Confirmar"},
             {"type":"QUICK_REPLY","text":"Remarcar"}]}]}'
    # → {"id":"…","status":"PENDING","category":"UTILITY"}
  4. Send it

    curl -X POST localhost:3010/v23.0/1234567890/messages \
      -H 'Authorization: Bearer test' -H 'Content-Type: application/json' \
      -d '{"messaging_product":"whatsapp","to":"5511987654321","type":"template",
           "template":{"name":"appointment_reminder","language":{"code":"pt_BR"},
           "components":[{"type":"body","parameters":[
             {"type":"text","text":"Maria"},{"type":"text","text":"14:30"}]}]}}'
    # → {"messages":[{"id":"wamid.…","message_status":"accepted"}]}, then sent, delivered, read

    Open the panel: the conversation with 5511987654321 shows the template as WhatsApp draws it, with its buttons.

  5. Answer as the user

    Tap a button or type in the panel, or do it from a test with one header:

    curl -X POST localhost:3010/v23.0/1234567890/messages \
      -H 'Authorization: Bearer test' -H 'Content-Type: application/json' \
      -H 'X-Mock-From: 5511987654321' -H 'X-Mock-Contact-Name: Maria Souza' \
      -d '{"messaging_product":"whatsapp","type":"text","text":{"body":"Oi! Quero remarcar."}}'
    # → your endpoint receives the messages webhook, signed with x-hub-signature-256
  6. In your compose

    services:
      whatsapp:
        image: ghcr.io/instituto-ama/whatsapp-mock:latest
        ports:
          - '3010:3010'
        environment:
          WEBHOOK_URL: http://host.docker.internal:4000/webhooks/whatsapp
          WEBHOOK_SECRET: my-app-secret
          WEBHOOK_LOCALHOST_REWRITE: host.docker.internal
        extra_hosts:
          - 'host.docker.internal:host-gateway'   # Linux
        volumes:
          - whatsapp-data:/app/data               # conversations, templates, numbers, media
    volumes:
      whatsapp-data:

    Templates can also be seeded from files: every JSON in a mounted templates/ folder is imported at start and when it changes.

  7. In CI

    Keep nothing between runs and take the waits out:

    WHAP_DB=:memory:
    MOCK_WEBHOOK_RESPONSE_DELAY_MS=0
    MOCK_TEMPLATE_REVIEW_DELAY_MS=0