> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Fax lifecycle orchestration: inbound and outbound legs

> Walk the full fax lifecycle — inbound receive, outbound send — and understand how the two legs differ in event stream shape, billing contract, failure vocabulary, and retry posture.

# Fax lifecycle orchestration

The [Outbound fax workflow](/guides/fax-send-workflow) covers sending; the [Inbound fax receive-side workflow](/guides/fax-receive-workflow) covers the receive-side ledger and events; the [Inbound fax routing](/guides/inbound-fax-workflow) 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](#1-two-leg-lifecycle-at-a-glance)
2. [Walk the inbound leg: where a received fax lands](#2-the-inbound-leg-where-a-received-fax-lands)
3. [Walk the outbound leg: send, transmit, settle](#3-the-outbound-leg-send-transmit-settle)
4. [Handle failure modes per leg](#4-failure-modes-per-leg)
5. [Understand warm-up, downgrade, and line-quality dynamics](#5-warm-up-downgrade-and-line-quality-dynamics)
6. [Decode escalation signals: carrier codes and terminal verdicts](#6-escalation-signals-carrier-codes-and-terminal-verdicts)

## Prerequisites

* A fax-capable number on your account (see [Buy numbers](/guides/buy-numbers)).
* An API key from **Settings → API Keys** (a `dv_live_sk_…` live key).
* Familiarity with the Fax channel page ([Fax channel](/channels/fax)) 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:

| Dimension | Inbound | Outbound |
| - | - | - |
| **Trigger** | A sender faxes your DID — the carrier posts `fax.received` to Orbit. | You call `POST /api/v1/messages/fax` with a document URL. |
| **Billing** | Free. Inbound receipts are never billed. | On-success: the wallet is charged only when the carrier confirms `delivered`. |
| **Event** | `message.received` with `channel: "fax"` | `message.delivered` or `message.failed` with `channel: "fax"` |
| **Failure vocabulary** | Operational: unresolved DID, signature rejection, media fetch failure, junk guard. | Carrier T.38 diagnostics: busy, no answer, no fax tone, poor line quality, handshake failure. |
| **Retry posture** | Carrier retries are deduplicated on `fax_id`; a retry creates no duplicate message row. | Orbit never retries a `failed` fax — re-sending is your call. |
| **Terminal state** | `received` — the fax is stored and routed; there is no further state transition. | `delivered` (success, billed) or `failed` (terminal, free). `sent` is non-terminal for fax — it means carrier-completed, wait for `delivered`. |

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](/guides/inbound-fax-workflow) 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](/guides/fax-receive-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](/guides/fax-send-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

| `error_code` | Meaning | What to do |
| - | - | - |
| `busy` | The destination line is occupied. | Re-send after a delay (the recipient's fax machine may free up). |
| `no_answer` | The destination did not answer. | Re-send after a longer delay; verify the number is a fax line. |
| `no_fax_tone` | The number answered as a voice line — no fax handshake detected. | Confirm the destination terminates a fax machine before re-sending. |
| `poor_line_quality` | The T.38 handshake completed but line noise corrupted the transmission. | Re-send at a lower quality setting — `normal` survives noisier lines than `high`. |
| (other) | Carrier returned no diagnostic, or an internal error occurred. | Surface to reconciliation; the attempt was not billed. |

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

### Inbound failure classes

| Failure class | Symptom | Resolution |
| - | - | - |
| **Unresolvable DID** | The `to` number does not resolve to a tenant. | Verify the DID is published as fax-reachable and assigned to your tenant. |
| **Signature rejected** | The carrier webhook returns `401`. | Fix the carrier public key or check for a proxy stripping signature headers. |
| **Media fetch failure** | The bounded PDF fetch timed out or returned non-2xx. | Email recipients get the download link instead of an attachment. Delivery is still counted. |
| **Routing write-time rejection** | A `422` when configuring `forward_emails` with a bad address or more than 20 recipients. | Fix the config before traffic lands — the `422` is at write time. |
| **Junk screening** | A per-tenant sliding-window rate guard throttles an abusive sender. | At the soft cap the pipeline persists and logs; at the hard cap the fax is dropped with `inbound.abuse_blocked`. |

The full failure diagnostics are covered in the [Inbound fax receive-side workflow](/guides/fax-receive-workflow) (inbound) and [Outbound fax workflow](/guides/fax-send-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](/guides/fax-send-workflow#5-pdf-vs-tiff-and-picking-a-quality) 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)

| Signal | Pattern | Escalation |
| - | - | - |
| **Persistent busy or no-answer** | The same destination number fails with `busy` or `no_answer` across multiple attempts spanning different times of day. | The destination machine is likely offline or the number no longer terminates a fax line. Stop re-sending and flag the destination for review. |
| **Rate of `no_fax_tone`** | More than a handful of sends to different numbers hit `no_fax_tone` in a short window. | The numbers you are sending to are voice lines, not fax machines. The problem is directory quality, not line quality. |
| **Clustered `poor_line_quality`** | Multiple sends to different numbers fail with `poor_line_quality` at the same time. | A regional carrier outage or a degraded Telnyx trunk — open a support case with the carrier-side diagnostics attached. |
| **No events at all** | A send returns `queued` and never reaches `sent`, `delivered`, or `failed`. | The queue worker may be stalled or the carrier dispatch failed silently. Check your event bus subscription and poll `GET /api/v1/fax/:id` for the record. |

### Receive-side signals (inbound)

| Signal | Pattern | Escalation |
| - | - | - |
| **DID unreachable** | Senders report your fax number does not answer. | The DID may not be fax-capable or the carrier connection may be misconfigured. Verify the DID is published as fax-reachable and that a fax-capable connection is bound. |
| **No message rows for known sends** | A sender confirms they sent a fax, but no `message.received` event appears and no message row exists. | The carrier webhook may not be delivering, or the DID may not resolve to your tenant. Check the carrier portal for webhook delivery logs. |
| **High rate of signature rejections** | Multiple `401` responses in the carrier's webhook delivery logs. | The carrier's signing key may have rotated. Update the public key in your tenant's fax connection configuration. |

For per-fax failure diagnosis, use the [Fax channel](/channels/fax) error catalogue and the [Message failed troubleshooting](/troubleshooting/message-undelivered-failed) guide. The [Troubleshooting hub](/reference/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](/webhooks/events); the webhook security recipe is in [Webhook security](/webhooks/security).

## Next steps

* [Outbound fax workflow](/guides/fax-send-workflow) — the full send-side walkthrough with billing, receipts, and the on-success-charge model
* [Inbound fax receive-side workflow](/guides/fax-receive-workflow) — the receive-side ledger, events, and failure diagnostics
* [Inbound fax routing](/guides/inbound-fax-workflow) — per-DID routing to email, inbox, and webhooks
* [Fax channel](/channels/fax) — field reference, status lifecycle, error codes, and limits
* [Webhook security](/webhooks/security) — signature verification recipes
* [Message failed troubleshooting](/troubleshooting/message-undelivered-failed) — general failure diagnosis
* [Troubleshooting hub](/reference/troubleshooting-hub) — every channel's failure pages


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.