Skip to main content

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 does data.status say?” Use it when you are reading inbound statuses, not designing receiver schemas.
This page walks the second one. The carriers Orbit delivers inbound statuses from — Telnyx Messaging, the Devotel wholesale softswitch (SMPP), DIDWW, and Meta (WhatsApp, Messenger, Instagram) — each report their own status vocabulary. The inbound catalog folds every one of those vocabularies onto the same canonical webhook events so your receiver branches once, not once per carrier.
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 closed data.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, or failure all 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 type value on your clipboard so you can paste it into your endpoint’s event subscription.
Use the dashboard when you are eyeballing a mapping — a receipt landed and you want to know which canonical event fired. Use the API when you want CI or codegen to pin it.

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.
Pull one canonical event’s upstream vocabulary when you are wiring a receiver branch:
The entry’s 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:
Two fields carry status. 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 canonical data.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:
Re-pin the fold before you silence the alarm. Fetch 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