Messages

Set up email sending, read the delivery log, and understand why a message did or did not arrive.

#Messages

Every message your instance sends — password resets, verification emails, and anything your automations trigger — is recorded in Messages. The log tells you what was sent, what happened to it, and when something failed, why.

A fresh instance can usually send a little, and that is the confusing part. Setup gives your Worker Cloudflare API credentials, and those double as a minimal mail channel — but one that can only reach addresses you have already verified as Email Routing destinations on your Cloudflare account. So your own test lands, and everybody else gets silence. This page covers getting from that state to real delivery.

#Set up a provider

Go to Messages → Providers and choose Set up email. Until one is configured, a banner tells you what your instance can currently reach — either "nothing at all" or "only verified addresses", depending on how it was deployed.

Everything the platform sends goes through the providers you configure here. That includes password resets and sign-in links, not just automation mail.

#Which provider

Provider Best for Delivery confirmations
Resend (recommended) Starting from nothing — free tier, full automation Yes
SendGrid You already have an account. If a domain is authenticated there, setup skips DNS entirely Yes
Postmark You already have an account and want its deliverability No — see below

Two more, added since: Mailgun (bring your own key, US or EU region — existing domains are listed so you can skip DNS, and delivery confirmations work) and Your own mailbox (see below, no confirmations).

#Sending from your own mailbox

If your site sends a handful of emails a week, you do not need an email provider at all. Gmail, Fastmail, Zoho, iCloud and most work mail systems let you create an app password — a separate, revocable password for one application. Paste it in, and mail goes out through your ordinary mailbox.

Turn on two-factor authentication first (providers require it before they will issue an app password), create the password in your mail account's security settings, then pick your provider in setup and enter your address and that password. Use the app password, not your normal account password — that is the single most common mistake here, and it shows up as a rejected login.

What you give up: the from-address is the mailbox itself, there are no delivery confirmations, and daily volume is limited — a few hundred at most. EmuView's own daily quota (emails_per_day, default 200) sits below every consumer allowance, so a busy day is refused here with a reason in the log rather than silently throttled by your mail provider.

#Why some providers show no delivery confirmations

The log can only show Delivered, Bounced or Complained when the provider tells us — and we only accept that if we can prove the message came from them.

  • Postmark does not sign its webhooks at all. Its documented protection is basic auth in the endpoint URL, which our endpoint cannot verify. Accepting unsigned events would let anyone who learns your project id forge a bounce and permanently block any recipient they name.
  • Your own mailbox genuinely has nothing to report: SMTP hands the message over and the conversation ends there.

In both cases messages stop at Accepted, and the timeline says so plainly rather than leaving a gap you might read as "still in progress". Everything else works normally.

Mailgun does report deliveries. When you add its webhook, paste the Webhook Signing Key from your Mailgun dashboard — not your API key. The API key produces a signature that looks perfectly valid and never matches, which reads as a broken integration rather than the wrong credential.

Postmark also approves new accounts by hand before they may send to arbitrary recipients, so a brand-new Postmark account is not a quick path — it suits operators who already have one.

#Using more than one provider

You can configure several and put them in an order. Each one is tried in turn, and the first that can carry the message takes it. A provider is skipped when it has no key stored, when it is outside its sending hours, when it has used up the allowance you gave it, or when a recent failure has put it in a short cool-off.

That is what lets you say "use the free tier until it runs out, then fall back to the work mailbox":

Setting What it does
Priority Lower goes first. Providers always come before the deployment's own mail binding.
Send allowance A count and a window — 200 per 24 hours, say. Counted from the log, so it needs no daily reset.
Sending hours Optional days and an hour range in a timezone you choose, for a relay you only want used at work.

Two things worth knowing about how the order behaves:

  • Falling through costs nothing. Whether a provider can take the message is decided before anything is sent, from what your instance already knows — so skipping an exhausted provider does not mean a failed attempt first.
  • A failure does not silently retry elsewhere. If a provider accepts a message and then something goes wrong, handing that message to the next provider would send it twice — two working password-reset links from one request. So a second provider is only tried when the first refused outright, which proves it never took the message. Everything else waits and is retried later, by which time the failed provider has been stepped over anyway.

A provider that fails is put in a short cool-off so the next message routes around it without waiting for anything to be noticed. It rejoins the moment it succeeds again. A refused recipient does not do this — one bad address is not evidence that a provider is unwell.

#When nothing can send

If every provider is exhausted, asleep or unwell, sends fail with email_no_capacity — distinct from email_no_channel, which means nothing was ever set up. The two need looking in completely different places, which is why they are separate.

Administrators get an in-app notification when a password reset cannot go out. That notification does not need email to work, which is exactly why it can carry this news. The forgot-password page also stops promising a link it cannot deliver, and tells people to contact you instead.

#The guided setup

The wizard takes you from nothing to a delivered message in about five minutes. Each step ends in something proven rather than something submitted, and you can leave at any point: nothing about your progress is stored, so when you come back the page asks your provider where things stand and puts you exactly where you are.

  1. Connect — create a free Resend account, make an API key, paste it. We check the key with Resend before saving it, so a bad paste or a key without the right permissions is caught immediately rather than at 2am on somebody's password reset. A key that can send but cannot manage domains is fine — setup just switches the domain step to manual.
  2. Send yourself a test — before any DNS work. Resend can send to the address that owns the account straight away, so you see a real email arrive within the first minute.
  3. Choose a sending domain — we suggest send. plus your own domain. A subdomain is the convention: it keeps your sending reputation separate from your main domain and leaves your normal email untouched.
  4. Publish DNS records — the records come from Resend, and we check them live against public DNS. Each one shows Found, Not yet, or Wrong value — that last one catches the trailing dot or mis-pasted value that would otherwise leave you waiting for a verification that will never come.
  5. Turn on delivery reports — paste the webhook signing secret so the log can show real outcomes.

You do not have to sit and wait for DNS. Close the page; the notification bell will tell you the moment your domain verifies and mail goes live for everyone.

#Doing it by hand

The provider card's Edit settings dialog remains for anyone who would rather paste values directly — API key, sending address, webhook secret. Test this provider on the card pushes a real message through the whole pipeline and drops you on its delivery timeline, because a configured provider and a working one are different claims.

That test is pinned to the provider whose card you clicked: it will not quietly fall through to a healthy one behind it, so a success means that provider works. Any provider can be tested, not just the first in the order.

There is a second test send, under Settings → Email, and it answers a different question: it uses whatever the order would choose, which is what a password reset will do. Use the card test to check one provider, and that one to check that account recovery works.

Seeing the log and setting up providers are separate grants: reading requires the system/messages read permission, managing providers and suppressions requires manage.

#Delivery webhooks

Without webhooks a message stops at Accepted forever. You will know the provider took it, and nothing after that — not whether it arrived, not whether it bounced.

Copy the webhook URL from the bottom of the Providers page into your provider's dashboard, then paste the signing secret it gives you back into the provider settings.

That URL is safe to share with your provider. It names which project's signing secret must verify the payload; anything that does not verify is rejected before it is read, so knowing the URL grants nothing.

#Read the log

Messages → Log lists every message, newest first. Search by recipient or subject, filter by status or channel, and click any row to open its delivery timeline.

Platform mail — password resets, verification — carries a tag next to the recipient, so you can find it without guessing at the subject line.

#What the statuses mean

Status Meaning
Queued Accepted by EmuView, not yet sent.
Sending Being handed to the provider now.
Sent The provider accepted it. Not a confirmation that it arrived.
Delivered The recipient's mail server accepted it.
Bounced Rejected. The address is added to suppressions.
Complained Marked as spam. Also suppressed.
Failed Could not be sent. The timeline shows why.
Suppressed Never sent — the address is on the suppression list.

The distinction between Sent and Delivered is the one worth internalising. Sent means the message left your instance successfully. If your provider has no webhooks configured, it is the last thing you will ever learn about that message, and the timeline says so explicitly rather than leaving a gap you might read as "still in progress".

#Suppressions

Messages → Suppressions lists addresses that sends skip. The view shows the 200 most recent entries.

Hard bounces and spam complaints are added automatically. This is not bookkeeping — providers require it, and continuing to mail an address that rejected you is the fastest way to get your entire domain filtered, including the mail that would otherwise arrive.

#What a suppression blocks

Not all of them block the same things, and the difference matters:

Reason Blocks
Hard bounce Everything, password resets included — the mailbox does not exist, so nothing could arrive anyway.
Spam complaint Everything. Somebody who reported you should stop hearing from you.
Unsubscribe Ordinary mail only. Account recovery still works.
Blocked by hand Everything, unless you say otherwise.

The unsubscribe case is the one worth stating out loud: leaving a mailing list must not stop somebody signing back into their account. If it did, they would be locked out with no way to work out why — and neither would you.

You can release an address, and occasionally you should: a mailbox that was full or a domain that was misconfigured may be fine now. Releasing one that hard-bounced because it does not exist will simply bounce again and cost you reputation, so the confirmation says which case you are in.

#Rate limits on platform mail

Password reset is reachable without signing in, so it carries a per-recipient hourly ceiling. Without one, anyone could use the reset form to flood somebody's inbox from your domain.

The ceilings are per recipient per hour:

Message Limit
Password reset 3
Magic link 5
Email verification 3
Test message 10

A refused send is still written to the log, with the reason, carrying the error code email_rate_limited — distinct from a transport failure, because the remedies are opposite: one means wait, the other means go and fix your provider. A user who says "I requested three resets and got nothing" can be answered from the log rather than from theory.

#In-app notifications

The bell in the header shows notifications delivered inside the app. These need no provider, no domain and no DNS — they work the moment the instance is running. Automations will be able to target this channel once flows gain a send-message step; today it carries platform-generated notices only.

#Troubleshooting

Start with the log, not this list. Every refusal is written to Messages → Log with the reason on it, including the ones that never reached a provider. Filter to platform mail and read the timeline of the message that did not arrive — that is faster than working through symptoms, and it answers "I asked three times and got nothing" directly.

Nothing sends at all. Check Providers. With none configured you are limited to whatever your deployment's own mail channel can reach, which on most instances is only addresses verified on your Cloudflare account.

Sends fail with a domain error. The sending address must be on a domain verified with your provider. Until then, most providers only allow the account owner's own address.

Sends stop suddenly, mid-day. A provider has probably hit the allowance you set, or wandered outside its sending hours. GET /health lists every provider in order with the reason each unavailable one was skipped.

Everything stays on "Accepted". Delivery webhooks are not set up. Add the webhook URL and signing secret in Providers.

One person never receives anything. Check Suppressions first — a past bounce or complaint blocks later sends to that address, and this is by far the most common cause. An unsubscribe blocks ordinary mail but not password resets, so check which kind you are looking at.

A message sits at "Queued". Delivery is retried on the hourly maintenance tick, so a message that failed transiently waits for the next one. After several failed attempts it stops and is marked failed with the last reason attached, rather than being retried forever.

#Flow email

Flow email now goes out through the same providers, in the same order, as everything else — configuring a provider covers your automations too.

Its log is still separate, under Automate → Email, and that dashboard links across to Messages. Both logs count against the same daily allowance. Merging the two is still to come.