Inbound email and SMS routing
Most of Orbit’s email and SMS surface is built for sending. This guide covers the other direction: what happens when a customer writes to you, and how you control where that inbound traffic goes. You will:- Configure inbound parse on a domain
- Understand the normalized inbound payload
- Attachment archival and quarantine
- Route inbound SMS with match rules
- Auto-reply with Flows
- Handle bounces and unsubscribes
Prerequisites
- An API key from Settings → API Keys (a
dv_live_sk_…live key — inbound routing configures real traffic). - For email: a domain you can point MX records at Orbit (for example, a dedicated subdomain such as
inbound.your-domain.com). - For SMS: an inbound-capable number on your account (purchased via the Numbers API or Dashboard → Numbers).
- A public HTTPS URL to receive webhook forwards.
Inbound route management endpoints require the owner or admin role. API keys created with the member role receive
403 on the CRUD calls below.1. Configure inbound parse on a domain
Inbound parse takes mail addressed to a domain or address you control, matches it against a route you register, parses the message into structured fields, and forwards the result as a signed JSON POST to your webhook URL.Point MX records
Point the receiving domain’s MX records at Orbit’s ingress (the dashboard shows the hostname when you open Email → Inbound routes → Add route). Once DNS propagates, any address at that domain (support@inbound.your-domain.com, billing@inbound.your-domain.com, …) is a candidate for a route.
Create a route
Register the destination pattern and webhook URL withPOST /email/inbound-routes:
201 carries — exactly once — the route’s signingSecret. Store it now; list and get responses mask secrets.
recipientPattern accepts three shapes:
Recipients are matched against the SMTP envelope recipient first, then the
To and Cc headers — BCC, aliased, and forwarded mail route correctly even when the To header omits your address. A duplicate pattern returns 409 CONFLICT. Routes are manageable from GET /email/inbound-routes (list) and DELETE /email/inbound-routes/:routeId.
2. The normalized inbound payload
Each matched message is POSTed to yourdestinationUrl as JSON, signed with the route’s secret:
textandhtmlare the decoded body parts (both can benull).attachmentsis metadata only — filename, MIME type, decoded byte size, and the inline-imageContent-ID. This declaration survives even when the attachment itself is quarantined and dropped by the threat scan (see attachment archival and quarantine below). Attachment bytes are never inlined into the JSON. SetincludeRawMime: trueon the route when you need the full original message.headerscarries every top-level header as a lowercased key map.
X-Orbit-Signature header (sha256=<hex> over the raw body, HMAC-keyed by the route’s signingSecret) before trusting a forward, as you would for any webhook. Only 2xx responses count as a successful forward — redirects are not followed — and the route’s failureCount / lastFailureAt (visible in GET /email/inbound-routes) track failures.
Every matched inbound email also opens (or threads onto) an inbox ticket in parallel with your webhook forward — the internal helpdesk surface works side by side with your developer integration. See the ticket fields in the receive step of the two-way messages guide.
3. Attachment archival and quarantine
Theattachments array in the payload above stays metadata-only — but what your recipient can open in the inbox depends on a second pipeline that runs after the webhook forward. Each decoded part is byte-scanned for threat signals, and the scan verdict decides whether the part is archived to your tenant’s media store (where the message-detail view renders it with a re-freshed download URL) or quarantined — dropped before it is ever uploaded or surfaced to an agent inbox. The inbound attachment threat scan concept page explains the full model; the two outcomes worth knowing here:
Quarantined parts are recorded on the message in a second metadata key, alongside the
email_attachments archive list. It carries finding codes and severities — never the raw payload:
4. Route inbound SMS with match rules
Inbound SMS routing is an ordered rule engine over each inbound (MO) message:POST /messages/inbound-routes registers a rule, and inbound messages are matched in ascending priority (lower number wins). When no rule matches, the message falls through to the default behaviour — the tenant-wide message.received event and inbox persistence. Rules are matched only on enabled entries.
Match types:
Target types:
Example: keyword → webhook
secret you set is used to sign the POST (X-Orbit-Signature: sha256=<hex>). Queue, inbox, and team targets produce no HTTP call — the assignment is published to the inbox layer directly. Each matched rule’s failureCount and lastFailureAt/lastMatchedAt are tracked and listed via GET /messages/inbound-routes (webhook secrets are masked in list responses). Update a rule with PATCH /messages/inbound-routes/:routeId; delete with DELETE /messages/inbound-routes/:routeId.
5. Auto-reply with Flows
For canned inbound replies, prefer theauto_reply target type on an inbound SMS rule — the decoupling keeps reply logic in a config rule instead of a network hop. For anything more orchestrated (look up an order, branch on the customer’s lifecycle stage, fan out to a channel other than the one they came in on), build a Flow that reacts to the inbound message event and sends through the channel of your choice — the Send & Receive Messages guide shows how the receive → reply loop looks at the API level, and Flows externalizes that loop into the builder.
Inbound email rules do not have a reply path themselves — use an email channel Flow on the inbound event to compose the response, or open a ticket (which your agents handle from the inbox).
6. Bounces and unsubscribes
Inbound workflows end in outbound mail, so the suppressions that matter are the ones on the outbound side:- Unsubscribe. Every outbound email carries a one-click unsubscribe link (the
List-Unsubscribeheader plus the body link). A recipient click lands at a signed URL, suppression is recorded idempotently against that recipient, and anemail.unsubscribedaudit entry is appended. Suppressed recipients are skipped at send time. The recipient is redirected to your configured page, or shown an acknowledgement. - Bounce handling. Hard bounces, spam complaints, and invalid addresses are tracked per message and surface with the standard email events. Suppress addresses proactively from the opt-out lists guide, and follow send-best-practices to keep deliverability healthy.
Troubleshooting: my recipient says an attachment is missing
Check the message’smetadata.email_attachments_quarantined before treating the send as a delivery failure. When a part is dropped by the threat scan, that key holds one entry per dropped part — the filename, the detected band, and machine-readable finding codes (for example double_extension, pe_executable, embedded_script). If it carries an entry for the missing part, the scan removed the attachment on purpose — this is not a delivery failure.
Next steps
- Send & Receive Messages — the two-way loop across all channels
- Inbound attachment threat scan — the clean / suspicious / malicious verdict model and the quarantine lifecycle
- Webhook consumer guide — verify forwards securely
- Email attachments — limits and MIME rules for outbound
- Opt-out lists — suppression management
- Flows overview — auto-reply and richer orchestration
- API Reference: inbound email routes, inbound SMS routes