Sign inSign up

dgadelha/whaloc

By dgadelha

โ€ขUpdated 18 days ago

Local emulator of the Meta WhatsApp Cloud API: signed webhooks, chat-style web UI, one Docker image

Image
Developer tools
0

351

dgadelha/whaloc repository overview

โ whaloc

whaloc is a local emulator of the Meta WhatsApp Cloud API (Graph API v25.0) in a single Docker image, configured entirely through environment variables. It plays Meta's whole side of a WhatsApp integration โ€” accepting sends of every message type, minting Meta-shaped wamids, walking the status ladder, moderating templates, storing media and POSTing signed webhooks back to your app โ€” so an application that talks to the Cloud API runs entirely offline: no Meta app, no business number, no public tunnel. Point your app's GRAPH_API_BASE_URL at whaloc, open the web UI, and be the WhatsApp user on the other side of the conversation.

It is deterministic by default: nothing fails, drops or changes state on its own. Rate limits, expired tokens, template rejections, media expiry and number changes all exist โ€” and happen only when you trigger them, from the UI or from a test.

๐Ÿ“– Full documentation lives on GitHub: READMEโ  ยท integration guideโ  ยท behavioral contract (SPEC)โ  ยท issuesโ 

โ Quickstart

docker run --rm -p 8080:8080 \
  -e WHALOC_WEBHOOK_URL=http://host.docker.internal:3001/meta-webhooks \
  -e WHALOC_APP_SECRET=dev-app-secret \
  -e WHALOC_WEBHOOK_VERIFY_TOKEN=dev-verify-token \
  dgadelha/whaloc:latest

Open http://localhost:8080โ  for the chat-style web UI and point the app under test at GRAPH_API_BASE_URL=http://localhost:8080/v25.0 (from another container on the same compose network: http://whaloc:8080/v25.0).

Seeded IDs are derived deterministically โ€” the same on every machine โ€” so this works verbatim against a fresh container (any non-empty bearer token is accepted):

curl -X POST http://localhost:8080/v25.0/573542517421694/messages \
  -H 'Authorization: Bearer dev-token' -H 'Content-Type: application/json' \
  -d '{"messaging_product":"whatsapp","to":"5571990000001","type":"text","text":{"body":"olรก"}}'

The seed also ships a pre-approved, zero-parameter template (hello_whaloc), so a type: "template" send works cold too. All seeded IDs are shown, with copy buttons, in the UI's Settings view.

โ What's in the box

  • The Graph surface your app already calls โ€” messages of every type, media upload/download (with Range), the resumable Upload API, templates with Meta's filters and cursor paging, phone-number management, business profile, subscribed_apps โ€” answering with Meta's exact envelopes, fbtrace_id and error codes.
  • Signed webhooks โ€” inbound messages, statuses, template/quality/account events, all X-Hub-Signature-256-signed over the exact bytes sent, with a browsable, redeliverable delivery log.
  • A web UI where you act as the WhatsApp user: reply, react, send media, mark read, fail a message with a real Meta error code, approve or reject templates.
  • Deterministic error simulation โ€” token expiry, injection rules with Meta's rate-limit envelopes and throttling headers, media TTL.
  • A control-plane API + WebSocket so everything the UI can do, a test can do โ€” plus whole-state export/import for shareable reproductions.

โ Essential configuration

Environment variables only โ€” blank counts as unset, and an invalid environment prints every problem and exits. The most common ones:

VariableDefaultPurpose
WHALOC_PORT8080Listen port (one port serves the API mock, control plane, UI)
WHALOC_PUBLIC_URLhttp://localhost:8080Base for generated media URLs โ€” must be reachable by your app
WHALOC_WEBHOOK_URLunset โ†’ webhooks disabledWhere signed webhooks are POSTed
WHALOC_APP_SECRETunset โ†’ unsignedHMAC key for X-Hub-Signature-256
WHALOC_WEBHOOK_VERIFY_TOKENunsetToken echoed in the hub.challenge handshake
WHALOC_SEED1 WABA, 1 number, 1 templateJSON seed; omitted IDs are derived deterministically
WHALOC_DB_PATH:memory:SQLite file for persistence, e.g. /data/whaloc.db
WHALOC_MEDIA_BACKENDlocallocal directory or s3 (MinIO, R2, AWSโ€ฆ)

The full referenceโ  covers seeding, status ladder timing, template auto-approval, strict tokens, media TTL and the S3 settings.

Persistence: the default database is in-memory โ€” every restart starts from the seed. Mount the declared /data volume and set WHALOC_DB_PATH=/data/whaloc.db to keep state. The container runs as the unprivileged node user (uid 1000).

โ Tags

  • latest โ€” the current release (moves with tagged releases and main)
  • X.Y.Z, X.Y, X โ€” the semver ladder, from release tags
  • sha-<short> โ€” every build, pinned to a commit

Images are multi-arch (linux/amd64, linux/arm64) and published with SLSA provenance and an SBOM; provenance is verifiable with gh attestation verify oci://docker.io/dgadelha/whaloc:latest --owner dgadelha. Also published to GitHub Container Registry as ghcr.io/dgadelha/whaloc.

โ License

MITโ . whaloc is an independent reimplementation for local development, unaffiliated with and unendorsed by Meta.

Tag summary

Content type

Image

Digest

sha256:32fd339e8โ€ฆ

Size

59.5 MB

Last updated

18 days ago

docker pull dgadelha/whaloc