← all adapters
whatsapp-style
simulates WhatsApp Business Cloud API (Meta) v21.0
restadapter v0.1.08 routes
verification
use it
# add to stunt.yaml
services:
whatsapp:
adapter: embedded:whatsapp-style
# then
$ stunt upThe embedded: source extracts this adapter from the binary — no git clone, fully offline.
how to mock the WhatsApp Business Cloud API (Meta) API locally
Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of WhatsApp Business Cloud API (Meta). It works offline, keeps state across requests and restarts, and every response is synthetic: the route shapes, status codes, pagination, and webhook delivery follow the real WhatsApp Business Cloud API (Meta) v21.0 surface — the data is fake, the behavior is real.
backing stores
messagestemplatesmediaphone_numbers
api surface (8)
generated from the adapter manifest — exactly what boots, nothing more
8 routes
POST/v21.0/{phone_number_id}/messages
POST/v21.0/{phone_number_id}/register
POST/v21.0/{phone_number_id}/media
GET/v21.0/{media_id}/content
GET/v21.0/{waba_id}/message_templates
POST/v21.0/{waba_id}/message_templates
GET/v21.0/{resource_id}
POST/v21.0/{template_id}
VM-verified behaviors (34)
- ✓a missing bearer is 401 in the Meta error envelope
- ✓wrong schemes, bare tokens and unknown bearers answer the same 190
- ✓every route enforces the same gate
- ✓the seeded test token dies at its clock-derived 10-year expiry
- ✓a text send answers Meta's contacts + messages envelope
- ✓message ids are a zero-padded monotonic sequence
- ✓a template send is accepted and reads back as type template
- ✓a send without a usable recipient is a code-131026 400
- ✓mark-as-read is unmodeled: a status=read body falls into the send path
- ✓a fresh message reads back sent
- ✓the status stays sent through the in-flight window
- ✓delivered is derived at +3s and stays delivered
- ✓simulate_fail ends at failed (simulator extension, as-is)
- ✓unknown wamids are a code-803 404
- ✓each send emits exactly one signed messages webhook
- ✓no status webhook fires before the terminal window
- ✓the terminal transition emits the statuses webhook exactly once
- ✓a multipart upload stores real bytes and reports their digest
- ✓the content download returns the exact bytes at the stored mime
- ✓a specific part content-type outranks the filename extension
- ✓a metadata-only JSON upload has no bytes to download
- ✓uploads demand messaging_product=whatsapp (code 100)
- ✓a multipart body without a file part is a 1304 400
- ✓the seeded phone reads back its registration profile
- ✓register answers success true and the phone stays readable
- ✓register on an unknown phone id still succeeds (as-is)
- ✓an unknown resource id is a code-803 404
- ✓the seeded welcome_message template lists APPROVED
- ✓a created template starts PENDING and echoes its payload
- ✓the approval lifecycle moves PENDING to APPROVED and REJECTED
- ✓an invalid status target leaves the template unchanged (as-is)
- ✓unknown template ids are a code-803 404
- ✓templates page by limit + after cursors
- ✓a malformed after cursor is a code-100 400
Not implemented (5)
- Only text and template message types; no image, video, document, or interactive
- No mark-as-read (status=read) or message reaction sends
- No business profile, flows, or commerce surfaces
- No template delete or conversation analytics endpoints
- No inbound customer-message webhook simulation
Differs from the real API (7)
- 24-hour customer service window not enforced; free-form sends never fail with 470
- delivered is clock-derived at a fixed +3s after send, not real carrier delivery
- simulate_fail:true on send is a simulator extension forcing terminal failed status
- Template review is client-driven: POST {template_id} flips PENDING to APPROVED/REJECTED
- mark-as-read is unmodeled: a status=read body falls into the send path
- register on an unknown phone id still answers success
- created_at is a fixed constant, not clock-derived
want more of the WhatsApp Business Cloud API (Meta) surface? adapters are YAML + Starlark — the authoring guide covers adding routes, fixtures, and state machines. Open an issue for anything big first, or just file a PR — merged routes ship in the next release.