> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

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

> Stitch the full email round trip — send outbound, provision an inbound route, parse the customer's reply, let a Flow acknowledge it, and hand the thread to an agent in the inbox.

# 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](#1-what-you-build)
2. [Baseline the outbound send](#2-outbound-baseline)
3. [Provision a receiving domain](#3-provision-the-receiving-domain)
4. [Register the inbound route](#4-register-the-inbound-route)
5. [Handle the inbound event with a Flow](#5-handle-the-inbound-event-with-a-flow)
6. [Read the thread as a ticket in the Inbox](#6-the-thread-lands-in-the-inbox)
7. [Debug the common failure modes](#7-common-failures)

## 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](/guides/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:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/email \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "self@your-domain.com",
    "subject": "Outbound baseline",
    "html": "<p>Loop step 1.</p>"
  }'
```

A **202** with `data.succeeded` non-empty is the signal to move on. Work
through [Send your first transactional email](/guides/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](/guides/inbound-email-parse).

## 4. Register the inbound route

Register the destination pattern and webhook URL once:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/email/inbound-routes \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "recipientPattern": "support@inbound.your-domain.com",
    "destinationUrl": "https://api.your-app.com/hooks/orbit-inbound",
    "includeRawMime": false,
    "description": "Support loop → ack Flow + inbox ticket"
  }'
```

The **201** response carries the route's `signingSecret` exactly once —
store it now, because list and get responses mask it:

```json theme={null}
{
  "data": {
    "id": "einr_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
    "recipientPattern": "support@inbound.your-domain.com",
    "destinationUrl": "https://api.your-app.com/hooks/orbit-inbound",
    "signingSecret": "einrsk_c0ffee…",
    "active": true
  }
}
```

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](/guides/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:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/flows \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Support auto-ack",
    "trigger_type": "event",
    "definition": {
      "trigger": {
        "type": "event",
        "event": "message.received",
        "filters": { "channel": "email" }
      },
      "nodes": [
        {
          "id": "ack",
          "type": "sendEmail",
          "data": {
            "to": "{{from}}",
            "subject": "Re: {{subject}}",
            "body": "Thanks — ticket {{ticket_id}} opened. An agent will pick this up shortly."
          }
        }
      ],
      "edges": []
    }
  }'
```

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](/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](/guides/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](/guides/inbound-email-parse).
* **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](/guides/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.
