# Receive email as one clean webhook

Point an MX record at us or connect a Gmail or Outlook inbox. Every message reaches your code as signed JSON: who sent it, what they actually wrote, and which conversation it continues.

[Get started](/signup)
[Documentation](https://docs.aiinbx.com/guides/receiving)

Example: a customer's reply as raw MIME (received and DKIM headers, a multipart body in quoted-printable, the quoted order email, and a base64 PDF) becomes one signed email.received webhook with the thread id, the reply+ord\_8812 address it was sent to, the category human, stripped\_text: 'Approved — PO attached.', and the PDF attachment already prepared as Markdown text

Domains

## Every address is already an inbox

Publish one MX record and any address on the domain receives. Put an order id in the address and it comes back with the reply.

[Domains](https://docs.aiinbx.com/guides/receiving#receiving-on-a-domain)

Mailboxes

## Or read an inbox that already exists

When the address lives in Gmail or Outlook, connect it over OAuth. No DNS changes, and its mail arrives as the same webhook.

[Mailboxes](/features/mailboxes)

Reply text

## Answers found inside the quote

People reply between your lines. Each message is cut into what was written, what was quoted and the signature, and stripped\_text keeps each answer under its question.

[Reply text](https://docs.aiinbx.com/guides/receiving#what-was-written)

Example: Grace replies 'Approved, two answers below.' and types her two answers (Rotterdam, dock 4; invoice Harbourly BV) under our two quoted questions. A parser that cuts at the first quote marker keeps only 'Approved, two answers below.' stripped\_text keeps both answers under the questions they answer and drops the attribution line and the rest of the quoted order email. Her signature stays, because it is her first mail on the thread.

Categories

## A person, or a machine answering for one

Bounces, out-of-office replies and newsletters are labelled before they reach you, so your code can tell an autoresponder from a customer.

[Categories](https://docs.aiinbx.com/guides/receiving#categories)

Attachments

## The PDF, already text

Attachments come with a download URL, and PDFs, documents and sheets with their content as Markdown. No parser to host.

[Attachments](/features/attachments)

Example: Quote-4471.pdf, a supplier's quote, arrives as Markdown: sender and recipient addresses, the quote heading and date, the line items as a Markdown table (item, quantity, price), the net, VAT and total, and the delivery and payment terms.

Webhooks

## Built for the day your endpoint is down

Every request is signed. Anything but a 2xx is retried for 24 hours under the same event id, and a failed delivery can be replayed.

[Webhooks](https://docs.aiinbx.com/webhooks)

Build or buy

## What an inbound pipeline takes

Receiving the mail is the easy part. The work is everything between the socket and a line of text your code can act on, and each piece has an edge case that only shows up in production.

| Part | On your own | AI Inbx |
| --- | --- | --- |
| Accepting mail | Run an SMTP server, or wire SES or Mailgun routes to a queue | One MX record, every address live |
| Parsing | MIME trees, charsets, quoted-printable, base64, inline images | JSON with text, html and headers |
| Reply text | Quote patterns per mail client, and Outlook marks none | stripped\_text and segments |
| Conversations | In-Reply-To and References, when clients keep them | thread\_id, matched on the quote too |
| Autoresponders | Header heuristics, until a loop gets through | category on every message |
| Attachments | Storage, signed URLs, a PDF parser | Download URL and Markdown text |
| Gmail and Outlook | Two sync APIs, token refresh, history import | Your OAuth app, our sync, the same webhook |
| Delivery | Your own retries, dead letters and replay | Signed, retried for 24 hours, replayable |

Set up

## Receiving, in three calls

Step 1, domains.create: the records to publish.

```
const domain = await aiinbx.domains.create({
  name: "mail.yourapp.com",
})

domain.records // INBOUND is the MX record
```

Step 2, webhookEndpoints.create: the email rides along with the event.

```
await aiinbx.webhookEndpoints.create({
  url: "https://yourapp.com/webhooks/email",
  subscriptions: ["email.received"],
  payload: "full",
})
```

Step 3, your handler: people only, new words only.

```
const event = await verifyWebhookRequest(request, secret)

if (event.type === "email.received" && event.data.category === "human") {
  const email =
    event.data.email ?? (await aiinbx.emails.retrieve(event.data.email_id))
  await orders.handleReply(event.data.to[0], email.stripped_text)
}
```

## Questions

### What is an inbound email API?

A service that receives email for your domain and hands each message to your application over HTTP, usually as a webhook, instead of you running a mail server or polling an inbox over IMAP. AI Inbx also parses the message, cuts it down to what the sender wrote, threads it and classifies it before the request is sent.

### Do I need to run a mail server to receive email in my app?

No. Publish the MX record AI Inbx returns for your domain and mail to any address on it is accepted, parsed and posted to your webhook endpoint. There is no SMTP server, IMAP polling or MIME parsing on your side.

### Will the MX record take over my existing inboxes?

Yes, for the domain it is published on: the MX record routes all of that domain's mail to AI Inbx. If the domain already receives mail in Google Workspace or Microsoft 365, receive on a subdomain such as mail.yourapp.com, or connect the existing Gmail or Outlook mailbox over OAuth, which needs no DNS change at all.

### How do I get only the new text of a reply?

Read stripped\_text on the email. It drops the quoted conversation and a repeated signature and keeps answers typed inside the quote in place. segments gives the whole message cut into written, quoted and signature parts, in order.

### How do I route replies back to the right record in my app?

Every address on a receiving domain is live, so put your id in the address: send from reply+ord\_8812@mail.yourapp.com and read it back from data.to when the customer answers. For whole conversations, store the thread\_id and every later reply arrives with the same one.

### What happens if my webhook endpoint is down?

Anything other than a 2xx within ten seconds is retried on a backoff for up to 24 hours, with the same event id every time so you can deduplicate. Failed deliveries can be replayed, and GET /events lets a service catch up on everything it missed.

Keep reading

## More on building with email

-   [Email reply parserParse every email reply down to what the sender wrote: quoted text and repeated signatures removed, answers typed inside the quote kept where they were.ThreadingMailboxes](/solutions/email-reply-parser)
-   [Auto-reply detectionTell an out-of-office, a ticket acknowledgement or a bounce from a real reply. Every inbound email arrives with a category, decided from headers and content.MailboxesSuppressions](/solutions/auto-reply-detection)
-   [SPF DKIM DMARC checkCheck a customer's SPF, DKIM and DMARC records over an API. Live lookups at their own nameservers name the record that is wrong and the change that fixes it.Wildcard domains](/solutions/spf-dkim-dmarc-check)

-   [ThreadingOne conversation, however it comes back.](/features/threading)
-   [AttachmentsPDFs, sheets and scans, already Markdown.](/features/attachments)
-   [MailboxesGmail and Outlook, same everything.](/features/mailboxes)
-   [Wildcard domainsOne domain, a subdomain per customer.](/features/wildcard-domains)

[All solutions](/solutions)

## Give your product an inbox. The hard parts are already handled.

[Get started](/signup)
