Skip to main content

Close the email loop: inbound parse, Flow reply, ticket

The outbound quickstart ends at a delivered message, and the inbound-routing guide ends at a parsed webhook. This tutorial stitches them into the loop a support address actually needs: a customer writes in, Orbit parses the mail on a managed route, a Flow sends an acknowledgement within seconds, and the thread opens as a ticket in the inbox for an agent to pick up. You will:
  1. See the loop you are about to build
  2. Baseline the outbound send
  3. Provision a receiving domain
  4. Register the inbound route
  5. Handle the inbound event with a Flow
  6. Read the thread as a ticket in the Inbox
  7. Debug the common failure modes

1. What you build

One work loop, end to end:
  • An outbound send establishes your verified sender and domain (two lines of cURL — the full sender verification is in Send your first transactional email).
  • An inbound route rents an address the customer can write back to.
  • A Flow reacts to the inbound event and replies “we got your message” without an agent touching anything.
  • The same inbound mail opens a ticket in the Inbox, so an agent owns the follow-up.

2. Outbound baseline

Confirm the sender exists and mail goes out — this is the whole outbound side of the loop:
A 202 with data.succeeded non-empty is the signal to move on. Work through Send your first transactional email first if any of domain verification, sender registration, or template rendering is still open — the inbound steps below assume that path is green.

3. Provision the receiving domain

Pick a dedicated subdomain (for example inbound.your-domain.com) so MX routing for inbound parse does not compete with your corporate mail, and point its MX records at Orbit’s ingress — the dashboard shows the hostname under Email → Inbound routes → Add route. Any address at that domain becomes a candidate for a route once DNS propagates. This is the same flow as step 1 of Inbound email and SMS routing.

4. Register the inbound route

Register the destination pattern and webhook URL once:
The 201 response carries the route’s signingSecret exactly once — store it now, because list and get responses mask it:
Verify X-Orbit-Signature (sha256=<hex> HMAC over the raw request body) on every forward — the full receiver pattern is in Build a durable webhook consumer. A lightweight receiver that verifies and returns 2xx quickly is all this step needs.

5. Handle the inbound event with a Flow

Inbound email route rules have no reply path themselves — the constraint is stated verbatim in the inbound-routing guide, and a Flow is its designated resolve. Subscribe to the inbound message.received event and compose the acknowledgement:
The channel: "email" filter prevents the ack Flow from answering SMS and WhatsApp inbound too. This minimal form — event trigger, one send node — is the whole loop; the Flows overview covers branches, delays, and richer orchestration.

6. The thread lands in the Inbox

Every matched inbound email opens (or threads onto) an inbox ticket in parallel with the webhook forward — your Flow acknowledgement fires off the pipeline while an agent picks up the assigned work under Inbox. The ticket fields and the receive → reply pattern at the API level are in Send & Receive Messages. Keep the Flow reply short and factual; the agent reads the ticket and carries the conversation from there.

7. Common failures

  • Route never fires. recipientPattern is matched against the SMTP envelope recipient first, then the To and Cc headers — but only when one of them names the address you registered. BCC-only mail, and mail whose envelope was rewritten upstream, both miss. Log the recipient your sending path actually used and match that — full matching rules are in Inbound email and SMS routing.
  • Signature mismatch. X-Orbit-Signature is HMAC over the raw request body — verifying a re-serialized JSON body always fails. Read the body as bytes before any framework middleware parses it; the pattern is in Build a durable webhook consumer.
  • Duplicate route rejected. Registering the same recipientPattern twice returns 409 CONFLICT on the second call — update the existing route with PATCH instead of re-creating it.
  • Flow fires for every channel. The filters.channel field scopes the trigger — omit it and your ack reply answers SMS and WhatsApp inbound too.
  • Ticket never opens on a broken webhook. The ticket pipeline runs in parallel with webhook forward — but only if a route actually matched. If GET /email/inbound-routes shows rising failureCount, the match may still be firing; if lastFailureAt is empty and no ticket opens, re-check the pattern from the bullets above.