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

> ## Agent Instructions
> Every Lath operation is POST https://platform.trylath.com/<operation name with dots replaced by slashes>, with a JSON body and `Authorization: Bearer <key>`. `email.send` is POST /email/send.
> Branch on `error.code`, never on `error.message`. Every refusal also carries `error.fix`, which names the next step.
> Send an `Idempotency-Key` header on any operation that is not retry-safe, so a retry cannot run it twice.
> A `lath_test_` key emails only the account's own members and sends no SMS; a `lath_live_` key reaches real recipients and is billed.
> The OpenAPI document, generated from the same registry as the routes, is at https://platform.trylath.com/openapi.json.

# Inbound email

> Receive mail on a domain you already send from. One MX record, every address at that domain, and the messages arrive as ordinary API objects and webhook events.

## Turn it on for a verified domain

`email.domain.receiving.set` with `enabled: true` adds an MX record to the domain's record list. The domain has to be verified first — receiving on an unproved domain is refused with `domain_not_verified`.

**The record is on the apex, not a subdomain, and MX is not additive.** Publishing it makes Lath the destination for every message sent to that domain, which is why this is a separate opt-in and not part of verification: turned on for a domain that already receives company mail, it takes that mail over.

Turning receiving off removes the record from the list Lath gives you, but your DNS is yours — take the record down at your host too, or mail keeps arriving at a door that now refuses it.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://platform.trylath.com/email/domain/receiving/set \
    -H "Authorization: Bearer lath_live_..." \
    -H "Content-Type: application/json" \
    -d '{"domainId":"...","enabled":true}'
  ```

  ```ts TypeScript theme={null}
  import { createLath } from "@trylath/sdk";

  const lath = createLath({ key: process.env.LATH_API_KEY });

  const { result } = await lath.email.domain.receiving.set({ domainId: "...", enabled: true });
  ```

  ```bash CLI theme={null}
  lath email domain receiving set --domainId ... --enabled true
  ```
</CodeGroup>

## Every address, one mailbox

Receiving is a catch-all for the whole domain. Anything sent to any address there arrives; there are no per-address rules, aliases or forwarding, so routing by recipient is something your own code does after the fact.

The address used for routing is the envelope recipient the sending server gave, not the `To` header — which is how a message you were blind-copied on still arrives, and still tells you it was addressed to you.

Inbound mail always lands in the **live** environment. There is no test-mode inbound.

## Reading what arrived

`email.inbound.list` pages summaries newest first, filterable by domain, sender and recipient, each with a 280-character preview and an attachment count. `email.inbound.get` returns one whole message: text, HTML, recipients, headers, the `spf`, `dkim` and `dmarc` verdicts as the receiving edge reported them, and attachments inline as base64.

Those verdicts are recorded, not enforced. A message that fails DMARC is still delivered to you with the failure attached, because deciding what to do about it is your policy and not Lath's.

A message that arrives twice with the same id for the same recipient is stored once, and the duplicate raises no second event.

## Attachments and size

A whole message may be 25 MB, of which attachments may total 10 MB; over either is refused at the door with `inbound_too_large` or `inbound_attachments_too_large`, and an empty message with `inbound_empty`.

Attachments come back inside `email.inbound.get` as base64 rather than from a download URL — there is no separate attachment endpoint. For a large attachment that means the message read is large too, so list first and fetch the one you want.

## Deleting, and how long it is kept

`email.inbound.delete` redacts a message in place: the subject, both bodies, the attachments and the headers are cleared and the recipient list is reduced to the one address it arrived at. The row stays so the timeline still shows that something arrived and was deleted. It is idempotent, and says whether it had already been done.

Inbound mail is not swept on a retention schedule the way message logs are — it stays until you delete it, and it is erased when an account is closed.

`email.inbound.received` is the event to subscribe to. `email.inbound.refused` exists for mail that was turned away at the door, which is deliberately never stored.
