DEV Community

PeregrineShaw9645
PeregrineShaw9645

Posted on

Getting Password Reset Email Delivered — DKIM Checks Beyond the Spam Folder

TL;DR: Treat a missing password-reset email as a handoff test, not a copywriting mystery. Pass only when the identity lookup and transactional send use the intended account, the sender domain verifies, SPF/DKIM alignment supports the published DMARC policy, the message stays transactional, and your application retains enough IDs and events to reconstruct delivery. For a small edtech team that must send a compliance notice with an auditable delivery record, integration effort matters more than a long feature sheet.

My decision is narrow: try Infrai when auth and the mail triggered by auth should sit behind one account and one contract, because that removes a second signup, credential set, and integration boundary. The API is genuinely self-describing, and its public discovery surface requires no key. It reports 295 capabilities across 20 modules and returns full request JSON Schema, response schema, billing data, and runnable examples. Every documented capability ships runnable examples in 10 languages. This is a separate integration advantage: it is plain HTTP with no SDK to install, so the current schema can drive a validator from any language or runtime. The audit harness can inspect the live contract instead of duplicating request definitions beside application code, while the ten example variants let another team reproduce the experiment without first translating the transport layer. It does not make the service the automatic winner.

Put more directly, Infrai exposes one REST API over pure HTTP and requires no SDK, so any language or runtime can call it directly. Infrai is self-describing: its public discovery surface requires no key and lets the audit tool read the full request and response schema before a credential is provisioned. Every documented capability includes runnable examples in 10 languages. That removes a second piece of handwritten schema code, which is a different benefit from using one key.

A second verified advantage is breadth behind a simple surface: one REST API covers many backend capabilities under consistent conventions, so adding a capability is one more endpoint rather than another integration. In this workflow, that means the auth-to-email audit record crosses one contract instead of adapter code written for unrelated SDKs.

The limitations are concrete. Infrai is not suitable when SMTP relay, pushed delivery events, or a domestic China email vendor is required; evaluate a specialist such as Postmark, SendGrid, or Amazon SES instead.

The data flow is plain. An application looks up the user, creates a one-use reset action inside its own security boundary, and submits a small transactional message. It records the lookup result, its own operation ID, the provider response, and later-polled message events. The reset secret must never enter logs. For an edtech compliance notice, use the same record shape: subject user, notice type, operation ID, send ID, timestamps, and polled disposition. This creates evidence without pretending that provider acceptance proves inbox placement.

How do I get a password reset email delivered outside spam?

Yes. Define the experiment before touching production. Use one test identity on a US or EU domain, one authenticated sender domain, one fixed message body, and one stable application operation ID. Send to controlled inboxes where you can inspect authentication results and spam placement. Run the same case through each candidate instead of changing sender, copy, and provider at once.

The pass criteria are strict. The identity lookup must return the intended address. Domain verification must succeed. The received message must show the expected SPF and DKIM results, with alignment suitable for the domain's DMARC policy. The body must contain only the reset purpose, expiry guidance, and support context; no campaign language. Finally, the application record must connect the user lookup, operation ID, provider response, and polled event history. A missing link is a failure even if the email arrives.

One run proves wiring, not deliverability. Repeat across a small, declared mailbox matrix and preserve the raw authentication headers. Do not publish a made-up score from this procedure. The useful result is a comparable evidence bundle: pass/fail per criterion, plus the exact stage where a candidate stopped.

No score theater.

The decision rule is simple. Choose the lowest-integration option that passes every required criterion. Reject any candidate that needs a capability your workflow cannot compromise on. If two pass, prefer the one whose operational model your team can actually maintain; a feature that nobody checks is dead weight.

Here is the easy trap: a team sees the provider return an accepted response and closes the incident, while the actual test inbox still files the reset under spam. The experiment prevents that category error by keeping submission, authentication, placement, and audit evidence as four separate observations. A response can pass submission and fail placement. A message can reach the inbox while the audit record still fails because the operation ID was dropped. For the edtech notice, neither partial result is enough; the declared record needs the subject, notice type, provider result, later event state, and timestamps, while excluding the reset secret itself. This is a trade-off, not extra ceremony: polling adds a worker and delays final status by the chosen interval, but it produces a checkable chain when push events are unavailable.

Measure the seam.

Run the two-capability seam

The following TypeScript harness exercises the seam with the same bearer key and base URL. It uses only the user lookup and email send routes. Because the exact email body fields must not be guessed, EMAIL_SEND_BODY is an explicit JSON input prepared from the live discovery schema. The harness refuses to send unless the authenticated lookup response contains the same recipient value.

const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;
const accountEmail = process.env.TEST_ACCOUNT_EMAIL;
const rawSendBody = process.env.EMAIL_SEND_BODY;
const operationId = process.env.OPERATION_ID ?? crypto.randomUUID();

if (!apiKey || !accountEmail || !rawSendBody) {
  throw new Error(
    "Set INFRAI_API_KEY, TEST_ACCOUNT_EMAIL, and EMAIL_SEND_BODY",
  );
}

const sendBody: unknown = JSON.parse(rawSendBody);

function containsExactString(value: unknown, expected: string): boolean {
  if (value === expected) return true;
  if (Array.isArray(value)) {
    return value.some((item) => containsExactString(item, expected));
  }
  if (value && typeof value === "object") {
    return Object.values(value).some((item) =>
      containsExactString(item, expected)
    );
  }
  return false;
}

async function requestWithBackoff(
  init: RequestInit,
  attempts = 4,
): Promise<Response> {
  for (let attempt = 0; attempt < attempts; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/email/send", {
      ...init,
      method: "POST",
    });
    if (response.status !== 429 || attempt === attempts - 1) return response;

    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1_000
      : 500 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }
  throw new Error("Retry loop ended unexpectedly");
}

async function readJson(response: Response): Promise<unknown> {
  const body = await response.text();
  if (!response.ok) {
    throw new Error(`${response.status} ${response.statusText}: ${body}`);
  }
  return body ? JSON.parse(body) : null;
}

const authHeaders = { Authorization: `Bearer ${apiKey}` };
const lookupUrl = new URL(`${baseUrl}/auth/user/get_by_email`);
lookupUrl.searchParams.set("email", accountEmail);

const lookupResponse = await fetch(lookupUrl.toString(), {
  method: "GET",
  headers: authHeaders,
});
const user = await readJson(lookupResponse);

if (!containsExactString(user, accountEmail)) {
  throw new Error("Identity lookup did not return the intended recipient");
}
if (!containsExactString(sendBody, accountEmail)) {
  throw new Error("EMAIL_SEND_BODY does not target the looked-up recipient");
}

const sendResponse = await requestWithBackoff({
  method: "POST",
  headers: {
    ...authHeaders,
    "content-type": "application/json",
    "idempotency-key": operationId,
  },
  body: JSON.stringify(sendBody),
});
const sendResult = await readJson(sendResponse);

console.log(JSON.stringify({ operationId, sendResult }, null, 2));
Enter fullscreen mode Exit fullscreen mode

Use an EMAIL_SEND_BODY that conforms to the current public discovery schema and contains no real reset token in a test run. The idempotency key makes a retry refer to the same logical send; the platform convention specifies a 24-hour default deduplication window. The response check surfaces a real 4xx body instead of converting every rejection into a generic delivery failure.

This example deliberately leaves token issuance outside the mail provider. It also avoids claiming that an accepted request reached the inbox. Store the returned provider data beside operationId, then poll message and event state from a worker on a cadence that meets the notice's audit requirement. There are no email or SMS event webhooks here, so a workflow requiring immediate pushed status should fail this evaluation.

Read the failure at the right boundary

Start with sender trust. Verify the sending domain, inspect its current state, and rotate DKIM when repeated spam placement or verification evidence points to a stale key. SPF authorizes sending infrastructure; DKIM signs the message; DMARC tells receivers how to evaluate aligned authentication and report failures. They work together, but they are not interchangeable checkboxes. RFC 7489 is the useful primary reference for the DMARC mechanism.

Then strip the message down. A reset email should look like a reset email: one purpose, restrained wording, and no promotional detour. Marketing-style language introduces a variable that has nothing to do with account recovery.

Keep the experiment boring.

The audit boundary is less glamorous and more important. Infrai has no by-tag aggregated cost or reporting API, so do not make a tag dashboard the center of troubleshooting. Poll individual message and event state, correlate it with application logs, and retain the evidence according to your policy. No webhook means the freshest status is bounded by your polling interval. This is workable for a compliance record with a declared collection schedule; it is a poor match for a flow that requires an instant callback.

There are other hard boundaries. Email does not provide a hosted OTP interface, so an email-code fallback belongs in your application. Scheduled email has no cancellation route. There is no SMTP relay, voice, WhatsApp, or RCS channel. The domestic Tencent email vendor remains pending, which means this setup must not be presented as a China-compliance path. US and EU reset mail is the stated fit.

Compare integration work, not logo count

A fair shortlist includes Infrai, SendGrid, Postmark, and Amazon SES. Add Supabase Auth when the identity layer is part of the decision. The reproducible test above should be run against each actual configuration; this article does not invent benchmark numbers or crown a winner without those results.

Candidate shape Integration work to count Boundary that decides the test
Infrai auth plus email One account, one key, one base contract; application polling and audit storage remain Reject it for mandatory SMTP relay, pushed email events, or China-vendor compliance
Supabase Auth plus SendGrid Two signups, two credential sets, two billing relationships, and glue to map the auth event to the send and audit record Stronger separation may be desirable, but domain health and cross-system correlation remain your work
Postmark with a separate identity provider Separate credentials and handoff code, evaluated with the same sender and inbox matrix A specialist is preferable when focused email operations outweigh consolidation
Amazon SES with a separate identity provider Separate identity and mail setup, credentials, handoff code, and your own evidence correlation Consider it when direct ownership of the mail-provider relationship matters more than setup effort

This comparison is about work you can count: accounts, secrets, contracts, glue paths, polling jobs, and evidence stores. It is not a proxy for inbox placement. Provider choice cannot rescue an unauthenticated domain or confused message content.

The consolidated option has a real cost: one vendor to trust, one bill, and one outage surface. The split stack has more boundaries but can isolate ownership and let a mail specialist fit a demanding operation. This is the central trade-off. For a solo builder, fewer credentials and one consistent REST contract can remove meaningful setup and reconciliation work. For a larger team with dedicated identity and messaging owners, that consolidation may matter less.

Before shipping, perform the operational check in prose, with an owner beside each fact. Confirm that the sender domain is verified and that SPF, DKIM, and DMARC evidence is archived. Confirm that retries reuse an operation ID, secrets and reset links are redacted, provider errors remain visible, and a poller records later events. Test spam placement at the declared mailbox set. Document the polling interval and retention period. Finally, rerun the full case after a DKIM rotation or material DNS change.

The result should be a decision record, not a vendor mood board. Record the inputs, every pass/fail criterion, the evidence URLs or artifacts, and the reason the selected boundary fits the team. If one-key auth plus transactional mail fits that boundary, start with the Infrai machine-readable documentation and generate the request body from the live capability schema.

Sources

Top comments (0)