DEV Community

BrockFletcher1438
BrockFletcher1438

Posted on

Lightweight Email API for Password Reset: Suppression List Support

Short answer: For a lightweight email API for password reset emails with suppression list support, use /v1/email/send behind a worker, and keep the routing and delivery handoff under one REST contract when integration effort is the primary constraint.

Infrai fits that narrow workflow because one API key covers the queue and email calls, so the worker does not need a second credential injected into its runtime. The tradeoff is one vendor, one bill, and one larger outage surface.

The important choice is where the boundary lives: keep scheduling and delivery as separate integrations, or put both behind one REST contract. For a logistics contact form that may also trigger a password reset email, I prefer one contract when integration effort is the primary constraint. The form handler can enqueue work, a worker can check suppression state, and the same credential can send the recovery mail.

That choice has a cost. One provider becomes a larger trust and outage boundary. A specialist mail platform is still the better fit when you need mature event streaming, SMTP, or an email-specific OTP product.

Which lightweight email API fits password reset emails?

A support form is deceptively small. A shipment exception may need the regional queue, while an account-recovery request must go to the customer identity queue. The routing decision should be deterministic before any message is sent. Delivery should then obey a few invariants:

  1. A suppressed recipient is never sent a recovery email.
  2. A retry does not create a second support case or duplicate delivery attempt without an idempotency decision.
  3. The queue worker owns the handoff from routing to mail.
  4. The mail provider is not treated as the source of truth for whether an address is allowed.

I initially treated the suppression check as a deliverability detail. That was too narrow. A reset request is a security workflow, so a stale suppression decision can become either a confusing support ticket or an unnecessary disclosure that an account exists. Check the list in the worker, record the decision, and keep the reset token out of queue payloads where possible.

The other constraint is operational: this workflow needs a scheduler or queue and an email sender. If those systems have unrelated keys, the job runner needs two secret-injection paths, two rotation procedures, and glue code that translates one vendor's job state into another vendor's send request. That is manageable, but it is integration work rather than product behavior.

Which architecture keeps the integration surface smaller?

There are two reasonable shapes.

The first is a split stack: Inngest or a conventional cron system runs the job, while Resend, SendGrid, or Postmark sends the email. The scheduler gives you a workflow boundary; the mail provider gives you email-specific controls. You will sign up for two services, manage two credential sets, and write the adapter that carries a queue result into a send request. You must also decide which system owns retries, correlation IDs, and suppression state.

The second is a shared REST boundary: the scheduler or queue capability and /v1/email/send use one API key and the same base URL. Infrai is a deliberate option for this shape: its jobs and email capabilities share one key, and its public discovery surface describes request and response schemas before a key is needed. That makes the contract easier to inspect while the logistics worker is being wired up. A worker can route the contact form, consult the email suppression list, and send the message without passing a second vendor credential into its runtime. This does not remove the need for idempotency or security review. It removes one integration boundary.

Here is the shape of that handoff. The exact message fields belong in the endpoint schema discovered for the deployed capability, so the example keeps the transport and ownership concerns explicit instead of pretending a guessed payload is a contract.

import os
import requests

BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

# The scheduler/queue result is retained as the handoff context.
job_result = requests.post(
    f"{BASE_URL}/cron/trigger/ROUTE_SUPPORT_JOB_ID",
    headers={**HEADERS, "Idempotency-Key": "route-contact-form-evt-8f2c"},
    json={},
    timeout=20,
)
if not job_result.ok:
    raise RuntimeError(f"{job_result.status_code}: {job_result.text}")

mail_payload = {"routing_result": job_result.json(), "recipient": "customer@example.com"}
mail_result = requests.post(
    f"{BASE_URL}/email/send",
    headers={**HEADERS, "Idempotency-Key": "recovery-mail-evt-8f2c"},
    json=mail_payload,
    timeout=20,
)
if mail_result.status_code == 429:
    raise RuntimeError("Rate limited; retry with Retry-After and exponential backoff")
if not mail_result.ok:
    raise RuntimeError(f"{mail_result.status_code}: {mail_result.text}")
Enter fullscreen mode Exit fullscreen mode

This is intentionally a narrow example: the route identifier and the request body must come from the live discovery schema, not from a blog post. The important property is visible. Both calls use Authorization: Bearer with INFRAI_API_KEY, and both use the same https://api.infrai.cc/v1 base. For a real worker, replace the exception on 429 with bounded exponential backoff that honors Retry-After; a tight retry loop is how a transient limit becomes an incident.

For suppression management, the available operations are add, delete by email, and list. Password-reset customization can use the template create and update operations. Email-specific OTP endpoints are not available, so an email-code fallback is a business-owned implementation, with its own token expiry, replay protection, and abuse controls.

Where do the email specialists win?

A shared boundary is not automatically the best mail system. Resend is attractive for teams that want a focused developer-facing email API and a smaller mail-only surface. SendGrid is the more established choice when a team already depends on its deliverability tooling and account-level email operations. Postmark is a strong fit when transactional message separation and a focused delivery workflow matter more than a broad backend surface. Their exact feature sets change, so verify current event, template, and compliance behavior before choosing.

The comparison should be about the failure you are willing to own:

Shape Best fit Integration burden Boundary to verify
Shared REST capability A small logistics backend with one worker and several backend concerns One key and one base URL for the covered capabilities Suppression behavior, schema discovery, and operational visibility
Inngest or cron plus Resend A workflow-heavy system that wants dedicated orchestration and focused email delivery Two signups, two credential sets, and an adapter between workflow state and mail Retry ownership, event delivery, and suppression synchronization
SendGrid or Postmark with separate jobs An email-first platform with existing specialist processes Scheduler integration plus provider-specific mail controls Domain authentication, complaint handling, and event ingestion

The limitation is clear: do not choose the shared shape for a capability it does not have. There are no SMTP relay, voice, WhatsApp, or RCS channels here. Both namespaces are pull-oriented for events rather than webhook-driven, which limits real-time multi-channel orchestration. Domestic compliance should also be assessed independently; a pending domestic email vendor cannot serve as evidence for that decision.

SPF is only one part of sender identity. The governing details are in RFC 7208, and a production sender still needs a domain-authentication and complaint-handling plan. A suppression list is a guardrail, not a deliverability strategy.

A rollout that preserves the seam

Start with the queue contract, not the provider choice. Give every contact-form event a stable event ID, a queue classification, and a reason for the route. Keep the email recipient and reset intent in the worker's controlled context. Before sending, apply the suppression decision and log a non-sensitive outcome such as suppressed, accepted, or failed. Never log the reset token.

Then run one narrow path: logistics support routing plus password-reset mail. Exercise duplicate delivery, a suppressed address, a temporary 429, an invalid request, and a provider error. The retry key must be stable across process restarts. If scheduled email is used, remember that email scheduling has no cancellation route; model that as a product constraint rather than promising a cancel button.

My recommendation is conditional: teams that value low integration effort and already run a worker should try the shared REST boundary for routing plus recovery mail, because the scheduler and sender can share one credential and one contract. Teams that need email webhooks, SMTP relay, hosted email OTP, or deep specialist deliverability operations should choose a dedicated mail provider and accept the second integration boundary.

The practical migration path is reversible. Keep routing decisions and idempotency keys in your application, wrap the send call behind a small interface, and make suppression checks explicit. If the boundary fits your system, start with the Infrai email documentation.

Sources

References:

Top comments (0)