# Verify email address

**Flow key:** `email-verification`

## Outcome

The rightful controller proves one email address for one account/action without replay, cross-account binding, or stale-link success.

## Actors

- User
- Identity service
- Email delivery provider

## Preconditions and entry points

- An unverified account or pending email change exists.
- The mailbox is controllable.
- Expiry and resend policies are known.

## Required test data

Use synthetic, isolated, resettable data. Never use real credentials, payment instruments, private customer records, or production notifications.

| Data | Canonical valid seed | Critical variations |
|---|---|---|
| Account | `carbon.verify+001@example.test` pending verification | already verified, disabled, deleted, other tenant |
| Verification link | fresh token for the pending account | expired, used, malformed, wrong audience, older resend |
| Delivery | one delivered message | delayed, duplicated, bounced, spam-folder, link scanner prefetch |
| Session | same browser that requested verification | signed out, other browser/device, other account signed in |

## Canonical steps

Treat this as the ordinary successful path inferred from common product behavior. Adapt it to observed labels, business rules, APIs, and implementation evidence; do not invent a requirement.

| Step | User or system action | Expected evidence |
|---:|---|---|
| 1 | Request or trigger verification. | One appropriately addressed message is queued without exposing secrets in logs or UI. |
| 2 | Open the latest verification message. | Sender, product, account hint, expiry, and destination are trustworthy. |
| 3 | Follow the verification link. | The token binds to the intended account, email, and verification action. |
| 4 | Complete any required authentication. | Account switching cannot verify the wrong identity. |
| 5 | Observe success and account state. | Email becomes verified once and restricted capabilities update. |
| 6 | Reuse the link and inspect older links. | Replay is harmless and stale tokens cannot change current state. |

## Alternate paths

- Verify during signup
- Verify after email change
- Enter a code instead of following a link
- Resend to same address
- Change address before verification

## Failure and recovery paths

- Delivery delay causes repeated resends
- Link scanner consumes one-time token
- Expired token yields dead end
- Email changes after token issuance
- Success page loads but persistence fails

## Completion oracle

- The stated outcome is true in visible UI and authoritative persisted state.
- Exactly one intended business result and no unintended side effect occurred.
- Identity, tenant, authorization, money/data, lifecycle, notifications, integrations, analytics, and audit agree where applicable.
- Refresh, retry, back/forward, and a second authorized read do not reveal a contradictory result.
- Any partial, delayed, canceled, failed, or unknown outcome is labeled truthfully and has a safe recovery path.

## Evidence to capture

- Entry context, actor, tenant, role, feature flags, locale, time zone, and initial identifiers/state.
- Step-level screenshots or DOM state plus request/response, correlation, job, event, and stable business IDs where available.
- Before/after authoritative records, versions, totals, permissions, lifecycle states, and downstream side effects.
- Notifications, audit events, integration deliveries, retries, deduplication, latency, and recovery evidence.
- Untested branches, unsafe-to-run cases, product decisions, and evidence tier.

## Product decisions to resolve

- Exact eligibility, required data, authorization, and validation rules.
- Which steps are synchronous, asynchronous, optional, skippable, reversible, or irreversible.
- Idempotency, concurrency, timeout, retry, cancellation, and compensation semantics.
- Retention, privacy, notification, audit, performance, accessibility, and localization commitments.

## Related CARBON items

- [email address](../../items/controls/email-address.md)
- [registration and invitation](../../items/identity/registration-and-invitation.md)
- [mfa and one time codes](../../items/identity/mfa-and-one-time-codes.md)
- [email and outbound message](../../items/content/email-and-outbound-message.md)

[Back to Identity and access flows](index.md)
