verification
use it
# add to stunt.yaml
services:
braze:
adapter: embedded:braze-style
# then
$ stunt upThe embedded: source extracts this adapter from the binary — no git clone, fully offline.
how to mock the Braze REST API locally
Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of Braze REST API. 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 Braze REST API 2.0 surface — the data is fake, the behavior is real.
backing stores
userscampaigns
api surface (12)
generated from the adapter manifest — exactly what boots, nothing more
12 routes
POST/messages/send
POST/messages/schedule/create
POST/users/track
POST/users/alias/new
POST/users/identify
POST/users/delete
POST/users/export/ids
POST/campaigns/trigger/send
GET/segments/list
GET/messages/scheduled
POST/webhooks
GET/webhooks
VM-verified behaviors (23)
- ✓a missing credential is the 401 message envelope
- ✓an unknown app-group key is rejected
- ✓x-authorization carries the raw app-group key
- ✓track ingests attributes, events, and purchases
- ✓re-tracking the same external_id updates one profile
- ✓export aggregates custom events and purchases, dedups ids
- ✓fields_to_export projects the profile
- ✓per-record errors keep partial success
- ✓_update_existing_only never creates
- ✓fatal envelopes: the 75-id cap and undecodable JSON
- ✓alias/new creates an alias-only profile track can target
- ✓identify re-keys an alias-only profile when the external_id is new
- ✓identify merges an alias-only profile into an existing one
- ✓send fatals: message, variant, and recipient rules
- ✓send succeeds with a 32-hex dispatch id and emits message.sent
- ✓campaigns/trigger/send validates the campaign id
- ✓schedule/create mints a UUID-shaped schedule id
- ✓end_time is required
- ✓upcoming broadcasts list both schedules
- ✓the send transition derives on read, exactly once
- ✓segments list seeds and pages by cursor
- ✓one identifier type per delete
- ✓delete removes the profile and its dependent records
Not implemented (8)
- No canvas endpoints: /canvas/trigger/send, /canvas/list, /canvas/data_series
- No campaign analytics: /campaigns/list, /campaigns/data_series, /campaigns/details
- No /messages/schedule/update or delete; no /campaigns/trigger/schedule/create
- No /users/export by segment; no /events/list or /purchases/product_list
- No email endpoints: /email/status, /email/hard_bounces, /email/spam, /email/unsubscribes
- No subscription groups: /subscription/status/set, /subscription/user/status, /get
- No content blocks: /content_blocks list, info, create, update
- No preference centers: /preference_center/v1 list and per-user URL endpoints
Differs from the real API (6)
- POST /webhooks registration is a local extension; real Braze has no webhook REST API
- message.sent webhook payloads use a synthetic shape; Braze has no documented scheme
- scheduled sends flip to sent on first read past the time; message.sent fires once then
- key store models expiry for app-group keys; real Braze keys do not expire
- /users/track echoes *_processed counts the real API does not return
- Segments list pages by a cursor; the real endpoint pages by 100s of page param
want more of the Braze REST API 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.