Skip to main content

One inbound email, end to end

Inbound email on Orbit has two reference guides: Inbound email and SMS routing documents the parse webhook anatomy and the DNS/console steps, and Route inbound support mail to the right team and ticket documents the route-to-ticket patterns. This guide is the missing piece between them: one concrete email, followed through every surface in order, so you leave knowing whether your setup needs a route, a webhook receiver, a Flow, or all three. You will:
  1. Pick your guide
  2. Meet the email you will follow
  3. Point the domain at Orbit
  4. Watch SMTP receive become a signed parse POST
  5. Verify the signature
  6. Read the message in the dashboard
  7. Send the reply
  8. Recognize the failure paths

1. Pick your guide

The question every new email customer asks is whether they need a route, a webhook, a Flow, or all three. The answer, in one paragraph: the route is the only required piece — no route match means no parse and no ticket. The webhook receiver is where the signed parse POST goes; it is optional if the dashboard alone is enough for your team, because the inbox ticket opens whether or not your endpoint answers. A Flow is optional automation on top — an auto-reply or a branch — and is only needed when something must happen without a human. This walkthrough wires all three surfaces minimally so you can see each one do its job.

2. Meet the email you will follow

Every example in this guide follows the same message. A customer, Maya, writes to your support mailbox with a DKIM-signed reply, one PDF attachment, and one inline image:
That raw MIME is the input to Orbit’s inbound pipeline. Everything after this section is what Orbit does with it, and what you see at each surface. The DKIM signature above is the sender’s — it proves the mail came from brightline.co. The signature you verify is the one Orbit adds to the webhook forward in step 5; the two solve different problems.

3. Point the domain at Orbit

Open Email → Inbound routes → Add route in the dashboard. The surface shows the ingress hostname your MX records must point at for the domain you register. At your DNS provider, set the MX record for the receiving domain (a dedicated domain or subdomain such as acme.example keeps inbound parse separate from your corporate mail):
Confirm propagation from your side before sending test mail:
While the MX does not resolve, no mail arrives at all — there is nothing to debug in Orbit yet, because the message never reached ingress. Once dig returns the hostname and the console shows the domain as verified, send Maya’s message from step 2 (or forward a real one to support@acme.example). Next, register the route that catches the mailbox. POST /email/inbound-routes takes the recipient pattern and your webhook URL:
The 201 response carries the route’s signingSecret exactly once — store it now, because list and get responses mask it. You use it in step 5.
recipientPattern accepts the exact address (used here), a catch-all (*@acme.example), or a bare local-part (support). Matching runs against the SMTP envelope recipient first, then the To and Cc headers, so BCC and forwarded mail route correctly. Registering the same pattern twice returns 409 CONFLICT. The full pattern reference lives in Inbound email and SMS routing.

4. Watch SMTP receive become a signed parse POST

Ingress accepts Maya’s message over SMTP, matches the route from step 3, and parses the raw MIME from step 2 into normalized fields. Your destinationUrl then receives a signed JSON POST:
Read it against the raw MIME:
  • text and html are the decoded body parts (either can be null); long bodies are shown trimmed here but arrive complete.
  • attachments is metadata only: filename, MIME type, decoded byte size, and the inline image’s Content-ID in contentId. Attachment bytes are never inlined into the JSON — set includeRawMime: true on the route when you need the full original message alongside the normalized fields.
  • headers carries every top-level header as a lowercased key map, including message-id and the sender’s dkim-signature.
  • matchedRecipient tells you which registered pattern caught the mail — useful when a catch-all serves several mailboxes.
The same parse that produced this POST also opens (or threads onto) an inbox ticket, in parallel with the forward. You will read that ticket in step 6; the two surfaces work side by side and neither depends on the other succeeding.

5. Verify the signature

The forward arrives with an X-Orbit-Signature header: sha256=<hex>, an HMAC over the raw request body keyed by the route’s signingSecret from step 3. Verify it before trusting anything in the body. A minimal Node receiver:
The parse handshake is the same shape in every language — Python, Go, Ruby, and PHP variants are in Verify webhook signatures. Two rules make the receiver honest: verify before reading the body, and return a 2xx promptly. Redirects are not followed, non-2xx responses (including your own 401s) count as forward failures, and the route’s failureCount / lastFailureAt on GET /email/inbound-routes tell you when forwards are failing.

6. Read the message in the dashboard

While your webhook was being forwarded, the same message opened an inbox ticket. Open the inbox and find the thread by subject or sender — for Maya’s mail, a ticket titled Invoice question — order #1234 from maya@brightline.co. The ticket carries the parsed body as its transcript and the sender’s address as the contact, so a second mail from Maya threads onto the same ticket instead of opening a new one. The attachment story in the dashboard is one step behind the webhook payload: each decoded part is scanned before it is archived to your media store. A clean part (Maya’s order-1234.pdf) appears in the message detail with a fresh download URL; the inline image is recorded by its Content-ID. A part the scan drops never reaches the store — the payload’s attachments entry still lists it (metadata only), and the message record notes the drop with finding codes under metadata.email_attachments_quarantined. The inbound attachment threat scan concept page covers the clean / suspicious / malicious verdicts; Operate the Inbox Tickets queue covers working the queue. The conversation also appears as a row under Maya’s contact on the Interactions page, which merges conversations and calls into one recency-ordered list — the place to look when you want the contact’s whole history rather than one ticket.

7. Send the reply

Two surfaces can answer Maya, and both keep the story coherent:
  • From the ticket. The inbox reply composer answers maya@brightline.co and threads the exchange onto the same ticket — the designated path for agent-handled mail, and the one that cannot accidentally loop back into your own parse domain.
  • From the API. POST /messages/email sends the outbound half. Send from the same verified sender address the conversation started with, and keep the subject threaded with Re: so Maya’s mail client files the reply under the original message:
from must resolve to a verified sender on your account, and at least one of html / text must be non-empty; the full field table is in the email channel reference. When Maya writes back, her reply matches the route from step 3 again and threads onto the existing ticket, closing the loop.
If you automate the reply with a Flow instead of a human or a direct API call, guard it: an unguarded send back at your own parse domain closes the loop on itself and re-triggers the Flow. The troubleshooting guide covers the reply-to / subject guards.

8. Recognize the failure paths

The four failures every new setup hits once, and how each one looks from your side: When none of the four explains it, work the inbound email troubleshooting guide top to bottom — it isolates DNS, route matching, normalization, and trigger issues one stage at a time, and lists exactly what to collect before escalating.

Next steps