# A real work domain is refused as a personal or throwaway email

> **Never executed in prod.** Written 2026-10-29 with the business-email rule (ENG-3589). The
> commands below have not been run for a real report.

## Symptoms

- A customer reports one of these for an address at their own company's domain:
  - at sign-up: `Sign up with your work email address. Personal email addresses can't be used.`,
    or `This email address can't be used. Sign up with your work email address.`
  - inviting a colleague, or an admin inviting them: `You can't invite personal email addresses.`
  - on the application: `Sign up with your work email address to apply.`
- Continue with Google is refused the same way for a new login at that domain.

## Confirm

From the repo root, ask the rule about the domain:

```bash
bun -e 'import { businessEmailRefusal } from "./packages/email/src/index.ts"; console.log(businessEmailRefusal("x@<domain>"))'
```

- `personal` or `disposable`: one of the pinned lists (`free-email-domains`,
  `disposable-email-domains-js`) or `packages/email/src/relay-domains.ts` holds the domain or a
  parent of it. This is the scenario.
- `null`: the lists pass it. A disposable refusal at sign-up then came from the SES email insights
  check, which this runbook cannot override; the person can be invited by a colleague instead,
  since invites skip SES.

Before allowing it, check the domain is the customer's business: a website, MX records pointing at
a company mail host, the domain on their application.

## Fix

1. Add the domain to `ALLOWED_DOMAINS` in
   [`packages/email/src/allowed-domains.ts`](../../packages/email/src/allowed-domains.ts), in sorted
   position. A listed domain covers its subdomains too.
2. Re-run the `bun -e` check above. It prints `null`.
3. Open a PR and merge it. The rule is bundled into each deployable that checks it, so all of these
   must deploy from that merge before every path accepts the domain:
   - `cognito`: the pre sign-up Lambda bundles the lists (sign-up, Continue with Google)
   - `customer-dashboard`: invites, the sign-up hint, the application form
   - `bank-dashboard`: invites, the sign-up hint
   - `admin-dashboard`: customer and bank invites made by staff
   - `temporal-worker`: onboarding (`invalidBasics` on submit, the owner invite)
4. After the deploys land, have the customer try again from a fresh page.

## Rollback

Remove the line and merge. The same deploys must run. Logins and invites created while the domain
was allowed stay; nothing re-checks them.

## Prevention

- `an allowed domain is never refused` in
  [`packages/email/test/email.test.ts`](../../packages/email/test/email.test.ts).
- A list update (bumping the two pinned versions in the root `package.json`) can add or drop a
  domain; read the diff of both lists for domains a customer uses.
