Inbound fax receive-side workflow
The Outbound fax workflow guide covers sending; the Inbound fax routing guide covers the per-DID routing knobs. This guide operates the receive side as a whole: how an inbound fax is addressed and deduplicated, what events it emits, how routing decides its destinations, how inbound failure modes differ from the outbound T.38 vocabulary, when a tenant-attached Telnyx fax connection changes the receive pipe, and the guardrails that keep junk fax traffic out of your inbox. You will:- Understand billing on the receive side — and why inbound costs read differently on the CFO dashboard
- Walk the receive-side ledger — the message row behind every inbound fax
- Anchor the contract — inbound receipts are free, outbound bills on success
- Consume the receive-side event — the
message.receivedpayload - Diagnose receive-side failures — the receive vocabulary is not the outbound T.38 vocabulary
- Attach your own carrier connection when the platform default is wrong
- Run the production checklist
Prerequisites
- A fax-capable number on your account (see Buy numbers — inbound fax works with the same inventory).
- An API key from Settings → API Keys (a
dv_live_sk_…live key) for the routing calls below. - A public HTTPS endpoint if you want your own webhook consumer in addition to email/inbox routing.
1. Billing on the receive side
Outbound fax is on-success-charge: the wallet is billed only when the carrier confirms a successful transmission, and failed attempts (busy, no answer, no fax tone, poor line quality) are never billed — the full mechanics are in the send-side guide. Inbound fax is the inverse: receiving a fax is a no-charge event. There is no wallet deduction and no per-page meter on the receive side; the cost you see on the CFO dashboard for inbound fax is the number-inventory cost of the DID, not per-fax usage. That asymmetry matters when operators read the billing ledger for the shared fax surface:- An outbound
deliveredevent is the one that carries the charge; an inboundmessage.receivedevent never does. - Reconciliation of the spend report therefore separates on one flag:
direction: "outbound"rows bill,direction: "inbound"rows do not. - A tenant-attached carrier connection (section 6) changes who owns the receive pipe, not the zero-charge property — inbound remains free either way.
2. The receive-side ledger
Every inbound fax is persisted as amessages row with channel: "fax", direction: "inbound", the sender’s number in from, your DID in to, and the provider media URL in media_url. The row is the audit record of the receive; the PDF copy forwarded to email, the inbox ticket, or fetched by your webhook consumer is the durable artifact.
Walk the ledger like this:
- Addressing decides persistence. The provider’s
fax.receivedwebhook posts the sender (from), the DID (to), the media URL, and a providerfax_id. Orbit resolves the tenant from thetoDID. A DID no tenant owns drops at this step; a DID owned but unrouted persists with no downstream delivery. - The dedup marker. The provider
fax_idis the idempotency key. A carrier retry of the samefax.receivedevent creates exactly one message row — carrier retries are expected, not duplicates. - Routing fans out once. If routing is enabled, the PDF is fetched in a bounded fetch and fanned out to the configured destinations (email, inbox, or your webhook consumer).
{ enabled, forward_emails, deliver_to_inbox, archive_path } shape the routing guide documents. This guide’s addition to that loop: the record exists regardless of routing — enabled shapes the human surface, not the ledger.
3. The contract: inbound free, outbound on-success
The fax channel’s billing contract is direction-scoped. From the operator’s side of the ledger:
Read the wallet ledger against that table: an outbound charge without a matching
delivered event, or any charge on an inbound event, is the anomaly to escalate.
4. Consume the receive-side event
Every inbound fax arrives on your tenant event bus asmessage.received with channel: "fax", whether or not per-DID routing is enabled. The envelope:
data.from— the sender’s number (screening input, see section 5).data.to— the receiving DID (addressing input, and the key your routing decision should match).data.metadata.media_url— the provider-issued PDF link. Presigned and temporary: fetch it promptly or rely on the email-forward/inbox-ticket copy of record.
X-Orbit-Signature, reject deliveries older than five minutes, return 2xx fast, and dedup on the event id — the recipe in Webhook security applies verbatim.
5. Receive-side failure diagnostics
The outbound side’s failure vocabulary is the carrier’s T.38 diagnostics — busy, no answer, no fax tone, poor line quality, handshake rejection — passed through onmessage.failed. The receive side never sees those codes: an inbound fax either lands or the receive pipe has an operational failure of its own. The failure classes to route on for the receive side:
- Signature rejected — Telnyx’s signature failed verification; the webhook returns
401and the fax is counted but never stored. Fix the carrier public key, or check for a proxy stripping signature headers. - Unresolvable DID — the
tonumber does not resolve to a tenant; the fax drops. Audit which DIDs are published as fax-reachable before this bites. - Routing write-time rejection — a bad
forward_emailsentry or more than 20 recipients fails with422at write time; fix the config before traffic lands. - Media fetch failure — the bounded PDF fetch timed out or returned non-2xx; email recipients get the link only. Delivery is still counted; the artifact is degraded, not dropped.
- Junk screening (rate guard) — a sliding-window per-(tenant, channel, sender) flood guard throttles abusive inbound senders before storage: at the soft cap the pipeline persists and logs; at the hard cap it drops the inbound, audit-logs
inbound.abuse_blocked, and returns200so the provider does not retry-burst. Redis-down fails open (allows). Content-classifier screening (which runs for SMS/WhatsApp/email/RCS) is channel-gated and does not yet score fax — build your own on themessage.receivedconsumer if the rate guard is not enough.
6. Tenant-attached carrier connections
Receive-side traffic normally rides Orbit’s shared Telnyx fax connection — the same default the outbound send uses. Attach your own Telnyx Fax application when the platform default is wrong:- Compliance / data residency — inbound faxes must traverse a carrier account you control and audit.
- Carrier-level features — a dedicated fax application with its own number inventory, routing rules, or webhook-secret rotation.
fax_connection_id in tenant settings. Resolution order is: tenant-level fax_connection_id → platform default → legacy voice connection fallback (the fallback logs a deprecation warning). The same resolution drives outbound and inbound symmetrically, so an org on a dedicated connection receives and sends over it.
Do not confuse connection choice with routing config. The connection decides which carrier account owns the receive pipe; the per-DID routing config (
GET/PUT /api/v1/numbers/:id/fax-routing) decides where the received fax is delivered. They compose — a tenant-attached connection still honors per-DID routing for its DIDs.7. Production checklist
Run through this list before a fax DID goes live:- Routing enabled on the DID.
enabled: truewith at least one destination (emails, inbox, or your webhook consumer). A bare DID persists only the message record. - Destinations validated.
forward_emailsaddresses are deliverable — the422at write time catches typos; later per-recipient delivery failures are logged and never block other recipients. - Junk screening understood. The flood guard is rate-based. If you need content screening, plan a
message.receivedconsumer that scores senders or OCR before humans see the fax. - Connection decision made. If compliance or residency requires a dedicated Telnyx Fax application, set
fax_connection_idbefore publishing the DID — traffic on the shared connection does not re-resolve later. - Consumer discipline (when you consume
message.receivedyourself): signature verification, five-minute replay window, fast2xx, dedup on eventid. - Retention expectations set. The message record is the audit trail; the PDF copy forwarded to email or the inbox (or fetched by your consumer) is the durable artifact. Treat the provider media URL as temporary.
Related
- Inbound fax routing — the per-DID routing guide this one complements
- Outbound fax workflow — the send-side companion: billing, receipts, and T.38 failure classes
- Fax channel — payloads, limits, and the error catalogue
- Webhook consumer — reliable endpoint design
- Webhook security — signature verification recipes