Skip to main content
All Docs
FeaturesMaking Tax DigitalUpdated March 26, 2026

Building Resilient Email Notifications for MTD Deadline Alerts

Building Resilient Email Notifications for MTD Deadline Alerts

Release v1.0.452 · SCR-04: API Error Handling & Circuit Breaking


The Problem

Email notifications are a critical part of the Making Tax Digital compliance workflow. When a quarterly HMRC submission deadline is approaching, the platform sends time-sensitive alerts to landlords and self-employed taxpayers so they can review and submit their figures before the HMRC cutoff.

Until this release, the Resend email client used a bare fetch() call with no safeguards:

// Before — no timeout, no retries, no circuit breaker
const response = await fetch('https://api.resend.com/emails', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
  body: JSON.stringify(payload),
});

This meant that if the Resend API was slow or unresponsive, the call would simply hang — blocking the Inngest critical-notification-email function until the Inngest or Vercel platform timeout was reached. No error would surface, no retry would occur, and the user would silently not receive their deadline alert.

During peak deadline periods — the end of each MTD quarterly period — this was an unacceptable compliance risk.


What Changed in v1.0.452

Three layers of resilience have been added to the Resend email client in src/lib/notifications/email.ts.

1. Hard Timeout via AbortController

Every Resend API call is now wrapped in fetchWithTimeout() with a 10-second limit:

const TIMEOUT_MS = {
  RESEND: 10_000, // 10 seconds
};

const response = await fetchWithTimeout(
  'https://api.resend.com/emails',
  requestOptions,
  TIMEOUT_MS.RESEND
);

If the Resend API does not respond within 10 seconds, the request is aborted via AbortController and an error is thrown immediately. The Inngest function can then handle that error, log it, and move on — rather than sitting idle for minutes.

2. Exponential-Backoff Retries

Transient failures (brief network hiccups, occasional 5xx responses from Resend) are handled automatically by fetchWithRetry() with 2 retry attempts:

const response = await fetchWithRetry(
  () => fetchWithTimeout(url, requestOptions, TIMEOUT_MS.RESEND),
  { retries: 2 }
);

Retries use exponential backoff, so a second attempt doesn't immediately hammer an API that's already under pressure. The two-attempt limit keeps total latency bounded — important when the Inngest function has its own execution budget.

3. Circuit Breaker

A circuit breaker has been introduced specifically for the Resend integration, configured with a lenient threshold:

  • Failure threshold: 10 failures within 60 seconds
  • Behaviour when open: calls fail fast immediately, no HTTP request is made
  • Recovery: the circuit moves to half-open after the window expires and tests with a single probe request

The threshold is deliberately lenient because email delivery is best-effort by nature — the circuit breaker's purpose is to detect and surface sustained outages, not to shed load aggressively. When the circuit is open, the Inngest function can log the state, raise an alert, and avoid piling up hanging requests against an API that is clearly unavailable.


Failure Modes Addressed

ScenarioBeforeAfter
Resend API hangs with no responseBlocks until platform timeout (~30s+)Aborts after 10 seconds
Transient 5xx from ResendEmail not sent, no retryAutomatically retried up to 2×
Sustained Resend outageRepeated slow/hanging calls for every emailCircuit opens after 10 failures; calls fail fast
Silent delivery failure during HMRC deadlineNo visibility, user misses alertErrors surface and are logged; circuit state is observable

Why This Matters for MTD Compliance

Under Making Tax Digital for Income Tax Self Assessment (MTD ITSA), landlords and self-employed taxpayers must submit quarterly updates to HMRC within defined windows. Missing a deadline can result in penalty points accumulating toward a financial penalty.

The platform's deadline-alert emails are part of the compliance safety net. Silently losing those emails during an email-provider outage — precisely when submission activity is highest and provider load may also be elevated — is a scenario the platform must guard against.

With v1.0.452, the email notification layer is now:

  • Bounded — no call can block the worker indefinitely
  • Self-healing — transient failures are retried automatically
  • Observable — sustained outages open the circuit and produce visible errors rather than silent hangs

This change is part of the SCR-04 supply chain controls audit, which reviews all external API integrations for missing timeouts, retry logic, and circuit breakers.