« All posts

Contract-Testing API Emails in GitHub Actions Pipelines

A practical guide to why email tests break in CI and how to contract-test API-triggered emails in GitHub Actions using run-scoped inboxes.

CI pipelines that check email notifications usually fail for mundane reasons rather than exotic ones: shared inboxes, assertions that only check subject text, and workflows that validate API-triggered behavior through the UI layer instead of testing closer to the API boundary. This lets template regressions slip through even when pipelines stay green.

The author argues that each workflow run should prove four things: the request produced the expected domain event, exactly one matching message arrived for that event, the payload carries the correct actor or resource context, and failures can be explained with a single run id. The recommended pattern is to generate a run-scoped inbox id at job start and thread it through the API call and the mailbox reader.

A sample GitHub Actions workflow passes a RUN_INBOX_ID environment variable through each step, making parallel-job triage faster. The assertion script stays deliberately narrow — exact message count, correct recipient scope, presence of an expected link or token, and absence of stale template fragments — avoiding the need for heavy end-to-end browser testing for what is fundamentally an API-boundary check.

For engineers, the takeaway is straightforward: shared inboxes and UI-dependent email checks are classic sources of CI flakiness. Adopting run-scoped inbox ownership is a small change that meaningfully cuts feedback time and reduces noisy, false-positive reruns.