---
title: Receiving
description: Two ways mail reaches your code — an MX record on a domain, or a connected Gmail or Outlook mailbox — and what arrives when it does.
sidebar:
  icon: inbox
---

There are two ways inbound mail reaches your application. Both work equally well for a domain or account you run and for one belonging to a customer who connected it through your app — what differs is what the setup needs and how much it covers.

<Columns cols={2}>
  <Column>
    **A domain**

    Point an MX record at AI Inbx and *every* address on the domain becomes an
    inbox your code can read. Needs DNS access, and takes over the domain's
    mail.
  </Column>
  <Column>
    **A connected mailbox**

    An existing Gmail or Outlook account, authorized over OAuth and synced in.
    Needs no DNS, changes nothing about the account, and covers one address.
  </Column>
</Columns>

Both land in the same place: a thread, and an [`email.received`](/webhooks/events/email-received) webhook.

## Receiving on a domain

Add the `INBOUND` MX record from the domain's `records` array and verify the domain — that's the whole setup. Whose domain it is doesn't matter to the API; if it's a customer's, you show them the records and they publish them. See [Domains](/guides/domains) for the record set and how verification works.

Once the MX record resolves, mail to any address on the domain is accepted, parsed, threaded, and delivered to your [webhook endpoints](/webhooks).

:::tip[Plus addressing]
Every address on the domain is live, so you can encode routing into the local part — `reply+ord_8812@yourapp.com` — and read it back off `data.to` in the webhook. Nothing to register per address.
:::

## Receiving through a connected mailbox

When the address already has an inbox at Google or Microsoft — so pointing an MX record at AI Inbx would take mail away from it — connect the mailbox instead. AI Inbx creates a one-time authorization URL, whoever holds the account approves it, and their mail syncs in from then on:

```ts
const { url } = await aiinbx.mailboxes.connect({
  provider: "google",
  return_to: "https://app.example.com/settings/mailboxes",
  ref: "user_8812",
})

// Send the customer to `url`.
```

`ref` is your own identifier, echoed back on the [`mailbox.connected`](/webhooks/events/mailbox-connected) event so you know which of your users just finished. [Mailboxes](/guides/mailboxes) covers the full flow — including a hosted page and a plain connect link that need no server call at all — and bringing your own OAuth app so the consent screen carries your brand.

## What arrives

A received message is delivered as an `email.received` event whose `data` carries the routing you need without a follow-up fetch:

```json
{
  "id": "evt_...",
  "type": "email.received",
  "created_at": "2026-09-01T10:31:04Z",
  "space_id": null,
  "data": {
    "email_id": "eml_...",
    "thread_id": "thr_...",
    "domain_id": "dom_...",
    "mailbox_id": null,
    "from": "grace@example.com",
    "to": ["support@yourapp.com"],
    "subject": "Re: Quick question",
    "snippet": "Thursday at 10 works for me…",
    "category": "human",
    "verdicts": { "spam": "PASS", "spf": "PASS", "dkim": "PASS", "dmarc": "PASS" },
    "attachments": []
  }
}
```

Retrieve the full message — bodies, headers, attachments, delivery events — with `emails.retrieve(data.email_id)`, or fetch the whole conversation with `threads.retrieve(data.thread_id)`.

### What was written

A reply carries the whole conversation underneath it, and people often type their answers inside that quote. `stripped_text` is the part the sender actually wrote: the quoted conversation and the signature removed, and an answer typed into the quote kept under the line it answers.

```json
{
  "text": "Hi Lena, answers inline.\n\nOn Mon, 1 Sep 2026 at 10:44, Lena Bergmann <lena@bergmann-metall.de> wrote:\n> 1. How many units per month?\n\n500, ramping to 800 in Q4.\n\n> 2. Which finish?\n\nAnodised black.\n\nBest\nTom",
  "stripped_text": "Hi Lena, answers inline.\n\n> 1. How many units per month?\n500, ramping to 800 in Q4.\n\n> 2. Which finish?\nAnodised black.",
  "segments": [
    { "kind": "written", "text": "Hi Lena, answers inline." },
    { "kind": "quoted", "text": "On Mon, 1 Sep 2026 at 10:44, Lena Bergmann <lena@bergmann-metall.de> wrote:\n1. How many units per month?" },
    { "kind": "written", "text": "500, ramping to 800 in Q4." },
    { "kind": "quoted", "text": "2. Which finish?" },
    { "kind": "written", "text": "Anodised black." },
    { "kind": "signature", "text": "Best\nTom" }
  ]
}
```

The cut reads the client's own markup first, then the markers around the quote, and finally the message being answered: when that message is on the thread, every line that reappears from it is quote, whatever the client marked. That last reading is how answers typed into an Outlook quote — which carries no markers at all — are found. `segments` is the whole cut in order, for a view that folds the quote or a model that wants the context too.

A message that quotes something never on file — a forwarded conversation, a reply to a mail sent before the mailbox was connected — keeps its quote in `stripped_text`: nothing underneath has been seen, so it is the content. `segments` still marks it as quoted.

Give a model `stripped_text` for what was said and the thread for what was said before it.

### Categories

Every inbound message is classified, so an autoresponder doesn't get treated as a human answer. `category` is one of:

| Category | Meaning |
| --- | --- |
| `human` | Classified as a message written by a person. |
| `out_of_office` | An away or vacation autoreply. |
| `auto_reply` | Another kind of automatic response — ticket acknowledgements, "we got your message". |
| `bounce` | A delivery failure notification that arrived as mail. |
| `verification` | A confirmation or code email. |
| `transactional` | Receipts, invoices, account notices. |
| `notification` | Alerts and system mail. |
| `marketing` | Bulk or promotional mail. |
| `spam` | Judged unsolicited. |

Gate your agent on it:

```ts
if (event.type === "email.received" && event.data.category === "human") {
  await handleReply(event.data.thread_id)
}
```

This filter reduces automatic reply loops, but classification can be wrong. Also apply sender checks, conversation reply limits, and human handoff rules. See [Email agents](/integrations/agents#decide-whether-to-answer).

### Authentication verdicts

`verdicts` reports what SPF, DKIM, DMARC, and spam scanning concluded about the *sender*. Treat a `FAIL` as reason not to act on the content, particularly for anything that changes state.

## Threading

Replies are matched onto existing conversations from `In-Reply-To` and `References` first, then — when a client mangles or drops those headers — by comparing the quoted content against messages already on the thread. Subject alone is never enough to merge two conversations, so two unrelated messages that happen to share "Re: Invoice" stay apart.

The upshot for your code: trust `thread_id`. [Threads](/guides/threads) covers reading and replying.

## Routing which events go where

An endpoint subscribes to the event types it wants, and can further narrow by sender or recipient with routing rules — useful when one workspace serves several products:

```ts
await aiinbx.webhookEndpoints.create({
  url: "https://app.example.com/webhooks/support",
  subscriptions: ["email.received"],
  routing: [{ effect: "allow", field: "to", pattern: "support@yourapp.com" }],
})
```

See [Webhooks](/webhooks#routing) for how allow and block rules combine.

## Attachments on inbound mail

Inbound attachments are stored and exposed as metadata plus a short-lived download URL. PDFs, documents, and spreadsheets are additionally prepared into Markdown or text so a model can read them without you running a parser — see [Attachments](/guides/attachments).

## Next

**[Webhooks](/webhooks)**

Endpoints, signatures, retries, replays.

**[Threads](/guides/threads)**

Reading a conversation and replying to it.

**[Domains](/guides/domains)**

DNS records, verification, diagnostics.

**[Mailboxes](/guides/mailboxes)**

Gmail and Outlook, and your own OAuth app.
