Learn

How transactional email actually works: from API call to inbox

Updated September 25, 2026

In short

When your app sends a transactional email through an API, the provider validates the request and returns an id immediately, then queues the message, signs it with DKIM as your domain, and delivers it over SMTP to the recipient's mail server. That server checks SPF, DKIM and DMARC, scores the message for spam, and answers with a status code: 250 accepted, 4xx try again later, or 5xx permanent failure. The provider turns that answer into an event - delivered, delayed or bounced - and sends it to your webhook.
How a transactional email travels from your app to the inbox and backHTTPSqueuedSMTP + TLS250replystatus becomes an eventPOSTYour appPOST /v1/emailsgets { id } at onceEmail APIvalidate, checksuppression listQueue + signerDKIM-sign asyour domainSending serverSMTP to therecipient's MXRecipient serverSPF · DKIM · DMARCspam scoringInbox or spamGmail, Outlook,company mailSMTP reply250 · 4xx · 5xxLogs + webhooksemail.deliveredemail.bouncedYour handlerupdate yourdatabase

Swipe the diagram to see the rest

The life of one transactional email: your API call returns an id straight away, delivery happens asynchronously, and the outcome comes back as an event.

1. Your app makes one HTTPS request

Your code sends JSON - from, to, subject, html, text - with an API key in the Authorization header. The provider checks the request before accepting it: is the key valid, is the from domain verified on this account, is any recipient on the suppression list, is the account within its quota? Any of those failing comes back straight away as a 4xx with a machine-readable error type, not as a bounce email hours later.

The request
POST /v1/emails
Authorization: Bearer sp_...
Idempotency-Key: order-10428-receipt

{
  "from": "Acme <orders@mail.acme.com>",
  "to": ["customer@example.com"],
  "subject": "Receipt for order #10428",
  "html": "<p>Thanks for your order.</p>",
  "text": "Thanks for your order."
}

If everything checks out you get 200 with an id. That id is the message's permanent handle - store it next to the order or user it belongs to, because every event about this email will carry it.

2. The message is queued and signed

The provider builds the MIME message - headers, a multipart body with your HTML and text, attachments - and signs it with DKIM. The signature covers the body and key headers, uses a private key held by the provider, and names your domain in its d= tag. Anyone can later check it against the public key published in your DNS.

It also sets the envelope sender, called the Return-Path or MAIL FROM. This is not the From address your recipient sees; it is where bounces go. On Sendpository it lives on a sp. subdomain of your domain, so bounces come back to us to be processed and your own SPF record is never edited.

3. SMTP delivery to the recipient's server

The sending server looks up the MX record for the recipient's domain - for gmail.com, Google's mail servers - opens an SMTP connection, upgrades it to TLS, and hands the message over. This is the step an email API saves you from writing: connection handling, TLS, per-provider throttling and retries all happen here.

4. The receiving server decides

Before accepting, the receiving server authenticates the message. SPF checks the connecting IP against the envelope sender's domain. DKIM verifies the signature. DMARC checks that at least one of those passed for the domain in the visible From line. The message is then scored for spam using those results, the sending domain's reputation, and its content.

How a receiving server checks SPF, DKIM and DMARCenvelope domaind=, s=From domainIncoming messageMAIL FROM: <b@sp.acme.com>From: Acme <hi@acme.com>DKIM: d=acme.com s=s1abcSPFTXT on sp.acme.com:is this server's IP listed?DKIMs1abc._domainkey.acme.com:does the signature verify?DMARC_dmarc.acme.com: did SPFor DKIM pass for acme.com?Verdictaligned pass → deliverfail → apply p= policy

Swipe the diagram to see the rest

What the receiving server checks. SPF looks at the envelope sender, DKIM at the signature's d= domain, and DMARC asks whether either one matches the From address people see.

The server answers with a three-digit SMTP code. 250 means accepted - into the inbox or the spam folder, which the sender is never told. A 4xx means "not now" (mailbox full, rate limited, greylisted) and the sender should retry. A 5xx means permanent failure, such as an address that doesn't exist.

5. The result comes back as an event

The provider turns every outcome into an event on the message: email.delivered, email.delayed, email.bounced, email.complained when the recipient marks it as spam, and email.opened or email.clicked if you enabled tracking. Each is recorded on the message's timeline and posted to your webhook.

Sendpository email detail page showing a message's timeline: Queued, Sent to provider, Bounced
A message's timeline in the Sendpository dashboard. This receipt was queued, handed to the provider and then bounced - and the address was suppressed.

What your code is responsible for

  • Send from server-side code only - the API key is a secret.
  • Store the returned id with the record the email is about.
  • Send an Idempotency-Key on anything that can be retried, so a timeout never becomes two emails. See idempotency.
  • Handle email.bounced and email.complained webhooks: flag the address in your own database so your product can ask for a new one.
  • Treat suppressed_recipient as expected, not as an error to retry.
A retried request with an idempotency keyYour jobEmail APIPOST /v1/emailsIdempotency-Key: order-42-receiptemail sentid d67e39ed…response lost - timeoutretry, same keyIdempotency-Key: order-42-receipt200 { id: d67e39ed… }no second email

Swipe the diagram to see the rest

Why an Idempotency-Key matters. The first request succeeded but its response was lost; the retry carries the same key and gets the original id back instead of sending a second email.

Questions

How long does a transactional email take to arrive?

Usually a few seconds. The API accepts it immediately, and the receiving server normally accepts within seconds. Delays come from the recipient's side - greylisting or throttling - and show up as email.delayed events while the message is retried.

Does a 200 response mean the email was delivered?

No. It means the provider accepted the message. Delivery is confirmed later by an email.delivered event, or refused with email.bounced.

Can I tell whether an email landed in spam?

Not reliably. Receiving servers answer 250 for both the inbox and the spam folder. Authentication, a good sending reputation and low complaint rates are what keep you out of spam.

Related

Send your first email in minutes

6,000 emails a month (up to 200 a day), free forever. No card needed.