All posts Engineering

The journey of an email: from API call to inbox

You POST /emails, get a 200 OK, and assume it went through. Fifteen minutes later: 'I never received my verification email.' Here's everything that happens between your API call and the recipient's inbox.

Erik Vlčák
Erik Vlčák
Customer Success Engineer
11 min read

You send a POST /emails request with a JSON body, receive a 200 OK, and assume everything went through. Fifteen minutes later, a support ticket arrives: "I never received my verification email."

The API accepted your request, and the email was sent. But "sent" and "arrived in someone's inbox" are two very different things, separated by DNS lookups, cryptographic signatures, reputation checks, and content filters that most developers never consider until something breaks. Here's what actually happens from your API call to the moment a recipient sees your message.

Step 1: Submission and validation

Your email begins as structured data: a from address, a to address, a subject line, an HTML body, and possibly attachments. Before accepting the message, the email service validates every part of it.

There's more here than you might expect. The from address must be from a domain you've verified. Attachments are checked for size limits and prohibited file types. If you're using a template, the service resolves it and merges your substitution data.

Recipient addresses are checked against a suppression list. Addresses that previously hard-bounced or filed spam complaints are blocked because resending to them would damage your sender reputation. This list is typically managed for you. Lettr, for example, maintains it automatically and blocks redelivery to addresses that previously caused bounces or complaints. Only after all of this passes does the service respond with success and queue the message for assembly.

Step 2: Message assembly

Your raw API data must now be formatted as an email message that complies with RFC 5322. The format dates back to RFC 822 in 1982 and has remained essentially stable for over forty years. Every email you've ever received uses the same structure.

That means creating headers: From, To, Subject, Date, Message-ID, MIME-Version, and others. Special characters are encoded per RFC 2047, which specifies how to represent non-ASCII text (accented characters, non-Latin scripts) in headers using encoded words such as =?UTF-8?B?...?=.

If the email includes both HTML and plaintext versions (it should, since some clients display only plaintext), the body is wrapped in a MIME multipart structure. Each part has its own Content-Type and is separated by a unique boundary string. The receiving client chooses which to display: a text-only client shows text/plain, while a modern client renders text/html. Attachments are encoded as Base64 MIME parts.

The result is a plaintext document that looks something like this:

From: notifications@yourapp.com
To: user@gmail.com
Subject: Verify your email address
Date: Tue, 04 Mar 2026 10:30:00 -0500
Message-ID: <abc123@yourapp.com>
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="boundary123"

--boundary123
Content-Type: text/plain; charset=UTF-8

Click here to verify: https://yourapp.com/verify?token=xyz

--boundary123
Content-Type: text/html; charset=UTF-8

<html><body><a href="https://yourapp.com/verify?token=xyz">Verify</a></body></html>
--boundary123--

This is the actual document transmitted over the wire. Strip away the clients, the apps, and the rich rendering, and what flows between mail servers is plain text like this.

Step 3: DKIM signing

Before the message leaves the sending infrastructure, it receives a cryptographic signature. This is DKIM, or DomainKeys Identified Mail. The sending server creates a hash of specific headers (usually From, To, Subject, Date) and the message body, then signs the hash with a private key. The signature is placed in a DKIM-Signature header, and the public key is stored in a DNS TXT record on the sender's domain so any receiving server can fetch it and verify.

If anyone modifies the message in transit (altering the body, changing a header, injecting content), the signature won't match when the receiver verifies it. It's a tamper-detection mechanism and one of the three pillars of modern email authentication, alongside SPF and DMARC.

Most email providers automatically handle key generation and signing. You just add the DNS records they provide. Lettr generates these records and verifies them during domain setup.

Step 4: DNS lookups and routing

Once the message is assembled and signed, the sending server has to figure out where to deliver it. That begins with a DNS lookup for the recipient's domain. The server looks up MX (Mail Exchange) records, which specify which hosts handle incoming mail for that domain. A domain can have multiple MX records with different priority values. The server tries the lowest-numbered record first (lower = higher priority, confusingly), and falls back to the others if it's unavailable.

If the MX lookup fails (the domain doesn't exist or has no MX records), the message bounces immediately. The address is simply unroutable.

Step 5: The SMTP handshake

The sending server opens a TCP connection to the recipient's mail server and talks SMTP (Simple Mail Transfer Protocol). Despite the name, the conversation is structured:

Sending server: EHLO mail.yourprovider.com
Receiving server: 250-smtp.gmail.com at your service
Sending server: MAIL FROM:<notifications@yourapp.com>
Receiving server: 250 OK
Sending server: RCPT TO:<user@gmail.com>
Receiving server: 250 OK
Sending server: DATA
Receiving server: 354 Start mail input
Sending server: [the full RFC 5322 message from step 2]
Sending server: .
Receiving server: 250 2.0.0 OK

That final 250 is what "delivered" means in email. The receiving server has accepted the message and taken responsibility for it. But here's what most people miss: that 250 doesn't mean the email is in the inbox. It means the receiver has accepted it for processing. Everything that happens next is on their side, and the sender has no visibility into it.

This is the delivery vs. deliverability distinction that trips up many developers. Delivery means the server accepted the message. Deliverability means it actually reached the inbox. You can have a 99% delivery rate and still have half your emails in spam.

Step 6: Authentication checks

Once the receiver accepts the message, it verifies the sender's identity. Three checks work together:

SPF (Sender Policy Framework). The receiver looks up the SPF record in the sender's domain's DNS. This TXT record lists the IP addresses authorized to send on behalf of that domain. If the sending server's IP isn't listed in the SPF record, SPF fails.

DKIM verification. The receiver retrieves the DKIM-Signature header, fetches the public key from DNS, and verifies the cryptographic signature. If the message was altered in transit or the signature doesn't match, DKIM verification fails.

DMARC (Domain-based Message Authentication, Reporting & Conformance). The policy layer. The domain owner publishes a DMARC record that tells receivers what to do when SPF or DKIM fail: none (do nothing, just report), quarantine (send it to spam), or reject (drop the message). DMARC also requires "alignment": the domain in the From header must match the domain that passed SPF or DKIM.

Since February 2024, Gmail requires all three for bulk senders (anyone sending 5,000+ messages per day). Even if you're not a bulk sender, missing authentication makes your messages look suspicious. Most teams slip up by failing to get all three configured correctly and to keep them in sync. Lettr verifies SPF, DKIM, and DMARC alignment during domain setup, so misconfigurations are caught before you send a single message rather than appearing as mysterious placement failures weeks later.

Step 7: Content filtering

Authentication tells the receiver who sent the message. Content filtering decides whether the message is wanted. Spam filters are proprietary; Google, Microsoft, and Yahoo don't publish how their systems work, for obvious reasons. But the general principles are known, and they reinforce each other in a specific order.

Reputation comes first. Your sending domain has a score, and almost everything else is evaluated in light of it. A domain that consistently sends mail that people open and engage with builds a good reputation. A domain that attracts spam complaints or hits invalid addresses builds a bad one. IP reputation still matters, but less than it used to, as providers are shifting toward domain-based signals.

Engagement is what builds that reputation. Gmail, in particular, tracks how recipients treat your messages. Opens, replies, moves out of spam, and deletes without reading - every interaction nudges the score. This creates a feedback loop: good engagement earns better placement, which in turn drives more engagement. Bad engagement does the inverse, and recovery is much slower than the damage.

Content analysis is the tiebreaker. The filter scans the body for spam patterns: excessive capitalization, deceptive links, known phishing phrases, and suspicious image-to-text ratios. A legitimate order confirmation and a phishing email can look very similar to a naive content filter, which is exactly why reputation carries so much weight when the content is ambiguous.

Recipient-level signals override the domain score. Even with a clean reputation, placement can vary by recipient. If a specific user has never opened your emails, Gmail may route them to spam for that user alone, while everyone else still sees them in the primary inbox.

Step 8: Placement

After authentication and content filtering, the receiver makes the final call. The email goes to one of three places:

Inbox. The message lands in the primary inbox. In Gmail, it might end up in Promotions or Updates instead, which is still technically "inbox" but with much less visibility.

Spam/Junk. The message is accepted but filtered out. The user can technically find it, but almost no one regularly checks their spam folder. Your delivery rate looks fine, your open rate bombed, and you have no idea why, until someone mentions they found your emails buried in the junk folder.

Out-of-band rejection. The receiver accepted the message during the SMTP conversation (returning a 250) but later decided not to deliver it. This results in a bounce notification minutes or hours later. These are particularly hard to diagnose because, from the sender's perspective, delivery already appeared successful.

There's no standard signal for spam placement. If your email lands in spam, you won't receive an error code or a webhook. You'll notice it indirectly through declining open rates, which is why tracking engagement matters. Lettr's events API surfaces open and click data for each message, so a sudden drop in a particular email type is visible before it becomes a support ticket.

The feedback loop

The story doesn't end with placement. Signals flow back from the recipient's mail server to the sender, and how you handle them directly affects your future deliverability.

Bounces come in two types. Hard bounces (5xx SMTP codes like 550 5.1.1 User Unknown) indicate the address is permanently invalid: it doesn't exist, the domain is gone, or the mailbox is disabled. Soft bounces (4xx codes like 421 Try again later) are temporary: the server is overloaded, the mailbox is full, or you're being rate-limited. Hard bounces should trigger immediate suppression. Soft bounces can be retried, but if they keep failing, suppress them too.

Spam complaints happen when a recipient clicks "Report Spam" or "Mark as Junk." Major mailbox providers use feedback loops (FBLs) to notify the sender. Above roughly 0.3% (Gmail's published threshold), you're in serious trouble. Anything over 0.1% already deserves investigation.

Opens and clicks are tracked using invisible pixel images and redirect links embedded in the HTML body. They're not perfect, as image blocking, privacy tools like Apple's Mail Privacy Protection, and plaintext rendering all distort the numbers. But they're the closest thing to engagement data senders have. A sudden drop in open rates on a previously healthy domain usually indicates that placement has gotten worse.

Lettr reports all of these (deliveries, bounces, opens, clicks, complaints) via webhooks and the events API, allowing you to respond to delivery issues automatically rather than waiting for support tickets.

Where things break

Each stage in this pipeline has its own failure modes, and the symptoms aren't always obvious.

DNS misconfigurations are the most common root cause. These include a missing SPF record, a DKIM key that was rotated but never updated in DNS, and a DMARC policy set to reject before all your legitimate senders pass authentication. These issues are silent. Emails start disappearing or landing in spam, and nothing alerts you unless you're watching.

Reputation damage accumulates gradually. Sending to a purchased list (don't), neglecting bounce handling, or letting complaint rates creep up all erode reputation over time. The frustrating part is the lag: you can damage your reputation today and not see the impact on inbox placement for days or weeks.

Content-filter false positives happen when legitimate transactional emails are flagged as spam. Password reset emails with links can look like phishing. Order confirmations with many product images can look like promotional spam. The fix is usually to keep transactional emails simple: one message, one purpose.

Shared infrastructure risks can catch people off guard. If your email provider uses shared IP addresses (most do for smaller senders), another customer's poor sending practices can drag down your deliverability. Domain authentication (SPF, DKIM, DMARC) helps by tying your reputation to your domain rather than to a shared IP address.

These failure modes compound. A DNS misconfiguration lets spam through, which damages reputation, which tightens content filters against you, which depresses engagement, which further damages reputation. The pipeline is resilient by design, but when it breaks, it tends to break in several places at once, and each broken piece pulls the others down with it.

What you can actually control

You can't control spam filters or guarantee inbox placement. What you can do is remove the things that get in the way:

Authenticate everything. Set up SPF, DKIM, and DMARC for every sending domain. Non-negotiable in 2026. With Lettr, the DNS records are generated and verified for you. You just paste them into your DNS provider.

Handle bounces and complaints. Suppress hard bounces immediately. Investigate complaint spikes. Stop retrying addresses that repeatedly fail. This is the single highest-leverage action you can take for long-term deliverability. Lettr surfaces bounce and complaint events via webhooks in real time, so your app can respond immediately rather than discovering the damage days later.

Warm up new domains gradually. A brand-new domain has no reputation, and mailbox providers treat it with suspicion, even with perfect authentication. Start small with your most engaged recipients, then scale up over days or weeks. Skipping warmup is one of the most common reasons a new integration looks great in testing but falls apart in production.

Separate your sending streams. Transactional and marketing emails should use different subdomains, at a minimum. A marketing campaign that triggers complaints shouldn't jeopardize your password reset delivery.

Monitor signals, not just delivery rates. A 99% delivery rate means nothing if your open rate is 2%. Track opens, clicks, bounces, and complaints by email type. Aggregate numbers hide the failures that matter most.

Keep transactional emails focused. One email, one purpose. Don't include product recommendations in a password reset. Mailbox providers will notice and reclassify your messages.

The gap between "sent" and "received"

Most developers think of email as a simple input-output system: call an API, and someone receives a message. But between those two events, your message passes through DNS resolution, cryptographic verification, reputation scoring, and content analysis. Each step can silently drop or misroute it. The sender's only confirmation is a 250 OK, which means "I'll take it from here" with no promise of what happens next.

Once you see the full pipeline, you stop treating email as fire-and-forget. It's a distributed system where authentication, reputation, and feedback loops feed into one another, and it fails in ways that don't show up in your error logs but do show up in support tickets.

If you're building an app that sends email and would rather not spend the next month debugging DKIM alignment and processing feedback loops yourself, give Lettr a try.