← Back to all posts
2026-09-07 · MailAnvil Team

How to Debug Transactional Email Delivery — Track Every Recipient with MailAnvil Logs

You send a booking confirmation from your app. The customer says it never arrived. Now you're staring at a 202 response wondering what happened inside the mail pipeline.

Every email API returns 202 Accepted the moment your message is queued. That tells you nothing about delivery. What you actually need is the answer to one question: did this email reach the inbox, and if not, why?

That's the exact gap MailAnvil's logs fill. Every send is recorded, and every recipient gets an individual delivery status you can query by API. Here's how to use it.

The problem: 202 means "queued", not "delivered"

Think about the Indonesian apps that depend on this. GoFood and GrabFood send invoice emails after every order — if the email bounces, the customer still paid, but now support gets a ticket. Fintech apps (OVO, DANA, GoPay) send OTP and payment receipts where a missing email is a compliance problem. Traveloka and Tiket.com send itinerary confirmations that passengers need offline at the airport.

In all three cases, "the API accepted my request" is not the same as "the customer got the email." You need per-recipient status to know the difference.

Step 1 — send, then grab the email ID

A normal send returns 202 with the email ID:

curl -X POST https://api.mailanvil.com/v1/send \
  -H "Authorization: Bearer $MAILANVIL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Booking confirmed",
    "html": "<p>Your ticket is attached.</p>"
  }'

The response body includes the id (e.g. em_01J...). That ID is your handle into the delivery history.

Step 2 — list recent sends

curl "https://api.mailanvil.com/v1/emails?limit=20&offset=0" \
  -H "Authorization: Bearer $MAILANVIL_KEY"

Response shape:

{
  "emails": [
    {
      "id": "em_01J...",
      "from_address": "[email protected]",
      "subject": "Booking confirmed",
      "recipient_count": 1,
      "status": "delivered",
      "ses_message_id": "010001...",
      "error_code": null,
      "queued_at": "2026-09-07 02:14:31",
      "sent_at": "2026-09-07 02:14:33"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}

limit accepts 1–100 (default 20). Paginate with offset. This is your quick sanity check — did anything send recently, and did it leave the queue?

Step 3 — inspect per-recipient status

The email-level status is an aggregate. When you send to 50 recipients and one bounces, you need to know which one. That's GET /v1/emails/:id:

curl "https://api.mailanvil.com/v1/emails/em_01J..." \
  -H "Authorization: Bearer $MAILANVIL_KEY"
{
  "email": {
    "id": "em_01J...",
    "status": "partially_delivered",
    "recipients": [
      { "address": "[email protected]",  "status": "delivered", "bounce_type": null,       "event_at": "2026-09-07 02:14:33" },
      { "address": "[email protected]",    "status": "bounced",   "bounce_type": "Permanent", "event_at": "2026-09-07 02:14:34" }
    ]
  }
}

What each status means (and what to do)

Recipient status — the ground truth per address:

Status Meaning Action
pending Queued, not yet sent Wait, or check for a queue delay
delivered Accepted by the receiving mail server Done — that's success
deferred Temporary failure (mailbox full, throttling) Retry will happen automatically
bounced Permanent rejection Remove from your list — bounce_type says Permanent vs Transient
complained Recipient marked you as spam Stop emailing this address immediately

Email-level status rolls up recipients: delivered (all good), partially_delivered (some bounced but others landed), bounced, complained, or failed (deferred with no success).

The bounce_type field is the signal most people miss. Permanent (address doesn't exist, typo like gmial.com) means don't retry — you'll just hurt your sender reputation. Transient (mailbox full) means the retry is already handled.

Wire it to a webhook for real-time

Polling the logs is fine for debugging. For production, subscribe to delivery/bounce/complaint events via a webhook so you can react the moment a status flips — deactivate a dead address, trigger a support ticket, or surface a delivery delay in your admin panel.

The takeaway

202 is the beginning, not the end. MailAnvil logs give you the full journey — queued → sent → delivered (or bounced, with a reason) — per recipient, queried through two simple endpoints. No more "did it arrive?" guesswork for your OTP, invoices, and booking confirmations.

Try MailAnvil free at mailanvil.com — 500 emails/month, verified domain required.