Email API send is the developer workflow for delivering application-triggered messages—password resets, receipts, verification links, alerts, and invitations—through an HTTP API rather than manually composed email or a shared inbox. The first successful API response is only the beginning: a production implementation must authenticate the sending domain, prevent duplicate sends, process bounces, and distinguish accepted mail from delivered mail.
What an email API send request actually does
An email API lets your application ask an email-delivery provider to create and transmit a message. Your app sends an authenticated HTTPS request containing a sender, one or more recipients, a subject, and message content. The provider validates the request, queues the message, applies configured sending-domain authentication, and attempts delivery to the recipient's mail server.
That process is useful for transactional email: one-to-one messages caused by a user or system event. Typical examples include:
- Email-address verification after signup
- Password-reset and magic-link emails
- Order confirmations and invoices
- Team invitations
- Security alerts and account-change notifications
- Product notifications a user has explicitly enabled
Do not equate an API response such as 200, 201, or a returned message ID with inbox placement. It normally means the provider accepted your request. The recipient server may still defer, reject, filter, or later bounce the message. That is why an email API integration needs both a send path and an event-feedback path. Resend, for example, returns an email ID from its send endpoint and supports delivery, bounce, click, open, and failure events through webhooks. (resend.com)
Choose the right sending model before you write code
An API is not the only way to send application email. SMTP, a provider SDK, and a direct cloud-service API can all work. The right choice depends on your application architecture, not on a universal winner.
Email API versus SMTP
SMTP is the long-standing mail-submission protocol. It is broadly supported by application frameworks, legacy systems, and devices, but it is connection-oriented and usually provides less structured application feedback by default. An HTTP email API is often easier to use in serverless functions, background jobs, and modern web apps because it uses ordinary HTTPS requests, JSON payloads, API-key or IAM authentication, and provider-specific event hooks.
Use an email API when you want structured sends, tags, templates, idempotency controls, webhooks, or a straightforward integration with a web backend. Use SMTP when a platform only supports SMTP or when you need portability across providers and accept that your application will manage more of the surrounding operational behavior.
Transactional versus marketing email
Transactional messages are triggered by a specific user action or account event. Marketing and subscription messages are sent to an audience according to consent and campaign rules. The categories can overlap operationally—both need authentication, consent-aware handling, and bounce management—but do not treat a promotional campaign as a password-reset email merely to bypass subscription controls.
Subscription messages need a visible and functioning way to opt out where applicable. Gmail's subscription guidance calls for one-click unsubscribe for subscription messages and says unsubscribe requests should be honored within 48 hours. (support.google.com) Keep marketing and product-notification preferences in your own database, then consult those preferences before queueing a send.
Provider API versus direct cloud-email API
A specialist email API provider can reduce implementation work with developer-focused SDKs, webhooks, templates, and dashboards. A cloud service can fit well if your infrastructure, permissions, observability, and event systems already run in that cloud. Amazon SES API v2, for instance, supports simple, raw MIME, and templated content, as well as configuration sets and message tags. (docs.aws.amazon.com)
Before selecting a provider, verify these practical details:
- Can you verify a domain and enable DKIM on it?
- Does the service offer the regions, data handling, account controls, and uptime model your business requires?
- Can it publish delivery, bounce, complaint, and failure events to a system you operate?
- Does it support idempotency or can you implement duplicate protection yourself?
- Can you separate transactional streams from subscription or marketing streams?
- Do quotas, rate limits, sandbox restrictions, and pricing fit your expected volume? Compare the actual transactional email pricing against the operational features you need, not only the headline per-email price.
Set up the sending domain before sending production mail
The biggest early mistake is coding the API call before preparing the domain. A provider may allow a test sender or sandbox address, but production mail should come from a domain you control, such as notify.example.com or mail.example.com.
Verify the domain with your provider
Every provider has a slightly different dashboard and DNS setup flow. In general, you add the domain in the provider dashboard, copy the DNS records it gives you, publish them at your DNS host, and wait for verification. Do not edit provider-generated selector names or target values by hand unless the provider documentation explicitly directs you to do so.
Use a dedicated subdomain when it helps you separate traffic and reputation. For example:
notify.example.comfor account and security messagesreceipts.example.comfor order and billing messagesnews.example.comfor opted-in newsletters
A subdomain is not a deliverability shortcut. It is an organizational boundary that can make authentication, analytics, and stream-specific policies easier to manage.
Publish SPF, DKIM, and DMARC
Email authentication is DNS-backed and must be configured correctly for the actual sending setup.
- SPF identifies which sending infrastructure may use a domain in the envelope sender.
- DKIM adds a cryptographic signature that a receiver can verify using a public key published in DNS.
- DMARC tells receivers how to handle mail that fails aligned SPF and DKIM checks, and it can request aggregate reports.
NIST describes SPF as source authentication, DKIM as message-integrity authentication, and DMARC as domain-owner feedback on the effectiveness of those mechanisms. (nist.gov) Google recommends both SPF and DKIM for senders, while its requirements for senders delivering more than 5,000 messages per day to personal Gmail accounts include SPF, DKIM, and DMARC. (support.google.com) Yahoo's bulk-sender guidance likewise requires SPF, DKIM, a valid DMARC policy, and alignment of the visible From: domain with either the SPF or DKIM domain. (senders.yahooinc.com)
A simplified DMARC record might look like this:
_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com"
Start with p=none while you inspect reports and confirm every legitimate sender is authenticated. Moving immediately to p=quarantine or p=reject without inventorying application mail, support platforms, billing systems, and other vendors can block legitimate messages. DMARC policy actions include deliver, quarantine, and reject. (support.google.com)
Make the visible From address match your intent
Use a recognizable display name and an address at the verified domain:
From: Acme Security <security@notify.example.com>
Reply-To: support@example.com
Avoid changing display names, sender domains, and reply-to addresses unpredictably. Recipients should be able to understand who sent the message and where a reply will go. For security-sensitive email, make sure the reply channel is monitored or omit Reply-To if replies are not appropriate.
The minimum fields in an email API send request
Provider schemas differ, but a reliable send request usually includes the following fields:
| Field | Purpose | Production guidance |
|---|---|---|
from | Visible sender identity | Use a verified domain and stable display name. |
to | Recipient address or list | Validate input shape; do not blindly trust client-submitted recipients. |
subject | Message subject | Keep it specific and avoid misleading urgency. |
html | Rich message body | Escape dynamic values and use simple, resilient markup. |
text | Plain-text alternative | Include it for accessibility and clients that do not render HTML well. |
reply_to | Reply destination | Use only an inbox or workflow you operate. |
tags or metadata | Internal classification | Add a non-sensitive event type or tenant identifier. |
| idempotency key | Duplicate-send protection | Tie it to one business event, not merely one user. |
Do not put passwords, access tokens, payment details, government identifiers, or raw secrets in subjects, tracking tags, or provider metadata. Subjects can appear in inbox previews, notifications, forwarding rules, and support screenshots. Metadata may be retained in delivery logs.
For password resets and magic links, generate the token on your server, store only a hashed form where appropriate, give it a short expiry, and invalidate it after use. The email API should deliver a URL your application can verify; it should not become the security boundary itself.
Worked example: send an account-verification email
The following is a provider-specific example using Resend's REST endpoint. Its documented POST /emails request uses bearer authentication and accepts from, to, subject, and html; the service returns an email ID after accepting the request. (resend.com) The same application design works with other providers, but their endpoint, credentials, headers, and response shape will differ.
1. Store the API key outside your code
Set the key in your deployment environment:
export RESEND_API_KEY="re_replace_with_a_real_secret"
Never place a live provider key in browser JavaScript, mobile app code, a public repository, an image, or an email template. A browser client could expose the key to every visitor and allow unauthorized sending. Generate separate keys for local development, staging, and production when the provider supports it, and use the least-privileged scope available. Resend, for example, offers a sending-only key scope. (resend.com)
2. Test the request with cURL
Replace the sender with an address on your verified domain and use an address you control as the recipient:
curl -X POST "https://api.resend.com/emails" \
-H "Authorization: Bearer $RESEND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: verify-email/user_123/order_456" \
-d '{
"from": "Acme <accounts@notify.example.com>",
"to": ["you@example.net"],
"reply_to": "support@example.com",
"subject": "Verify your Acme email address",
"html": "<p>Welcome to Acme.</p><p><a href=\"https://app.example.com/verify?token=REPLACE_ME\">Verify your email address</a></p>",
"text": "Welcome to Acme. Verify your email address: https://app.example.com/verify?token=REPLACE_ME",
"tags": [
{"name": "message_type", "value": "verify_email"}
]
}'
The Idempotency-Key is essential when a request can be retried after a timeout. The name above represents one verification event, not a reusable key for every email. Resend documents idempotency keys of up to 256 characters and keeps them for 24 hours; its example format includes an event type and entity ID. (resend.com) Other vendors may use a different header, offer a different retention window, or not support idempotency at all.
3. Move the send behind a server-side function
Do not let the client call your email provider directly. Instead, let your server respond to the actual business event, create a durable job, and send from a worker. This Node.js-style pseudocode shows the architecture:
async function requestEmailVerification(user) {
const eventId = `verify-email/${user.id}/${user.verificationVersion}`;
await jobs.insertIfAbsent({
idempotencyKey: eventId,
type: "send_verification_email",
userId: user.id,
status: "queued"
});
}
async function processVerificationJob(job) {
const token = await createVerificationToken(job.userId);
const verifyUrl = `https://app.example.com/verify?token=${encodeURIComponent(token)}`;
const response = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.RESEND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": job.idempotencyKey
},
body: JSON.stringify({
from: "Acme <accounts@notify.example.com>",
to: [await getEmail(job.userId)],
subject: "Verify your Acme email address",
html: `<p>Use this link to verify your address:</p><p><a href="${escapeHtml(verifyUrl)}">Verify email</a></p>`,
text: `Use this link to verify your address: ${verifyUrl}`
})
});
if (!response.ok) throw new Error(`Email API returned ${response.status}`);
const { id: providerMessageId } = await response.json();
await jobs.markSent(job.id, providerMessageId);
}
The critical parts are not the JavaScript syntax. They are the durable job, a business-event idempotency key, server-held credentials, a stored provider message ID, and controlled retry behavior.
Build a send pipeline that survives retries and outages
A direct API call inside a web request is tempting: create account, call email provider, return success. It fails awkwardly when either your app or the provider is slow. A user might refresh, your server might time out after the provider accepted the message, or a deployment may interrupt the request.
Use an outbox or job queue
Store the business change and a pending email job in the same database transaction when possible. A worker reads the queued job and performs the provider call. That design prevents a common failure mode: the user account is created but no verification email exists because the request crashed between the database write and the send call.
Your job record can contain:
- Internal job ID
- Business event type, such as
password_resetorinvoice_ready - Recipient user ID, not necessarily the raw email address
- Template version
- Provider message ID after acceptance
- Idempotency key
- Attempt count and next-attempt time
- Final state such as
queued,accepted,failed, orsuppressed
Retry only the failures that are safe to retry
Classify failures before retrying. A network timeout may be ambiguous: the provider could have accepted the email even though your application received no response. Reusing the same idempotency key makes this case safer when the provider supports it. An invalid API key, unverified domain, malformed payload, or policy rejection will not be fixed by blindly retrying every minute.
Use exponential backoff with a cap and jitter for temporary failures. For example, start with a short delay, increase the delay for each attempt, and stop after a defined maximum. Send persistent failures to a dead-letter queue or alerting workflow with the provider response, job ID, and event type—but never log API keys or full reset URLs.
Avoid duplicate emails at more than one layer
Provider idempotency is helpful, but it should not be your only control. Enforce uniqueness in your job table as well. For a welcome email, a unique key might be welcome/user_123; for a receipt, it might be receipt/order_987; for a reset, it might include the reset-request version.
This is how to tell it worked: an intentional repeat request for the same business event either returns the existing send record or reuses the provider's idempotent response, and the recipient receives one email rather than two.
Design email content for real inboxes, not just browser previews
A polished HTML preview does not guarantee a usable inbox experience. Mail clients vary widely in CSS support, image loading, dark-mode behavior, link handling, and text clipping.
Always provide HTML and plain text
Send both formats. HTML can provide hierarchy, buttons, and branded layout; plain text gives recipients and simple mail clients a readable fallback. Amazon SES's formatted-email documentation supports text, HTML, or both, and its raw-email mode exists for cases where you need direct MIME control for headers, multipart bodies, or attachments. (docs.aws.amazon.com)
Use semantic, conservative HTML:
- Keep the layout narrow and simple.
- Use actual text for essential instructions, not text embedded in images.
- Make links descriptive and fully functional.
- Include a text URL for critical actions such as verification or reset flows.
- Escape user-controlled values before inserting them into HTML.
- Keep a readable subject and preheader without revealing sensitive data.
Treat attachments as an exception
Attachments add size, scanning, MIME complexity, and a higher chance of recipient distrust. If a secure download page can meet the need, send a time-limited authenticated link instead. When attachments are required, test content type, filename encoding, size limits, and the behavior of your chosen provider. In SES API v2, attachments can be supplied in the simple or templated content structure, while raw mode requires you to build a valid MIME message yourself. (docs.aws.amazon.com)
Deliverability: authentication is necessary, not sufficient
Domain authentication lets a recipient server evaluate whether the mail is legitimately associated with your domain. It does not guarantee inbox placement. Mailbox providers also consider reputation, user engagement, complaint patterns, sending consistency, content, and recipient validity.
Keep streams and expectations clear
Use a distinct sender identity for high-value transactional email. Do not mix a high-volume promotional newsletter into the same sender identity as password resets if you can avoid it. A recipient who dislikes promotional mail may mark it as spam; that signal can affect how future mail is treated.
Send only messages users expect. A verification email immediately after signup is expected. A daily product pitch after a user created an account is a separate subscription decision. For subscription messages, include a visible unsubscribe option and implement provider-supported list-unsubscribe headers where appropriate.
Watch complaint and spam rates
Yahoo says senders should keep spam rates below 0.3% and remove addresses that generate 5xx errors or bounces according to a list-management policy. (senders.yahooinc.com) Gmail provides Postmaster Tools dashboards for spam rate, reputation, message authentication, and delivery errors. (support.google.com) These signals matter most at meaningful volume, but the operational lesson applies at every size: do not keep sending unwanted or undeliverable mail.
Validate addresses at the right moment
Basic format validation catches obvious typos, but it cannot prove that a mailbox is active or belongs to the person who entered it. Use a confirmation link for account ownership, and consider pre-send validation where the cost of bad addresses is material. A free email address verification tool can help screen obvious syntax and domain issues, but do not use validation as a substitute for consent or verified signup.
Process webhooks and delivery events as part of the product
A mature email API setup has two directions of communication:
- Your application calls the provider to request a send.
- The provider sends events back to your application about what happened.
Webhooks are HTTPS POST requests containing event data. Resend describes its webhooks as real-time HTTPS requests with JSON payloads and notes that events can be replayed, which means your handler must be safe if it receives the same event more than once. (resend.com) Amazon SES can publish send, delivery, bounce, complaint, reject, delay, open, click, and subscription events through configuration-set event destinations. (docs.aws.amazon.com)
Handle these events deliberately
| Event | What it means | Typical action |
|---|---|---|
| Accepted or sent | Provider accepted the message | Store provider ID; do not call it delivered yet. |
| Delivered | Receiving server accepted the message | Mark delivery; retain for support and analytics. |
| Deferred or delayed | Temporary delivery issue | Let the provider retry; alert only if delays become systemic. |
| Hard bounce | Address or destination is permanently invalid in many cases | Suppress future non-essential sends and investigate. |
| Complaint | Recipient marked mail as spam | Stop non-essential mail immediately and review consent/source. |
| Failed | Provider could not send | Inspect error category; fix configuration or retry if transient. |
| Unsubscribe | Recipient opted out | Update preferences before future subscription messages. |
Do not use open and click events as proof that a human read a message. Privacy features, proxies, image blocking, security scanners, and link-preview systems make those signals imperfect. Delivery and bounce events are more operationally useful for send reliability; user actions inside your app are better evidence of completion.
Verify webhook signatures and deduplicate events
Use the provider's official signature-verification procedure. Validate the raw body if the provider requires it, reject unsigned or invalid requests, store the provider event ID, and enforce a uniqueness constraint on that event ID. Then acknowledge quickly and move slower work—database enrichment, analytics, CRM updates, alerts—to a queue.
Do not delete a user instantly because one bounce event arrives. A bounce should update the email-address state and prevent inappropriate future sends; account policy should be a separate product decision.
Test the whole flow before production
A successful local API request is not a production test. Test messages must prove domain authentication, recipient rendering, event ingestion, duplicate prevention, and failure handling.
A practical test plan
- Send to inboxes at multiple mailbox providers, not only your company domain.
- Inspect the received message headers for SPF, DKIM, and DMARC results.
- Verify the visible From address, reply-to behavior, subject, HTML, and text fallback.
- Click a verification or reset link and confirm it expires and cannot be reused when your security model requires one-time use.
- Send the same business event twice and confirm idempotency prevents a duplicate email.
- Trigger a deliberate invalid-recipient test where permitted, then confirm your bounce event reaches the webhook endpoint.
- Temporarily make your webhook handler return an error in a non-production environment and confirm replay or retry behavior.
- Test API-key revocation and rotation procedures before an incident forces you to use them.
For Amazon SES accounts still in its sandbox, sending is restricted to verified identities or mailbox simulator addresses, and its documented SendEmail API has a 10 MB maximum message size. (docs.aws.amazon.com) That is a provider-specific constraint, but it illustrates why you should verify account state and provider limits before a launch.
How to tell an email API integration worked
A working production integration produces evidence at each stage:
- Your application created exactly one email job for one business event.
- The provider accepted the request and returned a message identifier.
- The job record stores that identifier and relevant, non-sensitive metadata.
- The recipient receives a usable message with correct authentication results.
- Your webhook endpoint receives and stores the eventual delivery, bounce, delay, or failure event.
- A duplicate request does not create a duplicate message.
- A bounce, complaint, unsubscribe, or suppression event changes future sending behavior.
That chain is much more meaningful than seeing an API response in a terminal.
Common email API send errors and how to fix them
“Email address not verified” or domain verification failures
This usually means the sender address or domain has not been verified with the provider, DNS records have not propagated, records were copied incorrectly, or you are using a sender address outside the verified identity. Confirm the exact From domain and compare every published DNS record against the provider's current dashboard instructions.
The API accepts email but nothing arrives
First look for a provider event: delivered, delayed, bounced, rejected, or failed. Then inspect the recipient spam folder and message headers. Common causes include an unauthenticated or misaligned domain, poor sending reputation, an incorrect recipient, content that triggers filters, or recipient-server delays. Do not repeatedly resend until you know which state occurred.
Duplicate receipts, invitations, or password-reset emails
This is usually an application retry problem rather than an email-template problem. Add a durable unique job record and reuse the same idempotency key for a single business event. A provider-supported key can make ambiguous timeout retries safe, but application-level uniqueness protects you across queues, deploys, and provider changes.
Webhooks are missing or unreliable
Confirm that the endpoint is publicly reachable over HTTPS, that signature validation uses the provider's required method, and that the handler returns a success response promptly after durable storage. Add deduplication because webhooks can be replayed. Resend explicitly supports webhook replay for missed events or reprocessing. (resend.com)
Your API key leaked
Revoke or rotate it immediately, create a replacement key with the smallest necessary scope, update the deployment secret, and inspect sending logs for unauthorized activity. Then identify the exposure route—client code, logs, CI output, repository history, screenshot, or shared environment variable—and remove it. Treat email-provider keys as sending credentials, not harmless configuration.
A production checklist for email API send
Before you call the integration finished, confirm all of the following:
- The From domain is verified with the provider.
- SPF and DKIM are configured; DMARC is published and monitored.
- Transactional and subscription email have distinct rules and, where useful, distinct sender subdomains.
- API keys are server-side, scoped, rotatable, and absent from logs.
- Every message contains a useful HTML version and plain-text alternative.
- Dynamic HTML is escaped and sensitive data is not exposed in subjects or metadata.
- Sends originate from a durable queue or outbox, not only a browser request.
- Every business event has an application-level duplicate guard.
- Provider idempotency is used where available.
- Provider message IDs are stored against your internal job records.
- Webhook signatures are verified, event IDs are deduplicated, and handlers are replay-safe.
- Bounces, complaints, and unsubscribes update suppression or preference records.
- You test messages at multiple mailbox providers and inspect authentication headers.
- You monitor delivery failures, spam complaints, and authentication status over time.
A reliable implementation is not complicated because an HTTP request is difficult. It is complicated because email is an asynchronous, reputation-sensitive, security-relevant system. Build the send call as one component of a complete lifecycle: authenticate, queue, send once, observe, suppress, and improve.
FAQ
What is the simplest way to send email with an API?
Verify a domain with an email provider, create a server-side API key, and send a request containing from, to, subject, and both HTML and text content. For a production system, add a durable job queue, idempotency key, and webhooks before relying on it for critical messages.
Does an accepted API response mean the email was delivered?
No. It means the provider accepted the request. Confirm delivery through the provider's event stream or webhook. A message can later be delayed, bounced, rejected, filtered, or delivered to spam.
Should I use an email API or SMTP?
Use an email API when your application benefits from JSON requests, serverless compatibility, structured metadata, webhooks, and provider SDKs. Use SMTP when your software only supports SMTP or portability matters more than API-specific features.
Do transactional emails need an unsubscribe link?
Purely transactional messages tied to an account action, such as a password reset or receipt, are different from subscription messages. Promotional, newsletter, and opted-in product-update email should have appropriate unsubscribe handling; Gmail's subscription guidance calls for one-click unsubscribe and honoring requests within 48 hours. (support.google.com)
Why should I use an idempotency key when sending email?
Network failures can leave your application uncertain whether a provider received a request. An idempotency key lets a supporting provider recognize a retry of the same send, reducing the risk that a timeout creates duplicate email. You should still enforce uniqueness in your own job or outbox table.