---
title: Email API Implementation Guide for Product Teams | SendHQ
description: Design a safe email API integration with verified domains, queued sends, stable identifiers, controlled retries, delivery events, suppression, and tenant isolation.
canonical: https://sendhq.cc/guides/email-api
last-updated: 2026-08-24
---
# How should a product team implement an email API safely?

Implement an email API as an asynchronous, permissioned workflow rather than a direct form-to-provider call. Authenticate the caller, confirm tenant ownership of a verified From domain, validate and size the message, assign a stable application job ID, enqueue once, and submit from a worker. Record the provider message ID on acceptance, ingest delivery events idempotently, and suppress permanent bounces and complaints. Use bounded retries only when duplicate risk is controlled. Keep credentials server-side, minimize message data in logs, and distinguish API acceptance, mail-server delivery, and inbox placement.

## Define the API boundary before choosing a provider

An email API should expose application intent without leaking every provider detail into product code. Define resources for messages, sending domains, API keys, events, and suppressions. Decide which fields callers may control, including From, To, Reply-To, subject, text, HTML, and a small allowlist of headers. Reject caller-supplied transport headers that could conflict with provider signing or routing. Treat sending as a consequential write: the response should identify an application message resource and its current state, not imply a mailbox outcome. Keep provider account, Region, configuration set, and transport identifiers behind an adapter. This boundary makes provider migration possible and gives authorization, retention, and abuse controls a stable place to live.

## Authenticate callers and authorize every sender domain

Store API keys only as one-way hashes and display the complete secret once. Give each key a workspace owner, status, creation time, and revocation path; add narrower scopes when one integration should only send or only read events. Authentication answers who presented a credential, while authorization decides whether that principal may use the requested From domain and message resource. Check domain ownership on every send, including batch endpoints, rather than trusting a domain identifier supplied by the client. Require provider verification before enabling production traffic. Never put provider credentials or workspace API keys in browser JavaScript, query strings, analytics, or error messages. Object-level authorization is especially important for message, event, suppression, inbox, and domain identifiers in a multi-tenant API.

## Verify the domain and align authentication

A sending domain needs more than a database flag. Complete the provider's ownership check and publish the required DKIM records. SPF authorizes hosts for the SMTP MAIL FROM or HELO identity, while DKIM associates a signing domain with a cryptographic message signature. DMARC evaluates whether a successful SPF or DKIM identifier aligns with the visible RFC 5322 From domain and lets a domain owner publish handling and reporting policy. When a domain already has SPF, merge the required mechanism into the existing record; RFC 7208 says a domain must not publish multiple records that cause selection of more than one SPF record. Roll out stricter DMARC policy only after controlled messages and aggregate reports show that every legitimate sender is aligned. Authentication reduces unauthorized domain use but does not establish inbox placement.

## Validate message structure and minimize accepted input

RFC 5322 defines an Internet message as header fields followed by an optional body, with MIME specifications extending content beyond basic text. An API can hide most wire-format details while still enforcing them. Normalize recipient arrays, cap recipient count and total encoded size, require at least one text or HTML body, and validate addresses without pretending syntax proves mailbox existence. Remove carriage-return and line-feed characters from fields that become headers. Generate the Message-ID or let the provider do so; do not reuse it as the application job ID because a new message version can legitimately receive a new identifier. Allow only documented custom headers, reject duplicates of protected fields, and render templates before provider submission so missing variables fail in a controlled application state.

## Queue once and use stable application identifiers

A user request should create one durable message job in a transaction, then a worker should perform the provider call. Give the job a stable identifier and record a request fingerprint or caller-supplied idempotency key when the contract supports one. HTTP defines POST as non-idempotent by default and cautions against automatic retry unless the client knows the operation is effectively idempotent or knows the original request was not applied. That matters for email because a timeout can occur after the provider accepted the message but before the worker received the response. On ambiguous failure, first reconcile the stored job and provider state instead of creating a fresh send. Use an outbox pattern when application state and queue publication must move together, and put a uniqueness constraint around the idempotency boundary.

## Design retries around failure classes

Separate validation, authorization, throttling, provider rejection, transient transport failure, and recipient delivery failure. Invalid input and an unauthorized From domain should fail without a retry. Provider rate limits and temporary service errors can be retried with bounded exponential backoff, jitter, an attempt ceiling, and a queue visibility timeout longer than the worker's request deadline. An ambiguous network timeout needs duplicate-aware reconciliation rather than an unconditional new request. SMTP itself distinguishes transient 4xx and permanent 5xx replies, but an application using a provider API should follow that provider's documented error semantics. Move exhausted jobs to a reviewable dead-letter state and retain the sanitized reason. Do not retry a permanent recipient bounce as though it were an API outage, and do not turn a complaint into another send attempt.

## Record acceptance and ingest delivery events

Persist the provider message identifier immediately after acceptance and map it to the application message ID. Provider events can then update the correct resource even when a complaint report redacts recipient detail. Amazon SES, for example, distinguishes a successful send from delivery to the recipient's mail server and can publish delivery, bounce, complaint, reject, delivery delay, rendering failure, open, and click events. Verify webhook authenticity using the provider's documented mechanism, validate the event schema, deduplicate by a provider event identifier or deterministic fingerprint, and allow repeated delivery of the same event without repeating side effects. Store raw payloads only when necessary, encrypted, access-controlled, and retention-limited. The normalized state should distinguish accepted, delivered-to-server, bounced, complained, delayed, rejected, and suppressed outcomes.

## Make suppression a send-time control

A suppression record should be checked before every provider submission, not only shown in a dashboard. Permanently bounced addresses and complaints normally require suppression; temporary delivery delays need a different policy. Scope suppression deliberately. An account-wide list can protect shared reputation but may let one tenant's recipient outcome block another tenant. A tenant-scoped list reduces that coupling but still needs an abuse and platform-safety layer. Record the reason, source event, tenant, creation time, and controlled removal path. Removing a complaint or permanent-bounce suppression is consequential and should require deliberate review and evidence that the address is valid and the recipient expects the message. Avoid copying raw recipient addresses into general logs or experiments; operational storage can enforce sending policy while analytics uses aggregate counts.

## Protect batch sends and sensitive business flows

A batch endpoint multiplies the impact of an authorization or validation error. Apply the same domain ownership, suppression, size, and content checks to every item, impose a strict maximum batch length, and return per-item results without leaking another tenant's data. Rate limits should exist at credential, workspace, domain, and provider levels, with separate controls for bursts and rolling volume. A single global requests-per-second limit is not enough because one request can contain many recipients. Require deliberate confirmation in agent-driven tools before a high-impact batch is submitted. Separate transactional and marketing permissions when their consent and operational rules differ. Monitor unusual recipient growth, repeated rejected domains, high bounce or complaint changes, and rapid key creation. Rate limiting supports safety, but it does not replace authentication, object authorization, verified consent, or abuse response.

## Test failure paths before production

Use provider simulators or controlled mailboxes to test acceptance, receiving-server delivery, hard bounce, complaint, delay, invalid domain, revoked key, throttling, provider timeout, duplicate webhook, and queue redelivery. Confirm that the same idempotency key creates one application message, a replayed event produces no duplicate side effect, and one tenant cannot read or send with another tenant's domain or message ID. Inspect a real received message for From, Return-Path, DKIM, SPF, DMARC alignment, text and HTML rendering, unsubscribe behavior where applicable, and links. Load-test the queue below approved provider limits and verify backpressure rather than bypassing it. Add alarms for queue age, exhausted retries, event-ingestion failures, quota headroom, bounce and complaint changes, and missing provider callbacks. A launch checklist should name an owner for each alert and recovery action.

## Apply the pattern with SendHQ carefully

SendHQ provides workspace-scoped bearer keys, verified From-domain checks, single and batch message creation, inbound inboxes, message events, and suppression resources. These features support the architecture in this guide: keep the key server-side, create a message resource, retain its ID, and read later events rather than treating the initial response as final delivery. Regardless of platform, callers remain responsible for intended recipients, lawful and expected mail, content accuracy, and careful approval of consequential sends.

## Frequently asked questions

**Should an email API send synchronously from the web request?**

Usually no. Create a durable application message and enqueue it, then let a worker call the provider. This isolates latency, supports bounded retries, and makes ambiguous provider outcomes easier to reconcile.

**How do I prevent duplicate emails when a request times out?**

Use a stable application job ID and an idempotency boundary with a uniqueness constraint. On an ambiguous timeout, reconcile the existing job before issuing another provider submission with a new identity.

**Does a successful email API response mean delivery?**

No. It normally indicates that the API or provider accepted the request. Use later events to distinguish receiving-server delivery, bounce, complaint, delay, rejection, and suppression from initial acceptance.

**What DNS records does an email API need?**

The exact records depend on the provider, but production sending commonly requires domain verification and DKIM, plus a correct SPF strategy and a DMARC policy aligned with legitimate sending streams.

**Should API keys be stored in browser code?**

No. Keep workspace and provider credentials in server-side secret storage, hash application API keys at rest where possible, show complete secrets once, and provide prompt revocation and rotation paths.

**How should an email API handle permanent bounces?**

Normalize the provider event, map it to the application message, and suppress routine future sends to that recipient within the intended scope. Removal should be deliberate and supported by evidence.

## Sources

- [RFC 9110: HTTP Semantics](https://datatracker.ietf.org/doc/html/rfc9110) — Internet Engineering Task Force
- [RFC 5321: Simple Mail Transfer Protocol](https://datatracker.ietf.org/doc/html/rfc5321) — Internet Engineering Task Force
- [RFC 5322: Internet Message Format](https://datatracker.ietf.org/doc/html/rfc5322) — Internet Engineering Task Force
- [RFC 6376: DomainKeys Identified Mail Signatures](https://datatracker.ietf.org/doc/html/rfc6376) — Internet Engineering Task Force
- [RFC 7208: Sender Policy Framework](https://datatracker.ietf.org/doc/html/rfc7208) — Internet Engineering Task Force
- [RFC 7489: Domain-based Message Authentication, Reporting, and Conformance](https://datatracker.ietf.org/doc/html/rfc7489) — Internet Engineering Task Force
- [Monitoring Amazon SES sending activity](https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity.html) — Amazon Web Services
- [Amazon SES notification troubleshooting](https://docs.aws.amazon.com/ses/latest/dg/troubleshoot-notifications.html) — Amazon Web Services
- [OWASP API Security Top 10 2023](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) — OWASP Foundation
- [SendHQ OpenAPI contract](https://sendhq.cc/openapi.json) — SendHQ
