# Inbound email (https://coss.com/products/inbound-email)

> coss.com Inbound email receives mail at your domain or at an address coss.com gives each environment, and calls your webhook with every message, parsed.

## How it works

Mail arrives, coss.com stores and scans it, and your endpoint gets a signed webhook. Fetch what you need from the API.

```ts twoslash
import { Coss, verifyWebhook } from "coss";
const coss = new Coss();
declare const request: Request;
declare const secret: string;
declare const domainId: string;
// ---cut---
// Receive at your own subdomain (or use the environment's inbound address, no DNS needed).
const { data: domain } = await coss.domains.receiving.create(domainId, {
  hostname: "inbound.acme.com",
});

// In your webhook handler: fetch the message, then its attachments.
const event = await verifyWebhook(await request.text(), request.headers, secret);
if (event.type === "email.received" && !event.data.spam) {
  const id = event.data.received_email_id;
  const { data: email } = await coss.email.received.get(id);

  for (const attachment of email?.attachments ?? []) {
    const { data: bytes } = await coss.email.received.getAttachment(id, attachment.id);
  }
}
```

The [API reference](/docs/api-reference/receiving/get-received-email) lists every field of a
received email.

## Built for parsing mail

Everything a support desk, a reply-by-email feature or an agent needs from a message, without running a mail server.

### An address per environment
Every environment has its own domain under coss.com's inbound domain, such as `k3j9x2m7qp4l.inbound.coss.com`. Mail to any address at it is received, with no DNS setup. `coss.email.received.address()` returns it.

### Your own domain
Set up [receiving](/docs/email/receiving#addresses) on one of your verified domains, at the domain itself or a subdomain such as `inbound.acme.com`, and publish one MX record. The environment of the key that set it up receives the mail.

### Routed by envelope
Messages are routed by their envelope recipients, Bcc'd addresses included. When one message reaches several environments, each gets its own copy that names only its own recipients.

### Verdicts on every message
Each message is scanned for spam and viruses and checked for SPF, DKIM and DMARC. Spam and authentication failures are flagged for your handler to decide; a message with a virus is blocked and its content never stored.

### Bodies, attachments, raw
Headers, `text` and `html`, attachments by id, and `getRaw()` for the whole message as `message/rfc822`. Attachments are always served as `application/octet-stream`, whatever they claim to be.

### A webhook per message
`email.received` names the sender, recipients, subject and spam verdict, never the content. Signed like every coss.com [webhook](/docs/webhooks), retried until your endpoint takes it, and forwarded to localhost by `coss listen`.

Received mail can be deleted at once with `coss.email.received.delete()`, content included. For
email addresses that keep conversations as threads and reply from the same address, see
[Inboxes](/products/inboxes).

## Specifications

The limits on received mail. The docs list each one in context.

- Message size: 40 MB
- Text and HTML kept: 2 MB each
- Headers kept: 64 KB
- Raw message: kept whole
- Daily inbound limit: 10,000 emails per organization
- Unroutable mail: dropped, never bounced
- Retention: 30 days
- Sandbox environments: receive real mail

## Testing

Try every verdict without sending a real email.

In a sandbox environment, **Send a test email** on the dashboard's **Received emails** page receives
a message without sending one, free. Put `[coss-test:spam]`, `[coss-test:virus]`,
`[coss-test:spf-fail]`, `[coss-test:dkim-fail]` or `[coss-test:dmarc-fail]` in its subject to try
each verdict, and use `coss listen` to forward the webhook to your local handler.

## Pricing

Received email is billed per message and environment it is stored in. Spam and viruses are free.

| Product | Rate | |
| --- | --- | --- |
| Received email | $0.40 per 1,000 emails for the first 1,000,000; $0.30 per 1,000 emails from 1,000,001 to 10,000,000; $0.22 per 1,000 emails beyond 10,000,000 | Mail flagged as spam or carrying a virus is free |

Free includes 3,000 received emails a month. See [pricing](/pricing) for the plans.

## Frequently asked questions

### What is an inbound email API?

A service that receives email for you and hands each message to your code. coss.com accepts mail for your addresses, scans it, stores its headers, bodies and attachments, and sends an email.received webhook so your app can fetch and act on the message.

### Do I need to change DNS to receive email?

No. Every environment has its own domain under coss.com's inbound domain, and mail to any address at it is received straight away. To receive at your own domain or a subdomain such as inbound.example.com, publish the MX record coss.com lists for it.

### How do I get the content of a received email?

The email.received webhook carries the sender, recipients, subject and spam verdict, never the content. Fetch the message with coss.email.received.get() for its headers and bodies, getAttachment() for each attachment, and getRaw() for the whole message as received.

### Does coss.com filter spam and viruses on inbound mail?

Yes. Every message is scanned for spam and viruses and checked for SPF, DKIM and DMARC. Spam is still delivered with spam set to true so your handler decides, while a message with a virus is blocked, its content never stored and no webhook sent.

### What happens to mail sent to an address nobody receives at?

It is dropped without a bounce, so coss.com never sends backscatter to forged senders. Mail over the organization's daily inbound limit is dropped too, and counted on the Received emails page.

### How much does receiving email cost?

Received email is billed per message and environment it is stored in, and the Free plan includes a monthly allowance. Mail flagged as spam or carrying a virus is free. The pricing page lists the current rates.
