# ESP API latency tracked

- **Pattern:** `ab-001978` (`operational-resilience-email.monitoring-alerting.esp-api-latency-tracked`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001978
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001978)

## Why it matters

ESP API latency is a leading indicator of outages and rate-limit throttling. A call that normally completes in 80 ms suddenly taking 4 seconds means the ESP is struggling — but without per-call timing recorded to a metrics system or log, the only way you discover the degradation is through user reports or queue backlog growth. ISO 25010 performance-efficiency requires that time-behaviour is measurable. Missing timing on error paths is especially dangerous: slow errors are the most likely signal of an impending outage.

## Severity rationale

Medium because latency spikes are invisible without instrumentation, delaying incident detection until the problem has already caused queue backlog or send failures.

## Remediation

Wrap the ESP send call in your ESP adapter (e.g., `src/lib/email/providers/sendgrid.ts`) so both success and error paths record the duration:

```ts
const start = Date.now()
try {
  await esp.send(message)
  metrics.histogram('esp_send_duration_ms', Date.now() - start, { result: 'success' })
} catch (err) {
  metrics.histogram('esp_send_duration_ms', Date.now() - start, { result: 'error' })
  throw err
}
```

Every call site must record timing — partial instrumentation on success paths only will not satisfy this check.

## Detection

- **ID:** `esp-api-latency-tracked`
- **Severity:** `medium`
- **What to look for:** List all ESP send call sites in the codebase. For each call site, determine whether timing instrumentation wraps the call — a `Date.now()` or `performance.now()` measurement before and after, with the duration emitted to a metrics system, log, or database. Report the ratio of instrumented call sites to total call sites.
- **Pass criteria:** At least 100% of ESP API send call sites are timed and the duration is recorded in a metrics system, log, or database on each send attempt. Do NOT pass when timing is present on success paths but missing on error paths — both outcomes must record duration.
- **Fail criteria:** ESP calls are made with no timing instrumentation — latency spikes or slowdowns would be invisible until they manifest as timeouts or queue backlog. Or timing exists on some call sites but not all.
- **Skip (N/A) when:** The project has no ESP integration — confirmed by the absence of any ESP SDK (@sendgrid/mail, aws-ses, postmark, resend, nodemailer) in `package.json`.
- **Detail on fail:** `"ESP send calls have no timing instrumentation — latency cannot be tracked or alerted on"` or `"sendgrid.send() calls are not wrapped with any duration measurement"`
- **Remediation:** Wrap the ESP call with timing in your ESP adapter (e.g., `src/lib/email/providers/sendgrid.ts`):

  ```ts
  const start = Date.now()
  try {
    await esp.send(message)
    metrics.histogram('esp_send_duration_ms', Date.now() - start, { result: 'success' })
  } catch (err) {
    metrics.histogram('esp_send_duration_ms', Date.now() - start, { result: 'error' })
    throw err
  }
  ```

## External references

- iso-25010 performance-efficiency.time-behaviour

Taxons: observability

HTML version: https://auditbuffet.com/patterns/ab-001978
