GuidesReceiving
Receiving email
You do not poll for mail. A webhook fires, you read the message it names, and you delete it when you are done. This page is that loop — and the Inbox, where the same mail waits to be answered.
The loop
Subscribe a webhook to email.received, and everything else follows from the payload it delivers.
const api = (path, init = {}) =>
fetch(`https://api.rasket.com${path}`, {
...init,
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
// 1. check the signature, as with every Rasket event (see the Webhooks reference)
const event = verifySignature(rawBody, headers, process.env.RASKET_WEBHOOK_SECRET);
if (event.type !== "email.received") return;
// 2. route on what the payload already carries — no call needed
const queue = event.data.received_for.includes("billing@inbound.acme.example")
? "billing"
: "support";
// 3. read the parts the payload deliberately leaves out
const id = event.data.email_id;
const message = await (await api(`/emails/receiving/${id}`)).json();
const files = await (await api(`/emails/receiving/${id}/attachments`)).json();
// 4. each part carries a fresh signed link, good for fifteen minutes
for (const file of files.data) {
if (file.download_url === undefined) continue; // a part we would not store
await archive(file.filename, await fetch(file.download_url));
}
// 5. give the storage back
await api(`/emails/receiving/${id}`, { method: "DELETE" });Your Inbox
The same mail is waiting for you in your Inbox, the mail app behind the envelope in the dashboard’s top bar. Messages that answer each other are one conversation: we thread on the In-Reply-To and References headers a mail client sets, and never on the subject line, so two strangers who both write "Invoice" do not land in the same place. A message we accepted but would not store is threaded too, marked with the reason.
A conversation holds both halves — what arrived and what you sent back — and lives as long as the mail it is made of. Delete the last message and the conversation goes with it.
Only the screen was renamed. The API path /emails/receiving is unchanged, and so is every field on it.
Replying
You reply from the same name on your sending domain. A message to support@inbound.acme.example is answered as support@acme.example — the address the person already knows, and the one their mail client can line up with your DNS.
So replying needs a domain you have verified for sending. Your managed receiving address is ours rather than yours, and its reputation is shared, so it receives and never sends. A team that has verified nothing sees the composer switched off and Add a domain to reply in its place; the Domains reference is the path out of that.
| Mail arrived at | Your reply goes out as | Their answer comes back to |
|---|---|---|
support@acme.example, receiving at the domain itself | Acme Support <support@acme.example> | The same address. There is nothing separate to show you. |
support@inbound.acme.example, receiving at a label | Acme Support <support@acme.example> | support@inbound.acme.example |
| Your managed receiving address | support@ one of your verified sending domains | That domain's own receiving address, if it has one. |
The From picker offers any name you have saved at any of your verified sending domains, and nothing else — never an unverified domain, and never the managed address. When the address you pick answers somewhere else, the composer says so underneath it — Replies will go to support@acme.example — so you can answer as sam@acme.example and still have the next message land in support. A reply-to must be an address on a host you receive on; anything else is refused, because a reply-to nobody can answer is worse than none.
- One
Re:, never a stack of them, and we set the headers that keep your answer inside the conversation on their side as well. - Reply, reply to everyone, forward, or start something new. All four are in the inbox app — see Inbox.
- A reply can carry files. Up to ten of them, alongside your text, your mailbox's signature if it has one, and the message you are answering, quoted.
If the domain you reply from does not receive mail and we have no arrival address to fall back on, the reply goes out with no separate replies-go-to at all — so the answer is sent to a domain we do not receive on, and you never see it. Turn receiving on for that domain.
Why the payload is small
The event carries the envelope and the attachment list, and nothing else. That is a decision rather than an omission, and each half of it has a reason worth knowing:
- No bodies. A webhook is retried until your endpoint accepts it, ten attempts over about sixteen hours. A body that was retried for hours is hours of duplicated content arriving at your endpoint, for a message you may not even want.
- No download links. A link lives fifteen minutes. One minted at delivery would be dead by the time a retry succeeded, which is a worse failure than no link at all — it looks like it should work.
- Enough to route.
received_for,from,subjectand the attachment list are all there, so the common case needs no call.
Route on received_for — the addresses of yours the message was accepted for — and not on to, which is whatever the sender wrote in the header.
Before the loop can run
Be reachable. There are two ways, and they differ only in which address people write to; everything downstream is identical.
- Your own domain. Turn receiving on for a verified domain in an inbound-capable region and publish the one
MXrecord we hand back. It sits at the domain itself unless you choose otherwise, soanything@acme.examplelands with us — but thatMXtakes the domain's mail from whichever mailbox provider has it today, so setreceiving_hostto a label such asinboundif another provider already receives for it.anything@inbound.acme.examplethen lands in the same place. We check first: turning receiving on at a name whoseMXalready points at another mailbox provider is refused with409 receiving_mx_in_use, naming that provider, until you choose a subdomain or sendconfirm_replace_mx: true. - The address you already have. Every team is assigned a managed receiving address —
anything@<word>.<our host>— when it is created. Anything before the@reaches you, so you can tag threads without registering anything. It is shown while no domain of yours is verified for receiving; mail sent there still arrives after one is.
Receiving is available in two regions, United States (N. Virginia) and Ireland (Dublin). A verified domain outside them cannot receive at all, and its domain page says so in place of an address rather than offering a toggle that would do nothing.
An address on your domain that no mailbox claims reaches you only if you have marked one of your mailboxes as the catch-all. Mark one and everything unclaimed lands there, so a customer writing to a name you never set up is not silently lost. With none marked, or with unclaimed addresses switched off, that mail is recorded as not delivered and nothing is stored. Inbox has the whole of it, along with who on your team can read which address.
A message larger than 40 MB, attachments included, is refused before it reaches us, so there is no event and no record. We do not hold it, and we do not truncate it.
When the loop gets something odd
A message we accepted but would not store is still recorded, with its bodies null and no attachments, so that "nothing arrived" and "something arrived and we would not keep it" are never the same silence.
| You see | It means |
|---|---|
dropped_reason: "virus" | Scanned and refused. The envelope is kept, the content is not. |
dropped_reason: "storage_quota" | Your mailbox was full. Delete some mail, or add storage. |
dropped_reason: "daily_cap" | You had already received your plan's messages for the day. |
dropped_reason: "parse_failed" | We could not read the MIME structure. The raw source is still downloadable. |
dropped_reason: "no_mailbox" | No mailbox claims that address, and no catch-all is set to take it. |
dropped_reason: "team_blocked" | Your account is blocked, so mail is recorded but not stored until that is resolved. |
404 | You deleted the message, a dropped record aged out, or it was never this team's. They are deliberately indistinguishable. |
Storage, and getting it back
Your mail is kept until the mailbox is full. There is no retention window on a message you keep: it stays until you delete it. What bounds it is an allowance in bytes, and a daily message cap alongside it. With the mailbox full, the next message is recorded with a dropped_reason rather than stored — never an older one thrown away — so the loop keeps running and you find out from the payload.
| Plan | Inbox storage |
|---|---|
| Free | 3 GB |
| Pro | 8 GB |
| Scale | 15 GB |
| Enterprise | 40 GB |
Another 100 GB is $15 a month on any plan, the free one included. Add it with POST /billing/addons; it is billed on your subscription and the allowance applies at once.
Every open mailbox adds 5 GB on top, shared or personal, the ones your plan includes included — and 15 GB each once you have more mailboxes than your plan includes. Open another and the allowance goes up with it; close one and it goes back down. Extra addresses on a mailbox add nothing, because they are the same mailbox. Nothing to buy separately — it is the same single allowance the table above shows.
When your inbox reaches 80% of its storage, and again when it is full, the team's admins get an email. One per crossing, and never a second inside 30 days.
DELETE /emails/receiving/{email_id} is how the room comes back. It removes the stored objects as well as the record and returns the bytes to your allowance; there is no undo and no soft delete. A dropped record is the one thing that does age out, on your plan's data retention window — and like a deleted message it goes whole, so it answers 404 rather than coming back with an empty body.
Route API replies into your Inbox
An email you send over the API can be answered in the Inbox like any other conversation. Set reply_to to a name at an address you receive on, and the answer arrives somewhere we can see it.
| You receive at | Set on the send |
|---|---|
| the domain itself | Nothing. Your From address already receives. |
a label, such as inbound.acme.example | reply_to: "orders@inbound.acme.example" |
| only your managed receiving address | Nothing you can set: it is not an address you send from. |
Arriving is all reply_to does. What puts the answer on the right conversation is the In-Reply-To it carries, pointing back at the message you sent — so a reply_to at an address we do not receive on threads nothing, because the answer never reaches us at all.
What a reply costs
A reply is an email. It goes out through the pipeline POST /emails uses, on the transactional meter, and counts as one message on your plan: a thousand replies are a thousand emails.
| Limit | A reply |
|---|---|
| Your plan's monthly transactional allowance | Counts it. One message, like any other. |
| Your daily send cap | Counts it. |
| Your suppression list | Applies. A suppressed address is not written to. |
| The verified-domain rule | Applies. There is no second send path that skips it. |
| Ten requests a second, per team | Applies, as it does to a send you make yourself. |
| Sixty replies an hour, per member | Only here — a guard against a stuck composer, not a ration on your plan. |
| A daily ceiling per personal mailbox | Only for mail you send from your own address: 100 a day, or 500 once your account has a payment method. Nothing else counts it. |
There is one edge worth knowing before you meet it: a reply whose every recipient is on your suppression list is refused after the message has been counted. The composer checks suppression before it lets you type, so this only happens to an address suppressed in the seconds between.
What you have sent and what your plan allows are on the dashboard's Usage screen, beside every other send.
Mail you send from your own address rather than a shared one is not on that meter at all. You are paying for the seat, not for the message, so a personal mailbox draws on neither your monthly allowance nor your daily cap — it has a daily ceiling of its own: a hundred messages, or five hundred once your account has a payment method. Reaching it is a sign something is sending on your behalf, not a sign you should buy more.
| Limit | A message from your own mailbox |
|---|---|
| Your plan's monthly allowance | Does not count it. |
| Your daily send cap | Does not count it. |
| 100 a day per mailbox, 500 with a payment method on your account | The whole of what bounds it. |
| Your suppression list | Applies. A hard bounce or a complaint still stops a message. |
| Someone who unsubscribed | Still hears from you. Leaving a mailing list is not asking a person to stop writing back. |
Write to someone from your own address on a shared conversation and their answer still comes back to the shared mailbox, not to you. That is deliberate: the conversation is the team's, so whoever picks it up next finds the whole of it — including on the days you are away.
Where to go next
- The Receiving reference — all seven operations, with the shape of every response.
- Events — the
email.receivedpayload in full, beside every other event. - Domains — turning receiving on, and verifying the sending domain a reply needs.