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. The SDK tabs below read the API key from ORBIT_API_KEY; cURL passes it as X-API-Key. Where a route sits below an SDK’s typed helpers, the tab uses that SDK’s escape hatch — orbit.request (Node.js), client.request (Python and Ruby), client.Request (Go), $client->request (PHP) — the same convention the Sandbox API cookbook uses for untyped routes, per the SDK sample coverage policy. 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. The inbound event the Flow’s {{from}} and {{subject}} templates read is the same envelope your destinationUrl receives on every forward:

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.