Fax lifecycle reference
The Outbound fax workflow, Inbound fax receive-side workflow, Inbound fax routing, and Fax lifecycle orchestration guides each cover one leg of fax in depth. This page is the reference that ties them together: the state model a fax row moves through, the timestamp fields that record when it moved, the retry posture on both legs, the per-DID inbound routing configuration, and the webhook events your consumers see at each transition. You will:- Read the lifecycle state model
- Read the timestamp fields on a fax row
- Follow the outbound path end to end
- Configure inbound routing destinations
- Consume the webhook events
- Use the audit trail for correlation
- Treat numbers as your fax address book
- Reconcile sends against terminal events
- Know the limits and where errors surface
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 for the field reference and limits.
1. The lifecycle state model
Every fax row on your tenant moves through a small state machine. Outbound rows start atqueued; inbound rows land directly in their terminal state because the carrier has already completed the transmission before Orbit sees the fax.
Two properties of this model matter for anything you build on top of it:
failedis terminal and there is no platform retry state. Orbit never re-attempts a failed fax on its own. If you want a retry policy, you own it: consumemessage.failedand re-send (a re-send is a new message row with its ownmessage_id). Section 3 gives a recommended cadence.sentis not terminal for fax. It means the carrier completed its side of the handshake; the billing and terminal verdict isdeliveredorfailed. Wait for the terminal event before you settle anything.
fax.received webhook (its own delivery retry to Orbit). Orbit deduplicates on the carrier fax_id, so a carrier retry never creates a duplicate message row and never re-fires message.received.
2. Timestamp fields on a fax row
Each fax row carries a timestamp per transition. A field is set once, the first time the row enters the matching state, and never rewritten afterwards;null means the row has not reached that state.
A fully-settled outbound row as returned by
GET /api/v1/fax/:id:
sent_at - created_at is queue wait, delivered_at - sent_at is transmission time, and a failed row’s failed_at - sent_at tells you how far into the handshake the attempt got. The list endpoint GET /api/v1/fax returns the same columns, cursor-paginated, so a dashboard refresh never needs the per-row call.
3. Outbound orchestration: request to terminal state
The outbound path runs six steps; only two of them are yours to trigger.- Dispatch.
POST /api/v1/messages/faxvalidates the document URL, thefrom/tonumbers, and the quality setting, then returns202withstatus: "queued". Nothing is billed. - Media fetch and render. The worker fetches your
media_urland prepares the document for transmission. A document that fails to fetch or render fails the send before the carrier is involved. - Carrier handoff. Orbit hands the document to the fax connection (the shared platform connection, or your dedicated one when
fax_connection_idis set).external_idpopulates at this point. - T.38 transmission. The carrier negotiates with the destination and transmits page by page. The row is
sending. - Verdict. The carrier confirms
delivered(wallet charged once, idempotency keyed per send so a worker re-dispatch cannot double-bill) orfailed(no charge, carrier diagnostic inerror_code/error_message). - Event emission.
message.deliveredormessage.failedfires on your tenant event bus and, when you setstatus_callbackon the send, POSTs to that URL too.
Retry policy
Orbit does not retry outbound faxes. Afailed row stays failed; there is no background re-attempt and no state between failed and a new send. This is deliberate: fax destinations fail for reasons that resolve on human timescales (a busy machine, an office that closed), and an automatic immediate retry mostly re-fails and re-consumes line time.
When you implement your own retry, a cadence that matches how fax lines actually free up:
Two refinements before you re-send:
- Downgrade quality on
poor_line_quality, do not just retry.very_high→high→normalshortens the T.38 handshake and survives noisier lines. The trade-offs are documented in the Outbound fax workflow guide. - Do not retry
no_fax_tone. The destination answered as a voice line; re-sending fax to a voice line never succeeds. Fix the directory entry instead.
message_id and created_at, so your reconciliation (section 8) should group attempts by your business document ID, not by message_id.
4. Inbound routing: where a received fax goes
When a sender faxes one of your DIDs, the inbound path runs without any request from you:- Carrier receive. The sender’s machine negotiates T.38 with the fax connection bound to the DID; the carrier assembles the received pages into a single PDF.
- Carrier webhook. The carrier posts
fax.receivedto Orbit with the sender (from), your DID (to), the carrierfax_id, and amedia_urlfor the PDF. Orbit verifies the carrier signature; a rejected signature returns401and stores nothing. - Tenant resolution and dedup. Orbit resolves your tenant from the
toDID and dedups onfax_id, so a carrier re-post creates exactly one message row. - Ledger write. The row is stored with
channel: "fax",direction: "inbound", and the carrier media URL. This happens whether or not routing is configured; the ledger is independent of the human surface. - Routing fan-out. Orbit looks up the per-DID routing configuration and delivers the PDF to the configured destinations (below).
- Event emission.
message.receivedfires on your tenant event bus regardless of routing, so a webhook consumer sees every inbound fax even with routing disabled.
GET and PUT /api/v1/numbers/:id/fax-routing, where :id is the number’s ID in your tenant inventory. The configuration:
GET /api/v1/numbers/:id/fax-routing returns the current config, or the disabled default for a DID that has never been configured, so you can render the “set up forwarding” empty state from it. A 404 means the number is not fax-reachable on your tenant; an enabled: false row means the ledger still persists inbound faxes but no fan-out runs. The full per-destination walkthrough, including the inbox ticket shape, is in the Inbound fax routing guide.
5. Webhook event reference
Three tenant events cover the fax lifecycle, all in the standard envelope withchannel: "fax". Verify the X-Orbit-Signature header on every delivery per the polyglot signature recipes; the event catalogue and signature mechanics live under Webhooks and Webhook security.
message.delivered
Fires when an outbound fax settles as delivered. This is the billing moment.
message.failed
Fires when an outbound fax fails terminally. The carrier diagnostic is in data.metadata; no charge is applied.
message.received
Fires for every inbound fax, whether or not per-DID routing is enabled.
status_callback on an outbound send, the same delivered/failed event also POSTs to that per-send URL; the signature recipe applies there too.
6. Tenant-scoped audit trail
Fax leaves two audit surfaces, both tenant-scoped:- The message ledger is the per-fax audit record. Every transition is recorded on the row itself via the immutable timestamp fields (section 2), so
GET /api/v1/fax/:idat any later point reconstructs when the fax was queued, dispatched, and settled. There is no separate event log to join; the row is the log. - Configuration changes land in your organization audit log. A
PUT /api/v1/numbers/:id/fax-routingwrites an audit entry recording the newenabledflag, recipient count, inbox flag, and whether an archive path is set, attributed to the acting user. Your Settings audit log (and its export/webhook, where enabled) is where you answer “who turned fax forwarding on, and when.”
Because a re-send is a new
message_id, “across retries” queries are queries across rows sharing your business ID, each row self-describing via its timestamps and terminal error_code.
7. Numbers as fax address books
A fax reaches you through a fax-capable DID, exactly as a call reaches you through a voice-capable one. The number is the address: senders dial it, the carrier binds it to your tenant, and the per-DID routing configuration (section 4) decides what happens on receipt. Outbound, thefrom on a send must be one of your fax-capable DIDs, and the number inventory shows fax capability per number.
Practical posture for most organizations is to treat fax DIDs as a small, published address book: one number per receiving function (contracts, pharmacy, billing), each with its own routing configuration, rather than one shared number with one fan-out. Provisioning and capability flags are covered in Buy numbers; the channel-level model in Fax channel and Fax delivery model.
8. Reconciliation recipe
To confirm every send reached a terminal state, tail your webhook stream into a map keyed bymessage_id, then cross-check against the ledger. Billing only ever attaches to delivered, so the same reconciliation proves the charge set.
unsettled returns, poll GET /api/v1/fax/:id before escalating: the record is readable from the moment the 202 returned, and a null external_id means the send is still queued rather than lost. The status variant GET /api/v1/fax/:id/status (cached about 10 seconds) cross-checks against the carrier once external_id populates. The billing-side reconciliation, including how fax charges appear in usage export, is in CDR, usage export, and billing reconciliation.
9. Limits and errors
Failure diagnosis beyond this table: the Fax channel error catalogue, Message failed troubleshooting, and the Troubleshooting hub.
Next steps
- Outbound fax workflow — the send-side walkthrough with billing and the on-success-charge model
- Inbound fax receive-side workflow — the receive-side ledger, events, and failure diagnostics
- Inbound fax routing — per-DID routing to email, inbox, and webhooks
- Fax lifecycle orchestration — the two-leg comparison: billing contract, failure vocabulary, retry posture
- Fax delivery model — the channel-level concept
- Fax channel — field reference, status lifecycle, error codes, limits
- Error handling runbook — terminal-state handling across channels
- CDR, usage export, and billing reconciliation — fax charges in usage export
- Webhook signature verification (polyglot) — verify every event above