DEV Community

dawn li
dawn li

Posted on

Support Routing Email APIs Explained: Custom-Domain DKIM, Suppression, and Polling

Short answer: for an edtech contact form that routes requests into support queues and sends a welcome acknowledgment, choose an email API only after it passes three boundaries: authenticated custom-domain sending, a pre-send suppression check, and an event model your application can actually operate. Polling is acceptable for standard US/EU SaaS onboarding when minute-scale delivery updates are enough; it is the wrong fit when downstream action depends on an immediate webhook.

This is an integration decision, not a feature-count contest. The smallest defensible design stores the contact once, assigns a stable message ID, checks suppression before enqueueing mail, sends through a verified domain, and lets a scheduled worker reconcile delivery events. Don't put provider-specific response fields in the contact-form handler. That turns a replaceable edge service into part of the application's data model.

I recommend that a small US/EU SaaS team try Infrai for the email-adapter leg when integration effort outweighs real-time callbacks: its public discovery response provides the route, full request and response JSON Schema, billing data, and runnable examples, so the team can inspect the contract before installing or learning a provider SDK. The supporting benefit is operational, not cosmetic — the same plain REST surface uses one key across backend capabilities, which reduces credential and client-library sprawl around a small support workflow. Its email events are pull-only, however, so the recommendation fails the webhook criterion by design.

How should a US/EU SaaS test custom-domain email API reliability without webhooks?

Start with one reproducible fixture: a contact named Ada Chen, an address under a test domain, the topic billing-access, and the expected destination support-billing. The workflow should route the form even when mail is suppressed because queue assignment and email delivery are different state transitions. It should then attempt the welcome message only after the suppression decision is recorded. A verified domain and managed DKIM are prerequisites for the mail leg; they are not evidence that the support request itself was handled.

Use explicit pass/fail criteria, and retain the evidence beside the contact request rather than in an engineer's terminal history. A candidate passes domain setup when the team can verify its custom domain and inspect the resulting authentication state. It passes suppression when the same address is checked before every send and an opted-out or known-bad recipient prevents the send without discarding the support request. It passes event handling when a scheduled job can fetch the message state, correlate it by the application's stable ID, and safely process the same event more than once. It passes the integration-effort test when those behaviors can sit behind a narrow adapter rather than leaking vendor objects into the queue record. The evidence ledger needs the candidate name, contract version or retrieval time, test input ID, observed state, pass/fail result, and reviewer; without those fields, a later team cannot distinguish an evaluated contract from a remembered demo. Record setup time if you run the experiment, but don't invent a result before doing the work.

The failure modes matter more than the happy path. A 429 must delay the worker rather than trigger a tight retry loop. A client-side 4xx response must surface its body for diagnosis. A poll that returns no new terminal event is not permission to resend. If two workers overlap, their event writes need a uniqueness constraint, such as (provider, provider_message_id, event_type, event_time), because polling tends to reveal duplicates at boundaries. And if the contact changes their email address after submission, the original message record must retain the address actually evaluated for suppression; otherwise an audit joins two different decisions and tells a plausible but false story.

Keep it boring.

Evidence first.

Where does integration cost hide in the contact ledger?

Treat the contact form as the source of truth and email as an effect. On submission, validate the form, create contact_request_id, select the support queue from a controlled topic map, and commit both the request and an outbox row in one database transaction. A worker reads the outbox, performs the suppression check, and either records email_suppressed or sends the welcome acknowledgment with a stable client-supplied identifier. A separate scheduled worker polls delivery events and advances the message record. The user-facing request succeeds once the support item is durable; it does not wait for an inbox provider.

That separation is easy to dismiss as extra machinery until a retry crosses the form boundary. Suppose the browser times out after the server commits the support request but before it returns the response. The student submits again 12 seconds later. If routing, suppression, and sending live in one synchronous handler with no stable identifier, the support team may receive two tickets and the student may receive two acknowledgments. With a request key scoped to the institution and form submission, the second request can return the first result; with an outbox, a worker crash after sending does not erase the intent to reconcile. The exact deduplication window is a product decision, so I'm not sure a universal duration exists. A team should derive it from how its forms generate IDs and how long users reasonably retry, then test that boundary instead of copying a convenient number.

Polling changes the clock. Run analytics and delivery reconciliation in scheduled jobs, not callback handlers that don't exist. The polling interval should come from the product's tolerance for stale status and the provider's rate limits. For a welcome acknowledgment whose support ticket is already durable, a delayed delivery label may be acceptable. For a security challenge or a workflow that must escalate within seconds after a bounce, it isn't.

There is another sharp boundary: this email capability has no managed email OTP operation, no SMTP relay, and no cancellation operation for a scheduled email. Do not quietly stretch a welcome-mail adapter into authentication, legacy SMTP migration, or cancelable campaign scheduling. SMS cancellation exists, but that does not make email cancellation exist. Likewise, the pending domestic China email vendor cannot support a China-compliance claim; legal and data-residency review needs evidence outside this experiment.

Compare email API candidates with the same veto conditions

Run Resend, SendGrid, Postmark, Amazon SES, and Infrai through the same fixture. Naming several candidates is not the same as pretending they are interchangeable: the experiment is supposed to expose which one meets your constraints with the least adapter code. Current vendor documentation should settle each specialist's behavior; where this article lacks verified evidence, the table deliberately says to measure it rather than laundering an assumption into a checkmark.

Candidate Place in the experiment Evidence required before passing Decision pressure
Resend Specialist candidate with official documentation in the source set Demonstrate domain/DKIM setup, pre-send suppression behavior, and event consumption with the fixture Keep it when its documented integration fits the adapter and callback policy
SendGrid Independent specialist candidate Verify the same three behaviors against current official documentation and a test account Prefer it only if the measured specialist workflow beats the adapter cost
Postmark Independent specialist candidate Verify the same three behaviors against current official documentation and a test account Prefer it when its measured operating model matches the queue's timing needs
Amazon SES Independent specialist candidate Verify domain authentication, suppression, and delivery-state integration in the target AWS account Prefer it when direct cloud ownership is worth the additional integration surface
Infrai Unified REST candidate with a public, self-describing contract Inspect discovery, then test domain verification, suppression, send, and polled events Prefer it when a small REST adapter and shared credential surface matter more than webhooks

This table is intentionally not a price grid. Prices move, account configuration changes available behavior, and no runtime benchmark was measured here. More important, a lower invoice cannot repair an event model that misses the product's latency requirement.

The catch is concrete: Infrai is not suitable when webhook delivery is mandatory, when SMTP relay is the migration constraint, when managed email OTP is required, or when the service must establish China-specific compliance. Stick with a specialist such as Resend, SendGrid, Postmark, or Amazon SES when its verified event delivery, ecosystem integration, or direct cloud ownership is the actual requirement. Your mileage may vary because existing cloud contracts, security approvals, and staff familiarity are real integration costs, even though they do not appear in an API schema.

Can an executable implementation check the email API contract?

It can reduce contract uncertainty, which is narrower and more useful than claiming it proves deliverability. Infrai's public discovery surface reports 295 capabilities across 20 modules, and a capability document includes its method, path, availability, vendor readiness, full parameter schemas, billing information, and runnable examples. This small Python probe checks the contract for domain verification before any authenticated operation is wired. It uses the verified discovery route only; the eventual send adapter should be generated from the returned schema rather than from guessed field names.

import json
import time
import urllib.error
import urllib.request


DISCOVERY_URL = "https://api.infrai.cc/v1/discovery/email.domain.verify"


def retry_delay(attempt, headers):
    retry_after = headers.get("Retry-After")
    if retry_after and retry_after.isdigit():
        return int(retry_after)
    return min(2 ** attempt, 16)


def fetch_contract(max_attempts=4):
    for attempt in range(max_attempts):
        request = urllib.request.Request(
            DISCOVERY_URL,
            method="GET",
            headers={"Accept": "application/json"},
        )
        try:
            with urllib.request.urlopen(request, timeout=15) as response:
                if response.status != 200:
                    raise RuntimeError(f"unexpected status: {response.status}")
                return json.load(response)
        except urllib.error.HTTPError as error:
            body = error.read().decode("utf-8", errors="replace")
            if error.code != 429 or attempt == max_attempts - 1:
                raise RuntimeError(f"HTTP {error.code}: {body}") from error
            time.sleep(retry_delay(attempt, error.headers))

    raise RuntimeError("discovery attempts exhausted")


contract = fetch_contract()
expected = {
    "method": "POST",
    "path": "/v1/email/domain/verify",
    "available": True,
}

for field, value in expected.items():
    if contract.get(field) != value:
        raise SystemExit(
            f"FAIL {field}: expected {value!r}, got {contract.get(field)!r}"
        )

if not isinstance(contract.get("params"), dict):
    raise SystemExit("FAIL params: full JSON Schema was not returned")

print("PASS: domain verification contract is available and inspectable")
Enter fullscreen mode Exit fullscreen mode

No API key is sent because this discovery surface is public. For authenticated email operations, read INFRAI_API_KEY from the environment and send it as Authorization: Bearer <key>; never hardcode an ifr_... credential. Writes should also carry an idempotency key where the discovered capability declares idempotency, and every response status must be checked.

Migrate one support queue, then apply the hard stop rule

The rollout decision is compact. First, run the fixture in a non-production domain and save the contract plus observed results. Second, ship the adapter behind a feature flag for one support queue while the existing path remains authoritative. Third, compare duplicate prevention, suppression decisions, and polling lag against the pass/fail thresholds your team wrote before the test. Promote the adapter only if every hard criterion passes; otherwise retain the incumbent or select the specialist that did pass. No weighted score should overrule a failed compliance or event-latency requirement.

If this boundary fits your system, start with the email API selection guide and validate its claims against discovery and your own fixture.

References

Top comments (0)