Fax: delivery & inbound-routing model
The Fax channel page documents the request shape and limits, and the inbound fax workflow guide walks the receive-path setup. This page is the concept layer underneath both: how the end-to-end fax lifecycle is structured, where it deliberately diverges from the SMS/MMS model, and why. Read it before you wire fax into a reconciliation flow or branch webhook handlers on delivery status.1. Termination exception: fax rides Telnyx
The platform’s termination policy says outbound voice and SMS exit exclusively through the Devotel softswitch; Telnyx and DIDWW are used for inbound traffic only. Fax is a named exception to that rule — alongside MMS, it is one of the two channels permitted to terminate outbound on Telnyx’s Programmable Fax endpoint. The reason is protocol. A fax is not a message the softswitch’s SMS/e-mail switches can carry: it requires a live T.38 (or G.711 fallback) session negotiated endpoint to endpoint between two fax machines. That session looks exactly like a voice call to result, and Telnyx’s dedicated fax product supports it natively. Forcing fax through the softswitch would mean re-implementing T.38 relay; routing it through the SMS/MMS planes is a category error — those planes are store-and-forward, and T.38 is not. So fax joins MMS as the allowed exception, and your outboundPOST /api/v1/messages/fax call terminates on Telnyx by design.
Inbound fax rides Telnyx inbound rather than the softswitch — the carrier delivers a fax.received webhook carrying the received PDF as a hosted media URL, and Orbit writes it to the channel-agnostic inbound pipeline. Nothing in this path deviates from your SMS security posture; the exception is narrowly scoped to which provider terminates the fax leg.
2. Document model: PDF/TIFF, quality, and the media handoff
A fax is a document, not a text body. Themedia_url you supply at send time points the carrier at your PDF or TIFF, and the carrier converts it to T.38 packets in real time. Orbit never re-hosts the file — the carrier fetches directly from your URL at dispatch.
Three facts define the document model:
- PDF or TIFF, HTTPS only. The single permitted file types are PDF and TIFF, and the URL scheme is hard-bounded to HTTPS. Any other scheme is rejected before the carrier ever sees it.
qualityis a resolution setting, not an SLA toggle.normal(default),high, andvery_highcontrol the scanning resolution of the transmitted pages. Higher values raise the resolution of fine graphics but also lengthen the T.38 handshake, which on a lossy line is whenpoor_line_qualityfailures appear. Pickhighfor documents with small text or signatures; fall back tonormalon long documents to lossy destinations.- The fax lifecycle is a single attempt. Unlike a data channel, fax has no built-in re-transmission schedule at the carrier: the attempt either succeeds or fails in one go. Orbit’s queue worker re-dispatches the send only while the message is still
queued(a transient provider error before the T.38 session begins); oncesendingbegins, the attempt is live and its outcome lands as a delivery receipt.
orbit-fax:<message-id>) covers all of this: a retry of the same POST /messages/fax dedups at the carrier and does not bill twice.
3. Charge semantics: on-success-charge, success vs attempt
Fax takes a stricter stance on charging than the SMS/MMS model: your wallet is billed only when the carrier confirms a successful transmission (delivered or at minimum sent for the non-terminal success state), and failed attempts (busy, no_answer, poor_line_quality, and every other failure diagnostic) are not billed.
Success means the carrier completed the T.38 session and acknowledged the transmission — the distinction between sent (carrier-completed) and delivered (the end station confirmed receipt) matters for your reconciliation, but both are charge-eligible. Attempt means the row moved through sending but the handshake never completed — every one of the failure diagnostics documented on the Fax channel page is a non-chargeable attempt.
This matters for how you reconcile your wallet ledger: a failed fax row costs zero, and the message.failed events in your webhook stream carry no wallet deduction. Inbound faxes are likewise never billed. The on-success-charge semantics differ from the SMS/MMS per-submit or per-DLR model — see Wallets, credits, and charges for the generic lifecycle and this section for the fax exception.
4. Inbound fax: config endpoints, delivery service, and the routing split
An inbound fax arrives as a Telnyxfax.received webhook on the shared inbound endpoint. The webhook handler validates the event, resolves the tenant from the recipient DID, and writes a messages row with channel: "fax", direction: "inbound". From there, routing decides where the fax goes — the model deliberately separates config from delivery:
- The routing config endpoints (
GET /api/v1/numbers/:id/fax-routing,PUT /api/v1/numbers/:id/fax-routing) read and upsert a per-DID config:{ enabled, forward_emails[], deliver_to_inbox, archive_path }. The config lives in the canonical number inventory record and is keyed on the E.164 of the DID, so it survives number reassignment within your account. - The delivery service runs after the message row is persisted, and only when the config’s
enabledflag is set. It fans the received PDF out to the configured destinations: email forwards (each recipient gets the PDF attached, or a link-only fallback when the media fetch fails) and an inbox ticket (with sender, recipient, and media metadata attached). This service never throws — once the webhook has acknowledged the carrier and the message row exists, a downstream delivery failure degrades to a logged warning rather than a 500.
5. Delivery receipts: the status vocabulary and wire expectations
Outbound fax receipts don’t follow the SMPP DLR vocabulary. Telnyx fax DLRs arrive in thefax.* event namespace, and Orbit maps each event onto the same message status field you use for every other channel:
Three expectations make receipts tractable:
- The
deliveredvssentsplit reflects carrier truth. Some routes promote todeliveredimmediately after transmission; others only after the receiving machine completes the handshake’s final page. Design your reconciliation to treatsentas carrier-completed-but-non-terminal anddeliveredas the strong “the customer has it” signal, becausesentrows can still be promoted or demoted. - Carriers map failure diagnostics onto
error_code/error_message. Unknown carrier text passes through verbatim, so branch on the payload fields, not the enum of expected values. - Read back through your org-level webhook sink or per-send
status_callback. The provider-sideexternal_idis attached asynchronously once the transmission is dispatched, andGET /api/v1/fax/:id/statusreturns the provider status with a short per-tenant cache — but the durable reconciliation path is the status callback, because the direct-provider readback is a convenience surface, not the record of truth.
Where to go next
- Fax channel page — request shape, limits, and common patterns.
- Inbound fax workflow guide — end-to-end setup of the receive path.
- Delivery lifecycle — the generic state machine.
- Wallets, credits, and charges — billing lifecycle.
- Inbound message routing — the generic inbound rules this page is the exception to.