← all adapters

whatsapp-style

simulates WhatsApp Business Cloud API (Meta) v21.0

restadapter v0.1.08 routes

verification

VMVM tier — no SDK suite yet· surface 8 · not implemented 5 · differs 7

use it

# add to stunt.yaml
services:
  whatsapp:
    adapter: embedded:whatsapp-style

# then
$ stunt up

The 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.