> ## 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.

# Inbound fax: operate the receive-side loop end to end

> Run the receive side of fax as a first-class workflow — addressing, lifecycle events, destinations, failure diagnostics, carrier-connection routing, abuse screening, and the production checklist.

# Inbound fax receive-side workflow

The [Outbound fax workflow](/guides/fax-send-workflow) guide covers sending; the [Inbound fax routing](/guides/inbound-fax-workflow) 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:**

1. [Understand billing on the receive side — and why inbound costs read differently on the CFO dashboard](#1-billing-on-the-receive-side)
2. [Walk the receive-side ledger — the message row behind every inbound fax](#2-the-receive-side-ledger)
3. [Anchor the contract — inbound receipts are free, outbound bills on success](#3-the-contract-inbound-free-outbound-on-success)
4. [Consume the receive-side event — the `message.received` payload](#4-consume-the-receive-side-event)
5. [Diagnose receive-side failures — the receive vocabulary is not the outbound T.38 vocabulary](#5-receive-side-failure-diagnostics)
6. [Attach your own carrier connection when the platform default is wrong](#6-tenant-attached-carrier-connections)
7. [Run the production checklist](#7-production-checklist)

## Prerequisites

* A fax-capable number on your account (see [Buy numbers](/guides/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](/guides/fax-send-workflow#2-the-on-success-charge-model). 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 `delivered` event is the one that carries the charge; an inbound `message.received` event 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 a `messages` 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:

1. **Addressing decides persistence.** The provider's `fax.received` webhook posts the sender (`from`), the DID (`to`), the media URL, and a provider `fax_id`. Orbit resolves the tenant from the `to` DID. A DID no tenant owns drops at this step; a DID owned but unrouted persists with no downstream delivery.
2. **The dedup marker.** The provider `fax_id` is the idempotency key. A carrier retry of the same `fax.received` event creates exactly one message row — carrier retries are expected, not duplicates.
3. **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).

For per-DID config the surface is:

```
GET /api/v1/numbers/:id/fax-routing
PUT /api/v1/numbers/:id/fax-routing
```

with the same `{ enabled, forward_emails, deliver_to_inbox, archive_path }` shape the [routing guide](/guides/inbound-fax-workflow) 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:

| Direction | Event reached | Wallet billed? |
| - | - | - |
| Outbound | `message.delivered` | Yes — on the carrier's success confirmation, per the on-success-charge contract (per-send idempotency key `orbit-fax:<message-id>` blocks duplicate billing). |
| Outbound | `message.failed` | No — failed attempts are free. |
| Inbound | `message.received` | No — inbound receipts are never billed. |

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 as `message.received` with `channel: "fax"`, whether or not per-DID routing is enabled. The envelope:

```json theme={null}
{
  "id": "evt_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d",
  "type": "message.received",
  "created_at": "2026-09-25T12:00:00Z",
  "data": {
    "message_id": "msg_a1b2c3d4e5f6g7h8",
    "channel": "fax",
    "from": "+14155552671",
    "to": "+18005551234",
    "metadata": {
      "media_url": "https://media.carrier.example/fax.pdf",
      "normalized": {
        "event": "message.received",
        "kind": "inbound",
        "data": { "message_id": "msg_a1b2c3d4e5f6g7h8", "status": "received" }
      }
    }
  }
}
```

Three fields drive a receive-side consumer:

* `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.

Consumer discipline for this event: verify `X-Orbit-Signature`, reject deliveries older than five minutes, return `2xx` fast, and dedup on the event `id` — the recipe in [Webhook security](/webhooks/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 on `message.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 `401` and the fax is counted but never stored. Fix the carrier public key, or check for a proxy stripping signature headers.
* **Unresolvable DID** — the `to` number 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_emails` entry or more than 20 recipients fails with `422` at 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 returns `200` so 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 the `message.received` consumer if the rate guard is not enough.

When a receive-side symptom does not match the classes above (a delivered fax with no message record, a record with no media URL), the [Troubleshooting hub](/reference/troubleshooting-hub) and the [fax channel page](/channels/fax) error catalogue are the next hop.

## 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.

Set the org's `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.

<Note>
  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.
</Note>

## 7. Production checklist

Run through this list before a fax DID goes live:

1. **Routing enabled on the DID.** `enabled: true` with at least one destination (emails, inbox, or your webhook consumer). A bare DID persists only the message record.
2. **Destinations validated.** `forward_emails` addresses are deliverable — the `422` at write time catches typos; later per-recipient delivery failures are logged and never block other recipients.
3. **Junk screening understood.** The flood guard is rate-based. If you need content screening, plan a `message.received` consumer that scores senders or OCR before humans see the fax.
4. **Connection decision made.** If compliance or residency requires a dedicated Telnyx Fax application, set `fax_connection_id` before publishing the DID — traffic on the shared connection does not re-resolve later.
5. **Consumer discipline** (when you consume `message.received` yourself): signature verification, five-minute replay window, fast `2xx`, dedup on event `id`.
6. **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](/guides/inbound-fax-workflow) — the per-DID routing guide this one complements
* [Outbound fax workflow](/guides/fax-send-workflow) — the send-side companion: billing, receipts, and T.38 failure classes
* [Fax channel](/channels/fax) — payloads, limits, and the error catalogue
* [Webhook consumer](/guides/webhook-consumer) — reliable endpoint design
* [Webhook security](/webhooks/security) — signature verification recipes


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