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:- See the loop you are about to build
- Baseline the outbound send
- Provision a receiving domain
- Register the inbound route
- Handle the inbound event with a Flow
- Read the thread as a ticket in the Inbox
- 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: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 exampleinbound.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:signingSecret exactly once —
store it now, because list and get responses mask it:
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 inboundmessage.received event and compose the
acknowledgement:
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.
recipientPatternis matched against the SMTP envelope recipient first, then theToandCcheaders — 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-Signatureis 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
recipientPatterntwice returns 409 CONFLICT on the second call — update the existing route withPATCHinstead of re-creating it. - Flow fires for every channel. The
filters.channelfield 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-routesshows risingfailureCount, the match may still be firing; iflastFailureAtis empty and no ticket opens, re-check the pattern from the bullets above.