Skip to main content

Fax lifecycle orchestration

The Outbound fax workflow covers sending; the Inbound fax receive-side workflow covers the receive-side ledger and events; the Inbound fax routing guide covers per-DID routing configuration. Each guide covers its leg in depth. This guide ties the two legs together so you see the lifecycle as a whole: where an inbound fax lands, what to do about partial pages, and how the inbound and outbound paths differ across every dimension that matters for operating fax at scale. You will:
  1. Understand the two-leg lifecycle at a glance
  2. Walk the inbound leg: where a received fax lands
  3. Walk the outbound leg: send, transmit, settle
  4. Handle failure modes per leg
  5. Understand warm-up, downgrade, and line-quality dynamics
  6. Decode escalation signals: carrier codes and terminal verdicts

Prerequisites

  • A fax-capable number on your account (see Buy numbers).
  • An API key from Settings → API Keys (a dv_live_sk_… live key).
  • Familiarity with the Fax channel page (Fax channel) for field reference and limits.

1. Two-leg lifecycle at a glance

The fax lifecycle splits cleanly into two independent legs, and one never blocks the other: Because the legs are independent, an inbound fax never touches the outbound send path and an outbound failure never blocks inbound receipt. The carrier connection (shared or tenant-attached via fax_connection_id) serves both legs symmetrically, but the two pipelines share only that connection — billing, events, and diagnostics diverge from there.

2. The inbound leg: where a received fax lands

When a customer faxes your number, the inbound leg runs end to end without you sending a request:
  1. Carrier handshake. The sender’s fax machine negotiates T.38 with the Telnyx fax connection bound to your tenant (the shared platform connection, or your dedicated connection if fax_connection_id is set). Orbit is not involved at this layer — the carrier handles T.38 negotiation, page-by-page reception, and assembly into a single PDF.
  2. Carrier webhook. The carrier posts fax.received to Orbit’s inbound endpoint with the sender’s number (from), your DID (to), a carrier fax_id, and a media_url pointing to the assembled PDF.
  3. Signature verification. Orbit verifies the carrier’s signature. A rejected signature returns 401 — the fax is counted but never stored. Fix the carrier public key or any proxy stripping signature headers.
  4. Tenant resolution and dedup. Orbit resolves the tenant from the to DID. The carrier fax_id is the idempotency key — a carrier retry creates exactly one message row, so a replayed webhook from the carrier is harmless.
  5. Persistence. The fax is stored as a messages row with channel: "fax", direction: "inbound", the sender in from, your DID in to, and the carrier media URL in media_url. The PDF copy that reaches email or the inbox is the durable artifact; treat the provider media URL as temporary.
  6. Routing. If the receiving DID has fax routing enabled, the PDF is fetched and delivered to the configured destinations — email recipients, the shared inbox, or your webhook consumer. Routing is documented in full in the Inbound fax routing guide. If routing is disabled or no destinations are configured, the message row is still persisted — routing shapes the human surface, not the ledger.
  7. Event emission. A message.received event fires on your tenant event bus, whether or not routing is enabled. Any webhook consumer you registered receives the event in the standard envelope. The Inbound fax receive-side workflow guide documents the event payload and consumer discipline.

Partial pages

A fax with missing pages — a sender’s machine jammed mid-transmission, a line drop after page N — arrives as a partial PDF. Orbit does not reject partial faxes and does not expose a page-count contract on the receive side. The carrier assembles whatever pages were successfully received and posts the PDF. If your workflow depends on page-count completeness (a signed contract must have all pages), verify the page count on your consumer after receipt — the carrier’s page-count metadata is available on the provider fax_id fetch, though Orbit’s standard message.received event does not surface it.

3. The outbound leg: send, transmit, settle

Sending a fax runs the reverse pipeline:
  1. Dispatch. POST /api/v1/messages/fax validates your document URL, from/to numbers, and quality setting. On success you get back a message_id with status: "queued". Nothing is billed.
  2. Carrier handoff. Orbit dispatches your document to the Telnyx fax connection. The carrier fetches the document from your media_url, negotiates T.38 with the destination, and transmits each page.
  3. Carrier verdict. The carrier confirms whether the transmission succeeded. If it did, the message moves to delivered and your wallet is charged. If it failed, the message moves to failed — no charge. The per-send idempotency key (orbit-fax:<message_id>) prevents a queue-worker re-dispatch from double-billing.
  4. Event emission. A message.delivered or message.failed event fires on your tenant event bus with channel: "fax". If you set a per-send status_callback, the event also POSTs to that URL.
The full send flow, including the on-success-charge contract and the document quality trade-offs, is documented in the Outbound fax workflow guide.

4. Failure modes per leg

The failure vocabulary is leg-scoped — the codes you see on message.failed for outbound are carrier T.38 diagnostics; the failure classes on the receive side are operational.

Outbound failure codes

After any outbound failure, re-sending is always your call — Orbit never retries a fax that reached failed.

Inbound failure classes

The full failure diagnostics are covered in the Inbound fax receive-side workflow (inbound) and Outbound fax workflow (outbound) guides.

5. Warm-up, downgrade, and line-quality dynamics

When a fax line has been idle, the first transmission in a window runs a longer T.38 handshake — the modems calibrate to the line. This warm-up period is invisible to you (the carrier handles it), but it has one operator-visible consequence: the first fax after a long idle window is the one most likely to hit poor_line_quality because the handshake calibration is fresh. If you see a poor_line_quality failure on the first send of the day and the second succeeds, the line was stable — the warm-up cost that first attempt. Re-send at the same quality before downgrading. When a poor_line_quality failure does recur, the right path is a quality downgrade, not a retry at the same level:
  • very_high → high — drops the handshake length significantly; survives most noisy lines.
  • high → normal — shortest handshake; sufficient for plain text documents.
  • normal has no further downgrade — at that point the line itself is the problem.
The quality field values (normal, high, very_high) and their trade-offs are documented on the Outbound fax workflow guide. The warm-up caveat does not apply to inbound fax — the carrier negotiates the session automatically, and a partial receive from a bad line arrives as a partial PDF. There is no inbound quality negotiation the operator controls.

6. Escalation signals: carrier codes and terminal verdicts

Some failure signals are not single-fax problems — they are signs that escalate beyond a re-send. Watch for these patterns in your delivery stream:

Carrier-level signals (outbound)

Receive-side signals (inbound)

For per-fax failure diagnosis, use the Fax channel error catalogue and the Message failed troubleshooting guide. The Troubleshooting hub collects every channel’s failure pages in one place.

Page-count limits

The carrier accepts up to 50 MB per document with no separate page-count cap — multi-page contracts and reports transmit in a single send. If a very long document is timing out on transmission, split it into multiple sends rather than bumping quality. On the receive side, the carrier assembles all received pages into a single PDF; a sender-side page-count cap applies at the carrier, not at Orbit, so the PDF you receive is whatever the carrier accepted.

DLR webhook shape

Delivery receipt events arrive through your tenant event bus and, when status_callback is set, through the per-send webhook URL. The message.delivered and message.failed events carry message_id, status, state_class ("terminal"), and is_terminal (true) — the same envelope as every channel. For fax specifically, message.failed carries the carrier’s error_code and error_message in data.metadata. The full event catalogue is in the events catalogue; the webhook security recipe is in Webhook security.

Next steps