Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To keep a Node.js app’s email deliverable, authenticate its sending domain with the records required by its provider, then reconcile provider-side complaints and bounces with your application’s contact state. The DNS records, suppression scope, expiration rules, and API behavior vary by service, so treat Cloudflare Email Service and Mailgun as examples—not interchangeable instructions.

What to configure on a custom sending domain

Start with the email provider’s current domain-onboarding screen and DNS instructions. A sending domain typically needs authentication and a way to handle delivery failures, but the exact record names and values are provider-specific. Do not copy one provider’s records into another provider’s setup.

Cloudflare Email Sending example

Cloudflare’s Email Sending setup documents MX records on cf-bounce.yourdomain.com to route bounce mail, SPF on the cf-bounce subdomain, DKIM at cf-bounce._domainkey, and DMARC at _dmarc.yourdomain.com. Use the values shown for your own domain in Cloudflare; these hostnames and records are not Mailgun or Amazon SES instructions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cloudflare separates its outbound Email Sending records from Email Routing records. Its sending records use the cf-bounce subdomain, while routing records are configured on the root domain and use a separate DKIM selector. The two services’ records can be verified separately. If the domain already has an SPF record, Cloudflare says to merge the required mechanisms into that existing record rather than publish duplicate SPF records; its documentation also notes an SPF limit of 10 DNS lookups.

Cloudflare says DNS changes can take up to 24 hours to propagate globally, though changes for domains using Cloudflare DNS usually complete within 5–15 minutes. Those are Cloudflare’s estimates for its DNS environment, not a delivery guarantee or a general timing rule for other DNS providers.

Verify before sending real traffic

  1. Enter the sending domain in the selected provider’s onboarding flow and copy the records and values it generates.
  2. Publish those records at the authoritative DNS provider. Check existing SPF configuration before adding anything that could create a duplicate SPF record.
  3. Use the email provider’s dashboard or recommended DNS tools to verify each required record, including any separate sending and routing configurations.
  4. Send controlled messages and inspect provider logs for authentication results, delivery failures, and suppression behavior before using the domain for normal traffic.

What suppression lists do—and why scope matters

A suppression list blocks delivery to addresses a provider considers unsuitable, such as recipients who complained or addresses associated with delivery failures. Providers may also let the sender add entries manually. Suppressions protect sending reputation, but a suppression on one domain or account may not automatically apply everywhere your application sends.

Cloudflare scope and expiration rules

Cloudflare distinguishes two scopes. An account suppression applies across the account’s sending domains and subdomains. A sending_domain suppression applies only to the exact sending domain; a suppression for a root domain does not automatically match its subdomain. Choose the scope with your domain architecture in mind, especially if marketing and transactional messages use separate subdomains.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Cloudflare suppression type Documented behavior
Complaint Does not expire.
Eligible soft bounce Defaults to a 24-hour expiration.
Eligible hard bounce May expire after seven days for some permanent failures, or remain indefinitely when a mailbox or domain does not exist or a recipient-side issue persists across repeated attempts.

These are Cloudflare’s rules, not universal email-provider defaults. Cloudflare also says not every temporary delivery failure should be treated as a recipient suppression: sender-side authentication or reputation problems, for example, should not be classified as recipient problems.

Keep complaints distinct from other failures

A complaint is not an ordinary transient bounce. Cloudflare recommends changing or deleting a complaint suppression only after the recipient opts in again through your application. Mailgun says an address on its complaint or unsubscribe suppression list will not be sent to even if it is allowlisted. Treat any removal as a deliberate, auditable action rather than a routine retry fix.

In application logic, keep complaint, hard-bounce, soft-bounce, and unsubscribe states separate. They have different causes and may require different product or communication policies. Provider documentation does not determine whether your app should disable account notifications, password resets, or other account functions; make those decisions with the product owner.

How to reconcile provider suppressions in a Node.js app

Do not treat your contact table and a provider’s suppression list as the same database. The provider decides whether it will accept delivery to an address; your application decides how that address is represented in its own contacts and workflows. Reconciliation should preserve both facts rather than erase the provider’s delivery history.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Mailgun’s Node.js SDK as one concrete example

Mailgun’s Node.js package documents mg.suppressions.list(domain, suppressionType, query?), with examples for bounces, unsubscribes, and complaints. It also documents get, create, upload, and destroy operations. This is a Mailgun-specific SDK surface; it should not be assumed to match Cloudflare, Amazon SES, or another provider.

A reconciliation worker can call the provider through a small adapter, normalize each returned address for matching, and update the corresponding application record idempotently. The following is application-level pseudocode: fetchSuppressionPage and its result shape are adapter contracts you define from the selected provider’s current API, not literal Mailgun SDK methods.

async function reconcileSuppressionPage(provider, cursor) {
  const page = await provider.fetchSuppressionPage({ cursor });

  for (const entry of page.entries) {
    const emailKey = normalizeEmail(entry.address);
    const contact = await contacts.findByEmailKey(emailKey);

    await suppressionObservations.upsert({
      provider: provider.name,
      providerRecordId: entry.id,
      emailKey,
      type: entry.type,
      observedAt: entry.createdAt,
      readOnly: entry.readOnly
    });

    if (contact) {
      await contacts.applySuppressionState(contact.id, {
        type: entry.type,
        active: true
      });
    }
  }

  return page.nextCursor;
}

Use a stable provider record identifier and timestamps when the provider exposes them. An upsert or equivalent idempotent operation helps ensure that observing the same record again does not repeatedly mutate the contact. Keep enough provenance to distinguish a provider observation from a user action, such as a fresh opt-in.

Respect mutability and preserve provider state

Mailgun’s guidance says to remove suppressed addresses from contact lists maintained by the sender while retaining the address in Mailgun’s suppression list. Cloudflare’s API guidance says to inspect read_only before trying to change an entry; do not infer whether an entry can be changed from its reason alone. The exact API permissions needed to list or modify records depend on the selected provider and operation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Polling is an application decision, not a universal interval

The cited provider documentation establishes list operations, but it does not establish a universal polling cadence, delivery latency, pagination completeness, ordering, or retry guarantee. Before choosing a schedule, inspect the selected provider’s current API contract for pagination or cursors, timestamps, rate limits, retry behavior, and any event or webhook options. If you use polling, document the schedule as your application’s design choice and make the worker safe to rerun; do not present it as a provider guarantee.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What happens when a send targets a suppressed recipient?

Cloudflare’s behavior depends on its “Drop suppressed recipients” setting. With the setting off, Cloudflare documents a REST API 400 response, an E_RECIPIENT_SUPPRESSED exception from the Workers binding, and an SMTP rejection when a recipient is suppressed. With the setting on, suppressed recipients are removed and any remaining recipients are processed.

For a Node.js sender, this setting changes what the application must handle: an error outcome versus a send that proceeds for the unsuppressed recipients. Decide how to report partial-recipient outcomes and log them according to the provider’s actual response. Do not assume this Cloudflare setting or its behavior exists in another service.

How domain architecture affects reputation

Cloudflare describes deliverability as maintaining a sending reputation that inbox providers trust. It warns that high bounce rates or spam complaints can lead providers to flag a domain, send messages to spam, or block delivery. Its guidance emphasizes SPF, DKIM, DMARC, list hygiene, and separating marketing and transactional traffic across domains or subdomains. Separation can help keep higher marketing complaint rates from affecting transactional reputation, but suppression scope must be designed to match the chosen separation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cloudflare’s 2026 deliverability documentation lists “Delivery rate >95%,” “Hard bounce rate < 2%,” and “Complaint rate < 0.1%” as key-metric recommendations. These are Cloudflare’s stated guidance for its own product context, not universal standards or independently validated guarantees.

What differs across providers

Before implementing the integration, compare the provider’s current documentation on the operational details that determine whether reconciliation is safe. Amazon SES offers one documented example: its SuppressionOptions reference names COMPLAINT and hard-bounce BOUNCE as automatic suppression reasons and supports account or tenant scope. That reference is a scope and policy comparison, not a Node.js polling tutorial.

  • Scope: Determine whether entries apply to an account, tenant, domain, or exact subdomain.
  • Event classes and lifecycle: Check how complaints, hard bounces, soft bounces, and unsubscribes are represented, whether entries expire, and what removal requires.
  • Integration surface: Confirm whether you will use a Node.js SDK or REST API and which list, get, update, or delete operations it actually supports.
  • Reconciliation contract: Verify pagination or cursor behavior, rate limits, read-only fields, event delivery, and retry rules before relying on them.
  • Domain isolation: Map each sending domain or subdomain to the application streams that use it and verify how provider suppressions cross those boundaries.

Implementation checklist

  • Authenticate the custom sending domain using only the selected provider’s current DNS records.
  • Verify the provider’s sending records separately from any inbound routing records.
  • Model complaint, hard-bounce, soft-bounce, and unsubscribe status as distinct application states.
  • Reconcile provider records idempotently, retaining provider identifiers and timestamps where available.
  • Preserve provider suppressions when removing addresses from application contact lists, and honor read-only indicators before attempting changes.
  • Test the configured suppressed-recipient behavior so the Node.js sender handles rejection or partial recipient processing correctly.
  • Set any polling schedule only after reviewing the provider’s current API limits and pagination or event options.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.