Inbound email API
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.
What arrives on port 25.
Received: from mail-wm1-f41.google.com (209.85.128.41)DKIM-Signature: v=1; a=rsa-sha256; d=harbourly.com; s=g1; b=Kx9…Message-ID: <CAF3kq9x7Rw@mail.gmail.com>In-Reply-To: <0107019a6f2c8e14-5b1d9c3a@eu-central-1.amazonses.com>From: Grace Chen <grace@harbourly.com>To: reply+ord_8812@mail.yourapp.comSubject: Re: Order #8812 is ready to shipContent-Type: multipart/mixed; boundary="000000000000a1f2" --000000000000a1f2Content-Type: text/plain; charset="UTF-8"Content-Transfer-Encoding: quoted-printable Approved =E2=80=94 PO attached. On Mon, 21 Sep 2026, Yourapp <orders@mail.yourapp.com> wrote:> Order #8812 is ready to ship. Reply to approve. --000000000000a1f2Content-Type: application/pdf; name="PO-2231.pdf"Content-Transfer-Encoding: base64 JVBERi0xLjcKJeLjz9MKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZWdlcyAvS2lkcyBbMyAwIFJdIC9Db3VudCAxID4+CmVuZG9iagozIDAg
…and the rest of the PDF, in base64
What your code receives.
{ "type": "email.received", "data": { "thread_id": "thr_8f2c", "from": "grace@harbourly.com", "to": ["reply+ord_8812@mail.yourapp.com"], "category": "human", "email": { "stripped_text": "Approved — PO attached.", "attachments": [{ "filename": "PO-2231.pdf", "preparation": { "format": "markdown", "text": "# Purchase order PO-2231\n…" } }] } }}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- support@mail.yourapp.comSupport queue
- reply+ord_8812@mail.yourapp.comOrder #8812
- invoices@mail.yourapp.comBilling
- anything-at-all@mail.yourapp.comReceived too
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- mail.yourapp.comMX
- nora.berg@gmail.comOAuth
- jonas@northwind.ioOAuth
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 textApproved, two answers below. On Mon, 21 Sep 2026, Yourapp <orders@mail.yourapp.com> wrote: > Order #8812 is ready to ship. Two questions first:>> Should it go to the Rotterdam warehouse?Yes, Rotterdam. Dock 4. > Which entity do we invoice?Harbourly BV. PO attached. Grace ChenProcurement, Harbourly
Approved, two answers below.Both answers went with the quote.
Approved, two answers below. > Should it go to the Rotterdam warehouse?Yes, Rotterdam. Dock 4. > Which entity do we invoice?Harbourly BV. PO attached. Grace ChenProcurement, Harbourly
stripped_text: each answer under its question, and her signature, new to 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- bounce
Mail Delivery Subsystem
Undeliverable: Your order has shipped
- out of office
Tom Becker
Automatic reply: Re: Invoice 2231
- marketing
Freightly
New: live tracking for every pallet
- human
Grace Chen
Re: Order #8812 is ready to ship
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.
AttachmentsQuote-4471.pdf
Lind Metall GmbH
Industriestraße 14
44536 Lünen, Germany
Northwind GmbH
Attn: Nora Berg
Quote Q-4471
31 Aug 2026
| # | Item | Qty | Price |
|---|---|---|---|
| 1 | Sheet 3mm | 500 | €4.20 |
| 2 | Cutting | 500 | €0.35 |
- Net
- €2,275.00
- VAT 19%
- €432.25
- Total
- €2,707.25
Delivery: 10 working days from order.
Payment: 30 days net.
Quote-4471.pdf.md
LLM-ready<!-- Page 1 --> Lind Metall GmbHIndustriestraße 1444536 Lünen, Germany Northwind GmbHAttn: Nora Berg # Quote Q-4471 31 Aug 2026 | # | Item | Qty | Price || - | --------- | --- | ----- || 1 | Sheet 3mm | 500 | €4.20 || 2 | Cutting | 500 | €0.35 | Net €2,275.00VAT 19% €432.25Total €2,707.25 Delivery: 10 working days from order.Payment: 30 days net.
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- ~1 min50310:31:04
- ~5 mintimeout10:32:06
- 20010:37:11
The same event id on every attempt: deduplicate on it and a retry is never a second order.
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.
Set up
Receiving, in three calls
const domain = await aiinbx.domains.create({ name: "mail.yourapp.com",}) domain.records // INBOUND is the MX recordawait aiinbx.webhookEndpoints.create({ url: "https://yourapp.com/webhooks/email", subscriptions: ["email.received"], payload: "full",})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
- 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
- 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
- ThreadingOne conversation, however it comes back.
- AttachmentsPDFs, sheets and scans, already Markdown.
- MailboxesGmail and Outlook, same everything.
- Wildcard domainsOne domain, a subdomain per customer.