Skip to main content
All Docs
FeaturesMaking Tax DigitalUpdated April 7, 2026

Notification API Endpoint Pinning (SCR-06)

Notification API Endpoint Pinning (SCR-06)

As of v1.0.480, the API base URLs used by the email, SMS, and WhatsApp notification modules are configurable via environment variables. This implements supply-chain risk control SCR-06.

Why this exists

Previously, the Resend and Twilio API URLs were hardcoded in the notification modules. Extracting them to environment variables enables three operational capabilities without requiring a code change or deployment:

  • Mock / local testing — point to a local mock server (e.g. http://localhost:8080) during development or integration testing.
  • Regional failover — redirect traffic to a geographically closer or higher-availability endpoint.
  • Emergency rerouting — switch delivery providers at runtime if an upstream provider has an outage.

Environment variables

RESEND_API_URL

PropertyValue
RequiredNo
Defaulthttps://api.resend.com/emails
Modulesrc/lib/notifications/email.ts

Overrides the endpoint used when sending transactional emails via Resend.

# .env.local — point to a local mock during development
RESEND_API_URL=http://localhost:8025/emails

TWILIO_API_BASE

PropertyValue
RequiredNo
Defaulthttps://api.twilio.com
Modulessrc/lib/notifications/sms.ts, src/lib/notifications/whatsapp.ts

Overrides the Twilio REST API base URL. This single variable controls both SMS and WhatsApp message delivery since both use the Twilio Messages API.

# .env.local — point to a Twilio region-specific host
TWILIO_API_BASE=https://api.twilio.com

Behaviour when unset

If either variable is not present in the environment, the module falls back to its production default (the previously hardcoded value). There is no change in behaviour for existing deployments.

Implementation detail

Each module exposes a private helper (getResendApiUrl() / getTwilioApiBase()) that is called once per request — not at module load time. This means the env var is read fresh on every invocation, so updates to the variable in a long-running process take effect without a restart.

// email.ts
const DEFAULT_RESEND_API_URL = "https://api.resend.com/emails";
function getResendApiUrl(): string {
  return process.env.RESEND_API_URL || DEFAULT_RESEND_API_URL;
}

// sms.ts & whatsapp.ts
const DEFAULT_TWILIO_API_BASE = "https://api.twilio.com";
function getTwilioApiBase(): string {
  return process.env.TWILIO_API_BASE || DEFAULT_TWILIO_API_BASE;
}

Inngest function config lint

Also introduced in this release is scripts/lint-inngest.ts, a CI script that enforces consistent Inngest function configuration across the codebase.

What it checks

The script scans every .ts / .js file in src/inngest/functions/ and verifies that any file containing a createFunction call also defines:

  • retries: — explicit retry count
  • concurrency: — explicit concurrency limit

Why it matters

Functions without concurrency limits can fan out uncontrollably under load. Functions without explicit retry counts inherit Inngest's platform default, making behaviour harder to reason about in production.

Running the lint locally

npx ts-node scripts/lint-inngest.ts

Exit code 0 = all functions compliant. Exit code 1 = one or more functions are missing required config, with specific file paths and missing fields listed in stderr.

Example output (failure)

[lint-inngest] ❌ Inngest function lint failures:

  src/inngest/functions/my-function.ts — missing: concurrency

Every inngest.createFunction() call must include explicit `retries` and `concurrency` config.