What it simulates
| Area | Cloud API | What happens |
|---|---|---|
| Messages | POST /{phone-number-id}/messages | Text, media, location, contacts, interactive, reactions, replies, templates (header, body, footer, buttons, carousel, limited-time offer, authentication). Receipts sent, delivered, read; typing indicator. |
| The user | the same call with X-Mock-From | The user writes, replies, sends media and voice notes, gives receipts: the message enters as inbound and goes to your webhook. |
| Templates | /{waba-id}/message_templates, /{template-id} | Create, edit, delete, list. A review with a verdict and a wait you choose, the message_template_status_update webhook, Meta's send errors. |
| Media | /{phone-number-id}/media, /{media-id} | Upload (multipart, Meta's types and size limits), describe, download with a token, delete. Files stay on disk. |
| Phone numbers | /{waba-id}/phone_numbers, request_code, verify_code, register, settings | Business, WABAs and numbers; the onboarding of a new number with the SMS code; two-step PIN; calling settings. An unregistered number cannot send (133010). |
| Calls | POST /{phone-number-id}/calls, call_permissions | The Calling API with real WebRTC audio: either side calls, with the permission request and reply, pre_accept, accept, reject, terminate and the calls webhooks. |
| Webhooks | POST /{phone-number-id} (webhook_configuration) | Per-number override verified with hub.challenge, signed with x-hub-signature-256, an optional response delay, a log of every attempt. |
| Everything else | the rest of the specification | Routed, validated and answered with Meta's own example (Flows, groups, business profile, QR codes…). |
Meta's OpenAPI file for v23.0 is vendored in spec/v23.0. Every request is validated against it, every answer is held to its response schemas, and a contract test sends every request example of the specification through the mock. Where the specification contradicts Meta's own examples, the correction is written down with its reason in errata.json.
Calls and numbers
The phone button of the chat header calls with real WebRTC audio, the browser's microphone and speaker. The user calls the business (POST /{phone}/calls with X-Mock-From and the offer) and the application hears calls / connect, answering pre_accept and accept with its SDP. The business calls the user: without the user's permission it sends the call_permission_request first; with it, connect with the offer, and the user's phone rings. Calls stay in the conversation as call log bubbles: answered with the duration, missed, declined.
With nothing typed, the send button is a microphone, as on the phone: it records a voice note (Ogg Opus, mono, 48 kHz) and sends it as audio with voice: true.
- Create a number.
POST /{waba-id}/phone_numberswithcc,phone_numberandverified_name: it comes not verified, not registered. - Ask for the code.
POST /{phone-number-id}/request_code; the code Meta would send shows on the number and in a toast. - Verify it.
POST /{phone-number-id}/verify_code; a wrong code is(#136025). - Register it.
POST /{phone-number-id}/registerwith the six-digit PIN; the first one is kept, another is(#133005). - Turn calling on.
POST /{phone-number-id}/settingswithcalling; it needs the number registered and a webhook ((#138018)). - Take it off the Cloud API.
POST /{phone-number-id}/deregister. A number off the Cloud API cannot send ((#133010)) nor call.
The panel
Open http://localhost:3010. It speaks English and Portuguese and keeps no private routes: every action is the Cloud API call your application would make.
Zoom
Interactive WhatsApp Web
Active customer conversation with billing invoices, status checkmarks, payment link pills, and reply composer.
Zoom
WebRTC Voice Calling
Full in-browser voice call simulation with audio stream, avatar, ringing animation, mute, and hangup controls.
Zoom
Template Builder & Phone Preview
Design templates with media headers, body variable chips {{1}}, and test live on the smartphone mockup.
Zoom
WhatsApp Web Dark Mode
Native dark mode palette with voice notes, playable waveforms, and dark chat bubbles.
Zoom
Webhook Deliveries Queue
Real-time grid of webhook deliveries showing HTTP 200 OK badges, delivery latencies, and retry history.
Zoom
New Conversation Modal
Pop-up dialog to initiate outbound or simulated incoming conversations with custom contact numbers.
Zoom
Meta Graph API Explorer
Interactive v23.0 specification reference with cURL examples and stateful operation documentation.
Zoom
Marketing Cards Carousel
Preview rich multi-card interactive carousel templates directly inside the smartphone simulator.
Zoom
Webhook Destination & HMAC
Configure target webhook URLs, HMAC signature verification secrets, or toggle instant verification bypass.
Zoom
Cloud API Event JSON
Expand any webhook delivery to inspect the exact Meta Cloud API JSON structure, timestamps, and pricing.
Zoom
Meta Business Numbers & WABAs
Manage Meta Business Accounts, WABA IDs, phone numbers, quality rating scores, and certificates.
Zoom
Delivery Engine Settings
Toggle between automatic status progression (sent → delivered → read) and granular manual simulation mode.
Zoom
Known Template Catalog
Complete library of 20 verified templates across Marketing, Utility, and Authentication categories.
Zoom
Search & Unread Filtering
Quickly filter unread chats, clinic unanswered queries, or search conversations in real-time.
Zoom
Interactive Location & Map
Simulate location sharing templates with interactive map card, venue address, and directions buttons.
Templates from files
Every JSON in templates/ is imported at start and when it changes, and templates/examples/ brings one example of every component Meta allows. Details in docs/templates.md.
Configuration
| Variable | Default | What it does |
|---|---|---|
PORT | 3010 | HTTP port |
WEBHOOK_URL | (empty) | Default webhook; phone:url maps a number |
WEBHOOK_SECRET | (empty) | Signs webhooks with x-hub-signature-256 |
WEBHOOK_LOCALHOST_REWRITE | (empty) | Host that replaces localhost in webhook URLs (host.docker.internal) |
MOCK_WEBHOOK_RESPONSE_DELAY_MS | 5000 | Wait before each webhook delivery (also in the panel) |
MOCK_DELIVERY_MODE | auto | manual: ticks only move when the user's phone gives a receipt |
MOCK_TEMPLATE_REVIEW_DELAY_MS | 2000 | Default duration of a template review |
WHAP_CALL_RING_MS | 60000 | How long a call rings before it ends unanswered |
WHAP_DATA_DIR | /app/data in the image | SQLite database and media files |
WHAP_DB | (file) | :memory: keeps nothing |
MOCK_PHONE_NUMBER_ID, MOCK_WABA_ID, MOCK_BUSINESS_ID | 1234567890, 100000000000001, 100000000000002 | The ids of the default number, WABA and business |
MOCK_ALLOWED_HOSTS | (empty) | Extra Host names accepted (a compose service name) |
Development
bun install
bun run src/index.ts server # http://localhost:3010
bun --bun vitest run --maxWorkers=2 # tests, including the contract test
bun tsc --noEmitA new Graph version is a new spec/<version> folder and an entry in src/graph/versions.ts; the contract test runs every version. More in docs/panel.md and the README.
License
Apache-2.0, see LICENSE. Meta's OpenAPI specification in spec/ is MIT, Copyright (c) Meta Platforms, Inc. and affiliates.
Not affiliated with Meta or WhatsApp. An independent simulator of their public API for local development and testing. WhatsApp and Meta are trademarks of their respective owners.