Email & outbound

Why did my app stop sending emails?

By Jake Luo · Published 2026年9月2日

Almost always the stop is at your email provider, not in your code: you crossed a plan's sending limit, or the provider paused the account after a volume spike, a bounce rate or a complaint rate crossed a threshold. Check the provider's own activity log before you touch anything, because your application log will happily record a success — a rejected send frequently comes back looking like one, and if nothing in your code reads the response body then nothing anywhere will tell you. The next most common cause is domain authentication that broke when somebody edited DNS.

Look at the provider's log first, not your own

The confusing part of this failure is that nothing looks broken. Your code ran, the send function returned, no exception was thrown, and your own logs show messages going out. That is because your application log records what your code decided to do, and the provider's log records what happened to the message. When the two disagree, the provider is right. Open its activity view and filter to the window where sending stopped: you will normally see either silence after a timestamp, which means your code stopped calling, or a run of rejections with a reason attached, which means it called and was refused.

Do that before you change code, rotate an API key or re-send anything. Each of those makes the diagnosis harder, and one of them makes the problem worse: a re-send burst against an account already flagged for volume is the standard way to turn a temporary pause into a permanent one.

The four things that actually stop the sending

In rough order of how often each one turns out to be the answer:

  • A plan limit you grew into Daily and monthly send caps are the most common cause and the least fair-feeling, because nothing about your system changed — your signups did. The limit gets crossed on a Tuesday that looks like every other Tuesday, and what gets dropped is whatever was queued last, which is usually your notification and lifecycle mail rather than the login emails sent earlier in the day.
  • A provider pause Providers stop accounts on sudden volume changes, on a hard-bounce rate over a threshold, and on spam complaints. This one usually does come with a warning email — sent to whichever address was on the account when it was created, which in a small team is often nobody's inbox any more.
  • Broken domain authentication SPF, DKIM and DMARC records live in DNS, so they break when DNS changes: a registrar migration, a record that replaced rather than appended, an expired verification, a rotated signing key. This one degrades before it stops — delivery fails at one mailbox provider first, then the bounce rate climbs, then the pause arrives.
  • Your own code stopped calling A missing environment variable in one environment, a queue worker that died quietly, a feature flag left off after a deploy, an SDK upgrade that changed a required field. If the provider's log shows silence rather than rejections, this is your branch, and the answer is in your deploy history rather than your provider's dashboard.

Read the body, not the status code

This is what turns a ten-minute incident into a three-day one, and we have made the mistake more than once in our own system. A rejected call does not reliably arrive as an error. It arrives as an HTTP 200 with the failure inside the response body, and code that checks only the status code records a success. We have hit that exact shape with several different vendors: one answers 200 with a false success flag in the body when a token is missing a permission; another accepted a social post, reported a validation failure in the body, and our system — reading only the envelope — recorded the post as published and told the customer so. Nothing had been posted.

The rule that came out of it is short, and it applies to email as much as to anything else: treat a send as successful only when the response body says the provider accepted it, log the provider's own reason string when it does not, and alert on that string rather than on your own summary of it. The status line describes the conversation with the API. The body describes the email.

The same discipline covers rate limiting. A provider answering 429 is telling you something useful and temporary, and code with no backoff converts that into permanent loss, because the message is dropped and never retried. We ran without that backoff for longer than we should have, and what eventually surfaced it was not an alert. It was somebody going to look.

Make the next one loud

The fix that matters is not raising the limit, it is noticing before you reach it — and the alert should be on the ratio, not the count. "We used 80% of today's allowance by 2pm" is actionable; "we sent 100 messages" stops meaning anything the moment the plan changes. Our own volume crossed a hundred messages a day as signups grew, against a free-tier ceiling of exactly a hundred: roughly 107 on the day we finally looked, after sitting at 99 of 100 earlier that week. No part of the system said a word about it. The upgrade was the easy half. The alert we still owed ourselves was the half that mattered, and the reason it was missing is that the cap had never been near before, so nobody had thought of it as a number that moves.

Two more things worth doing while you are in here. Keep a second way into the account for when an email carrying a login does not arrive, for the reasons in our page on why magic link logins fail. And separate the mail that carries a login or a receipt from the mail that carries a campaign — by sending domain at minimum, ideally by provider — so that a marketing send tripping a complaint threshold cannot take your password resets down with it. Our page on setting up lifecycle emails covers what belongs in each stream. Building AgentCeres — the AI Growth Officer at agentceres.com — taught us the priority the expensive way: the messages a customer actually asked for are the ones you protect first.

FAQ

How do I tell a sending limit from a spam problem?
Look at the shape and the timing. A limit stops delivery to everyone at once, at a specific moment, and shows up in the provider's log as rejections. A spam problem is gradual and uneven: mail is accepted, delivery is reported as successful, and it lands in the junk folder at one mailbox provider before another. If your provider says accepted and delivered, you do not have a sending problem, you have a placement problem — which is a different investigation, covered in our page on cold emails and spam filters.
Should I add a second email provider as a fallback?
Eventually, but it is rarely the first fix and it is not free. A second provider needs its own domain authentication and builds its own sending reputation, so a cold standby you have never sent from will deliver noticeably worse than your primary on the day you need it. Get monitoring and headroom in place first. If you do go multi-provider, the usual reason is separation rather than redundancy: one for mail people asked for, one for campaigns.
Do transactional and marketing emails share the same limit?
Often yes, even when the provider presents them as separate products, because the cap is usually on the account rather than the stream. That is exactly why a campaign can silently eat the allowance your password resets needed. Read your plan's wording rather than assuming, and if the caps really are shared, treat your marketing volume as a variable that has to leave room for the transactional floor.
The provider says the message was delivered, so why did nobody get it?
Delivered means the receiving mail server accepted it, and nothing after that point is visible to your provider. Between there and a human, the message can land in a spam folder, be quarantined by an organisation-wide filter, be silently routed by a rule, or be forwarded to an address that bounces. Ask the recipient to search their mail rather than check the inbox, and check whether the failures cluster at one mailbox provider, which points at authentication or reputation rather than at your app.
How much sending headroom should I plan for?
Size the plan against your growth, not against today. If messages per day tracks signups, and signups are the thing you are actively trying to increase, then a cap you are at 80% of is a cap you have already decided to cross. Work out what your volume looks like at three times your current signup rate, and treat the gap between that and your plan as the number your alert is really about.
Related questions
How do I set up lifecycle emails for my SaaS?Why do my magic link logins fail?How do I write cold emails that don't get marked as spam?

Want this done for you?

AgentCeres is a managed AI marketing team — specialists draft the work, you approve what ships. 14-day free trial, from $39/month.

Start free trialMore answers