Receive email at your own domain, over an API
You do not poll for mail. A webhook fires the moment a message arrives, you read the parts you want, and you give the storage back when you are done.
Handle one received message
// 1. verify the delivery, as with every Rasket eventconst event = verify(rawBody, headers, process.env.RASKET_WEBHOOK_SECRET);if (event.type !== "email.received") return;
// 2. route on what the payload already carries, with no call at allconst queue = event.data.received_for.some( (address) => address.startsWith("billing@"),) ? "billing" : "support";
// 3. read the parts the payload deliberately leaves outconst { body: message } = await rasket.emails.receiving.get(id);const { body: files } = await rasket.emails.receiving.attachments.list(id);
// 4. each part carries a fresh signed link, good for fifteen minutesfor (const file of files.data) { if (file.download_url === undefined) continue; await archive(file.filename, await fetch(file.download_url));}
// 5. give the storage backawait rasket.emails.receiving.remove(id);What the inbound email API gives you
Inbound mail as a resource your code reads, with the same signed events, the same keys and the same domains as everything you send.
One event carries enough to route
The email.received payload has the sender, the subject, the attachment list and received_for: the addresses of yours the message was accepted for. Route on that and the common case needs no call at all.
Attachments behind signed links
Ask for the attachments when you want the bytes. Each part comes back with a link good for fifteen minutes, and following it needs no API key: the signed link is the credential.
Route on the address we accepted
received_for is what the message was actually delivered to. The to header is whatever the sender wrote, and a sender can write anything, so routing reads the first and not the second.
A record even when we keep nothing
A message that fails the virus scan, or arrives over your storage quota or daily cap, is still recorded with a dropped_reason and no content, so nothing arriving and something we would not keep are never the same silence.
An API surface, not a mailbox
There is nothing to live in: no folders, no replies, no seat to buy. Mail arrives as an event your code handles and files where it belongs, and the dashboard lists what arrived so you can watch it while you build.
How receiving works, end to end
Three steps, and after the first one there is no setup left to do.
- 1
Be reachable
Turn receiving on for a verified domain and publish the one MX record we hand back. Every team also gets a managed receiving address, so you can build the handler before you touch DNS.
- 2
Subscribe a webhook to email.received
The event arrives signed, like every other one, and carries the envelope and the attachment list. Verify it, read received_for, and send the message wherever it belongs in your product.
- 3
Read what you need, then delete it
Fetch the message for its headers and bodies, fetch the attachments for links to the bytes, and delete it when you are done. Deleting is the only way to free inbound storage before retention does.
Choosing where your domain receives mail
The address people write to is yours to choose. Everything downstream is identical whichever you pick.
The address you already have
Every team is given a managed receiving address the first time it opens the Receiving screen. Anything before the @ reaches you, so you can tag threads and build the whole loop without registering a name or publishing a record.
A label on your own domain
Turn receiving on for a verified domain and it sits at inbound unless you say otherwise, so anything@inbound.acme.example lands with us. Any other label works just as well: pick the word your product already uses.
The domain itself
Set the receiving host to the domain and anything@acme.example reaches us. That takes the domain's mail away from whichever mailbox provider has it today, so choose it only for a domain whose every address should come to you here.
Receiving is available in two of the regions a domain can sit in. A verified domain outside them says so on its page, rather than offering a toggle that would do nothing.
Dropped messages, retention and inbound storage
What happens to a message we accept but would not keep, and how the space it uses comes back.
Received mail counts against your plan's inbound storage, and there is a daily message cap as well. Over either, a new message is recorded with a dropped_reason instead of being stored, so the loop keeps running and you learn about it from the payload.
| dropped_reason | What it means |
|---|---|
| virus | Scanned and refused. The envelope is kept, the content is not. |
| storage_quota | Your inbound storage was full. Delete some mail, or raise the cap. |
| daily_cap | You had already received your plan's messages for the day. |
| parse_failed | We could not read the structure. The raw source is still downloadable. |
Deleting a message is the only way to free inbound storage before retention does. It removes the stored bytes as well as the record and returns the space to your quota, and there is no undo. Retention takes away what you leave behind, whole: a message past it answers 404 rather than coming back with an empty body.
Related features and receiving documentation
The parts of the product the inbound loop leans on, and the reference pages behind every claim on this page.
- Webhooks: the signed delivery every message arrives on
- Domains: verify the name you want to receive at
- Email API: the sending half of the same domain
- Receiving guide: the loop, in full
- Receiving reference: every route and response
- Events: the email.received payload, field by field
Plans, quotas and what each one includes are on the pricing page.
Questions about receiving email
How do I start receiving mail?
Turn receiving on for a verified domain and publish the one MX record we hand back. Every team is also given a managed receiving address without publishing anything, so you can build and test the handler before you touch DNS.
Where does my domain receive mail?
Wherever you choose. It sits at inbound by default, so anything@inbound.acme.example lands with us. Any other label works, and so does the domain itself, for anything@acme.example. That last one takes the domain's mail from whichever mailbox provider has it today, so pick it only for a domain whose every address should reach us.
Does the webhook carry the message body?
No, and that is a decision rather than an omission. A webhook is retried until your endpoint accepts it, and a body that was retried for hours is that content arriving again and again for a message you may not even want. The event carries enough to route on, and you read the rest when you need it.
How long do attachment links last?
Fifteen minutes, and every read mints fresh ones. Following a link needs no Authorization header, so a browser can fetch the bytes without an API key ever reaching one. A part we would not store is listed with no link at all, so check for the field rather than assuming it.
What happens to a message with a virus, or one over my quota?
It is recorded without content and with a dropped_reason saying which: virus, storage_quota, daily_cap or parse_failed. The loop keeps running and you find out from the payload rather than from a gap in what arrived.
Is there a size limit on an inbound message?
Yes. A message over 150 KB, attachments included, is refused at the sending server, so there is no event and no record. We do not hold it and we do not truncate it.
Start receiving mail today
Take the managed address your team already has, point a webhook at your handler, and run the loop before you touch DNS.