Read the inbound event catalog for carrier-folded webhook normalization
When to use this catalog
Two dashboard catalogs answer two different questions about webhooks:- Event catalog (schema-level) — “what does the payload of each event type look like, and how do I validate it?” Use it when you are designing a receiver and need the JSON Schema, an example envelope, and a fingerprint to pin.
- Inbound event catalog (carrier-folded) — “my carrier reports
UNDLV/message.delivered/read/opt_out; which canonical Orbit event arrives on my endpoint, and what doesdata.statussay?” Use it when you are reading inbound statuses, not designing receiver schemas.
The catalog is static platform metadata with no tenant data. It answers
the mapping question for everyone the same way.
The four fold-in carriers
Each carrier supplies inbound message lifecycle signals in its own vocabulary. The fold maps each raw status onto one canonical event, a closeddata.status (delivered / failed / sent / read / opted_out), and a high-level kind (delivery / failure / opt_out / received):
On top of the per-carrier rows there is a universal inbound row: every carrier’s inbound message (SMS MO, a WhatsApp text, a Messenger message, a DIDWW MO) folds onto
message.received. A receiver listening for “a message arrived” needs exactly one branch.
Every mapping is a literal string match, case-insensitive and whitespace-trimmed. You never match a pattern — the catalog tells you exactly which wire strings resolve, and matching is first-hit-wins in a published order.
Browse the dashboard view
In the dashboard, open Developer → Webhooks → Inbound event catalog at/developer/webhooks/inbound-catalog. The page is role-gated to owner, admin, and developer, and it renders the same table the API serves.
The page is a searchable list on the left and a detail pane on the right:
- Search matches canonical events, canonical statuses, kinds, and any raw upstream string — typing
undlv,delivered, orfailureall narrow to the same entries. - Selecting a canonical event opens its detail: the canonical
data.status, the kind, and the per-carrier vocabulary grouped into Telnyx, the Devotel softswitch, DIDWW, and Meta. - Copy event name puts the canonical
typevalue on your clipboard so you can paste it into your endpoint’s event subscription.
Fetch the mapping over the API
GET /api/v1/webhooks/inbound-events returns the same table in the standard { data, meta } envelope. The static catalog needs no tenant scope; the read is authed the same way as the rest of the webhooks API.
equivalent_upstream array is the literal-match table: every (provider, raw status) pair that resolves to this canonical event, returned in the order the normalizer tries them. Pin that array’s checksum in CI the same way you pin event-schema fingerprints — a carrier vocabulary move shows up as a mapping-table diff, and you want that to fail your build before it silently skips a receiver branch.
One receipt walked end to end
A Telnyx delivery status arrives on your endpoint after a one-step fold. Walk a concrete receipt through:
The signed envelope your receiver gets:
data.status is the closed canonical set you switch on. data.provider_status echoes the carrier’s own term verbatim, so your per-carrier detail (an SMPP EXPIRED versus REJECTD, or a Telnyx message.sending_failed versus delivery_failed) survives the fold. The normalized block is the same mapping the catalog publishes — event, status, kind, provider, raw status — stamped at dispatch time so your receiver never has to repeat the lookup.
Reconcile a receipt that arrives without a normalized block
The fold is a closed vocabulary. A carrier string the catalog does not cover resolves to no mapping, and the dispatch stamps the canonicaldata.status the emitter already carried with no normalized block. That is the difference between a mismapping (bad) and a non-mapping (expected for genuinely novel statuses): a non-mapping still delivers the canonical event, it just does not carry the catalog-verified normalization block.
Treat a missing normalized block as a reviewer alarm, not a drop:
GET /api/v1/webhooks/inbound-events and check the raw string against the equivalent_upstream array the endpoint returns: if it appears, the mapping you are pinning in CI is stale; if it does not, the status is outside the published vocabulary and you decide whether to treat it as delivered / failed / read / sent / opted_out in your own handler. The catalog is the reference, not the unreachable side.
See also
- Event catalog (schema-level) — the sibling design-time catalog this page pairs with
- Inbound event catalog reference — the index-style page this guide complements
- Normalized inbound event envelope — the
data.normalizedblock anatomy - Build a durable webhook consumer — the receiver this catalog feeds