All 17 Email Delivery sections

See every section

Being told when it breaks

Updated Email Delivery 1.7.0Tested with WordPress 7.1

Two things happen when a message is refused:

  • A notice appears in wp-admin, on every screen, until somebody looks at it. This is the real alert. On Email Delivery's own Dashboard it is the banner at the top instead.
  • An email goes to you as well, at most once a day.

When the message went to some of its addresses and was refused for the others, the notice, the banner, the email and a posted alert say An email did not reach every address. rather than that email is not going out, and the notice and the banner are amber rather than red. It is still an alarm, because the refused address will not be tried again until you send it from the log.

The two are counted apart, each at most once a day. A stale address on the morning's first order sends its own email, and an outage an hour later still sends Email is not going out straight away rather than the next day. The lesser news never takes the place of the greater either: while a message that nobody received, or a sign-in the daily check found refused, is standing from the past week, the notice and the banner go on saying that, in red, and a message that did not reach every address does not replace it. The other way round, a real failure or a refused sign-in replaces a standing did not reach every address.

The notice comes first deliberately. A plugin that only emails you when email has stopped working is a smoke alarm wired to the fuse that just blew — the email usually arrives, because a failure is normally one service refusing one address rather than the whole machine being off the network, but it is the second copy and not the first. For a copy that does not need email at all, the same alert can also be posted to Slack, Discord or a web address of your own (below).

Alerts in Slack, Discord or anywhere else#

Settings → When it does not arrive → Also post an alert to Slack, Discord or a web address. Switch it on and paste the address to post to in Web address to post to. It must start with https://.

  • A Slack incoming webhook (https://hooks.slack.com/services/…) gets a plain message in the channel the webhook belongs to.
  • A Discord webhook (https://discord.com/api/webhooks/…, including the addresses Discord's PTB and Canary apps copy, and the older discordapp.com ones) gets a plain message in its channel, with every mention switched off, so nothing in the alert can ping anybody.
  • Any other address on the internet gets the details as JSON (below), for a monitoring tool, an automation service or a script of your own. Microsoft Teams is not formatted specially: a Teams address gets the same JSON.

Some addresses are not saved, because WordPress will never post to them, and the screen says which rule the address broke: one with a user name or password in it, a number or a local name (localhost, a name with no dot) instead of a host on the internet, a host with characters other than the letters a to z, digits, dots and hyphens (an international name has to be written in its xn-- form), or a port other than 80, 443 and 8080 (a self-hosted automation tool on port 5678, say). The rest of the form is saved either way.

The screen says which of the three it recognised the saved address as, and how the last alert went:

  • accepted;
  • refused, with what the address answered (for Slack, words such as no_service or invalid_payload);
  • it got no answer, with the error (a timeout, say);
  • WordPress would not post there: the host could not be found, or it leads to a private network. Nothing was sent.

Saving a different address, or forgetting it, clears that line, so it is always about the address saved now.

It is posted on the same alarms as the notice and the email, and nothing else: a message the mail server refused, a message given up on after its last attempt, and two bad results in a row from the daily sign-in check (refused, no sign-in offered, or could not connect), each said as what it was. At most once a day, counted on its own, so an alert email that has already gone today does not hold it back. A message that did not reach every address is counted apart from the rest, as the email is, so its post never holds back the post about an outage; when both are waiting, the outage is posted first. A test that works (see Sending a test) starts the day again, so the next failure is posted at once. It is posted from your site's own server, not from anybody's browser, and a failing checkout never waits for it: the post goes out in WordPress's scheduled tasks straight afterwards. On a site where a page cache answers every visit without loading WordPress and nothing else runs the scheduled tasks, it waits for the next visit that does, as the retries do. It is not tried again if the address refuses it, and it is not written to the log.

Send a test → Post a test alert posts a test alert straight away and says where it went, or what the address answered. It sends no email, and it does not count as the day's alert.

Include the address the message was for is off. Off, the alert quotes the mail server with every email address taken out of what it said, so a customer's address does not leave your site. Switched on, the alert names the address the refused message was for, and the privacy policy text WordPress suggests says so.

The address is stored scrambled, like the mail passwords, and is never shown again: the screen names its host only, for example hooks.slack.com. Where a refusing address repeats a piece of itself in its answer (its path, or a ?token= in it), that piece is taken out before the answer is kept. Slack and Discord treat a webhook address as a secret, because anybody holding it can post in that channel. It is left out of an exported settings file, and importing a file keeps the one this site already has. After a site is copied to a new host it may need entering again, and the screen says so. Forget the saved address removes it; a new address typed in the same save is kept instead.

For safety, an alert is not posted to an address on your server's own private network (only your own site's address is allowed), a redirect is not followed, and only the usual web ports (80, 443 and 8080) are used.

The JSON sent to any other address has these fields:

FieldWhat it holds
eventmessage_failed, message_given_up, signin_failed, mail_held or test. A message the mail server took for some of its addresses is message_failed too; its text says it did not reach every address. mail_held is posted once, when a copy of the site holds its first message
siteThe site's title, or its host when it has none
site_urlThe site's address
timeWhen it happened, in UTC, for example 2026-10-01T09:30:00Z
textThe alert in words, as Slack and Discord show it: in the site's language, or for Post a test alert in the language of whoever pressed it
saidWhat went wrong, in the words it came in: the mail server's reply, or the connection error when it could not be reached. For message_given_up, Email Delivery's own sentence. Empty for a test. Email addresses are taken out unless you include them, and a long one is cut at a word
testtrue for Post a test alert, otherwise false
connectionFor signin_failed only: main or backup
stateFor signin_failed only: refused, no_signin or no_connection
reasonFor mail_held only: moved (another address) or environment (WordPress calls it staging, development or local)
set_up_at, answers_atFor mail_held only: the address the site was set up at, and the one it answers at now
environmentFor mail_held only: what WordPress calls the site, such as staging
recipientOnly with Include the address the message was for on: whom the message was for

The message's subject and contents are never posted.

The daily sign-in check#

On a quiet site the first message to fail may be a customer's receipt. So once a day the plugin signs in to your mail service and straight out again, the way it would before sending a message, and sends nothing. If an app password was revoked, an API key deleted, or the service stopped accepting that kind of sign-in (Microsoft is switching it off by default for existing Microsoft 365 accounts at the end of December 2026), the next daily check finds it. A message sent in the hours between the change and that check can still fail first.

It is on for sites that install Email Delivery from this version on. A site that already had Email Delivery set up keeps working exactly as before, with the check off, and the Dashboard offers it under Worth doing next with a Switch it on button. You can also switch it on or off in Settings → When it does not arrive → Check the mail server sign-in every day. The offer stays until you do one or the other: pressing Use port after a test, or importing a settings file, leaves the check as it was and does not answer it for you.

It is set up exactly as a message is. Anything on your site that changes how WordPress sends mail (a phpmailer_init snippet that relaxes the certificate check for a mail server with a mismatched certificate, say) applies to the check as well, so the check does not fail where your messages go. The same goes the other way: a plugin that does something every time WordPress sets up a message, such as a mail logger or a signing plugin, sees the check too, once a day, and may note it with no message in it.

Each check ends one of four ways, and they are told apart because each has a different fix:

  • Accepted: the mail server let the site sign in.
  • Refused: the mail server answered the sign-in with a refusal. This is the password, the key or the kind of sign-in, and the fix is in Settings.
  • No sign-in offered: the site connected, and the mail server did not offer a way to sign in at all. Usually Encryption is set to None where the mail service offers a sign-in only after TLS (Gmail and Microsoft 365 on port 587 do this), or the web host sends mail through a server of its own instead of yours.
  • Could not connect: the site never got as far as signing in. Nothing answered, the web host blocked the port, the name did not resolve, or encryption would not start. A correct password does not help here; the mail service being down or the host blocking the connection is the usual cause.

One bad result on its own could be a network hiccup, so the plugin looks again ten minutes later. Only a second bad result in a row, on the same connection, raises the alarm: the same notice on every wp-admin screen, headed The mail server refused this site's sign-in., The mail server did not offer this site a sign-in. or This site could not connect to its mail server., with the mail server's own reply or what went wrong, and the same email at most once a day. The notice adds No message has failed yet. only when the log shows no message that did not go in the past week; with the log switched off it cannot know, so it does not say so. The email also says what happens meanwhile: with a backup connection set up, messages are tried on the backup; without one, they will not go out; and when it is the backup's own sign-in, the backup cannot take over.

The alarm goes away when a later check of that connection is accepted, when a test through that connection works (a test of the main connection does not take down an alarm about the backup), when you dismiss it, or when that connection no longer signs in or the check is switched off.

If a message has really failed in the past week and its alarm is still up, a sign-in alarm never replaces it: a message that did not go is the more urgent news. A good sign-in does not clear it either, because the mail server may still be refusing something else, such as the From address. A message that reached some of its addresses is not that: email was going out when it was sent, so a refused sign-in since takes its place, with its own email and post.

  • Send a test → Check the sign-in now runs it straight away on each connection that signs in, shows the conversation with the username and password taken out, and sends nothing. When a mail server does not answer, it waits out the Timeout in Settings for that connection, one connection after the other, before the page comes back.
  • The Dashboard has a Sign-in card saying when each connection was last checked and whether it was accepted, refused, offered no sign-in or could not connect, or that it has not been checked yet. A test that goes through a connection counts as an accepted sign-in there. Under could not connect it links to Send a test when the test would try other ports (see When the mail port is blocked).
  • The backup connection is checked as well when it is a mail service.

It does nothing when the site sends through the web server, because there is no sign-in to check. It checks the sign-in only: a mail service can still accept the sign-in and refuse a message, for example from a From address it does not know, and that shows up the usual way, when a message fails. The check runs on WordPress's scheduled tasks, so on a site with little traffic it can run later in the day than planned.

Still stuck? Ask the developers who wrote it. We aim to reply within one business day.