Email API

Debugging "Email Not Received" with Delivery Logs and SMTP Codes

A step-by-step method for the "I never got the email" ticket: reading OquMail delivery logs, what SMTP codes 250, 421, 450 and 550 mean, and what to fix.

"I never got the email" is the most common support ticket for any app that sends mail, and without evidence it turns into an argument. A delivery log ends the argument: it shows whether your API accepted the message, which mail server it was handed to, and what that server said. This guide walks through the log in order and tells you what each outcome means and what to do about it.

Quick answer

Find the message in the delivery log by recipient and time. If the status is delivered with a 250 response, the receiving server accepted it and the problem is on the recipient side (spam folder, filters, wrong address). If it is deferred, the receiver is asking you to wait and it will retry. If it is failed with a 5xx code, read the text of the response; it names the cause. If there is no log entry at all, your app never sent it, and the bug is in your code or queue.

Step 1: did your app send it?

Before opening any log, check your own records: the email_sends row, the job in the queue, the API response you stored. A 202 from the API means the message was accepted and queued; anything else means it was rejected before leaving, and the status code tells you why (400 malformed body, 401 bad key, 403 limit or suspension, 422 blocked by outbound rules, 429 too fast). If your app has no record, the trigger did not fire: the webhook was missed, the flag was off, the recipient was filtered by a staging guard. Most "not received" tickets end here.

Step 2: open the delivery log

In OquMail go to Admin > Delivery Logs. Each row is one delivery attempt with the recipient, the MX server it connected to, the SMTP response code, a status and a timestamp. Filter by date range and find the recipient. A message to one recipient normally has one row; several rows for the same recipient and time mean the platform retried after a deferral, which is normal. Rows that can be retried show a Retry button.

Step 3: read the SMTP response

  • 250 (delivered): the receiving server accepted the message. It is in their system. Tell the user to check spam, promotions and filters, and confirm the address is the one they are looking at.
  • 421 or 450 or 451 (deferred): temporary. "Try again later", greylisting, or rate limiting. The platform retries; most clear within minutes to an hour. If it persists for hours, the receiver is throttling your domain.
  • 452 (deferred): the receiver is out of resources or the message has too many recipients. Retry resolves it.
  • 550 5.1.1 (failed): no such user. The address is wrong. Ask the user to check for a typo; fix it in your database.
  • 550 5.7.1 (failed): policy rejection, often SPF or DMARC failure or a blocklist. Check your domain's authentication with the DNS check in the OquMail dashboard.
  • 552 (failed): the recipient mailbox is full or the message is too large. Nothing to fix on your side except waiting or slimming the message.
  • 553 or 554 (failed): the receiver refused the message outright, often for content or reputation. Read the text; it usually includes a URL with the receiver's reason.

Step 4: match the outcome to a fix

  1. Delivered but not seen: ask for a spam-folder check and a search by subject. Ask whether the address is on a corporate domain; company gateways quarantine mail without telling the user.
  2. Deferred for a long time: check the response text for a reason (greylisting says so; rate limits mention volume). Slow your sending pace and confirm SPF, DKIM and DMARC pass.
  3. Failed with 5.1.1: correct the address and resend with a new idempotency key. Stop sending to that address until it is fixed.
  4. Failed with 5.7.1: re-verify the domain records. A missing DKIM record after a DNS change is the usual cause.
  5. No entry: fix the trigger in your app. Check logs around the time, check the queue for stuck jobs, check feature flags and environment guards.

Reproducing with a test send

When the log is ambiguous, send a fresh message to the same address from a curl command and watch the log update. This separates "the platform cannot reach that mailbox" from "my app did not send".

curl -X POST https://api.oqumail.com/api/v1/emails \
  -H "Authorization: Bearer $OQUMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"support@yourdomain.com","to":"jane@example.com","subject":"Test message from Acme support","html":"<p>Testing delivery to this address. Please reply if you see this.</p>","text":"Testing delivery to this address. Please reply if you see this."}'

Habits that make debugging fast

  • Store the API message id and the timestamp of every send next to the record that caused it, so support can go from an order to the log in one step.
  • Include a short reference in the subject or footer ("ref 10482") that users can quote.
  • Ask users for the exact address they expect mail at; many have several and check the wrong one.
  • Keep a monitored support@ mailbox on the sending domain; users often reply to the missing email's predecessor with the complaint.
  • Check the delivery log for a pattern across users before assuming a single user's problem. Ten deferrals from the same receiver in an hour is a reputation issue, not ten typos.

Common questions

The log says delivered but Gmail has nothing, not even spam. Why?

Gmail filters and forwarding rules can archive or forward without a trace, and Google Workspace admins can quarantine messages. A 250 from Gmail means Gmail has it; the search should include "in:anywhere" and the user's filters should be checked.

How long is a message deferred before it fails?

Retries continue for a period after the first deferral, and the log shows each attempt. If a message is still deferred after several hours, treat it as a reputation or throttling issue and investigate the response text.

Can I retry a failed delivery from the dashboard?

Rows that are eligible show a Retry button in the delivery log. Use it after fixing the cause, for example after DNS records propagate. For a wrong address, fix the address and send a new message instead.

Free business email on your own domain

OquMail gives you up to 15 mailboxes on your domain — free — with guided SPF/DKIM/DMARC, webmail, IMAP/SMTP for any mail app, and a send API. Most teams are live in under fifteen minutes. Start at oqumail.com.

Get started free

Ready for business email on your domain?

Up to 15 free mailboxes, guided DNS, webmail, and a transactional API — start in minutes.

Create your free workspace