In a consumer product, changing an email address is a settings screen: confirm the new one, send a notice to the old one, done. In a product with organisations and a directory behind it, the same operation is a statement about identity, and it can arrive from three different places that do not agree with each other.
The user can change it in your settings. An administrator can change it in your dashboard. And the directory can change it upstream and simply tell you afterwards, through a provisioning update or a differently shaped assertion at the next sign-in. Those three paths need different rules, and products that treat them as one field update produce their worst identity bugs here.
Decide whether the address identifies or describes
The first question is whether email is your join key or an attribute you happen to store. If it identifies, changing it is a rename of the primary identity and everything referencing it must follow. If it describes, changing it is nearly free, because the stable identifier underneath is untouched and the address is one more column on the record.
Almost every product should choose the second, and most discover this late. The migration is not conceptually hard — introduce an internal identifier, backfill it, repoint references — but it touches invitations, audit entries, shared links, support tooling and every integration that keyed on the address. Doing it deliberately at the start costs an afternoon. Doing it after the fact is a project.
If the address is your join key, a marriage or an acquisition becomes a database migration.
When the directory changes it for you
An acquisition, a rebrand, a domain consolidation: one morning the directory sends updates for four hundred people whose addresses have all changed domain. This is a legitimate change and you have to apply it. The danger is that a provisioning update contains only the new address and your handler cannot tell the difference between the same person renamed and a genuinely new person who happens to have been assigned that address.
This is why the provisioning identifier matters more than anything in the payload. Match on the stable identifier the directory assigns, update the address as an attribute, and never infer identity from the address in an update. Where an update arrives with no such identifier, the safe response is to refuse it and surface the conflict rather than guess, because guessing wrong here attaches one person's history to another person's account.
- Key directory updates on the provisioning identifier, never on the address in the payload
- Apply an upstream change as an attribute update, not as a create followed by a delete
- Keep the previous address on the record so support can answer where did my account go
- Where the identifier is missing, escalate the conflict rather than resolving it silently
- Expect bulk changes; a domain migration arrives as hundreds of updates in one window
The old address gets one last message
When a change originates with the user, both addresses have a role. The new one must be verified before it becomes usable, or you have handed anybody with a stolen session the ability to redirect all future password resets to a mailbox they own. The old one must be notified, because that message is the only chance the real owner has to notice that something is wrong while they can still act on it.
Give the notification something to act on: a statement of what changed, when, and a link that reverses it or reaches a human. A notice that says your email was updated with no recourse informs somebody of a theft without helping them. Keep the ability to sign in with the old address alive for a short, bounded window during the transition, and be explicit in the interface about when it stops working.
The trail has to survive the rename
Audit entries written last year contain whatever the address was last year. If your log stores addresses rather than identifiers, a rename quietly rewrites the past — either literally, if you update in place, or in effect, because nobody can connect the old entries to the current person. Both outcomes are bad in an investigation and awkward in a compliance conversation.
Record the stable identifier in the event and resolve the display name at read time, so history stays intact and the current address is always shown. Then log the change itself as its own event, with both values, actor and source. The question an investigator asks is not what is this person's address, it is when did it change and who changed it, and only an explicit event answers that.
Everything here, already built
Sign-in, enterprise SSO, directory provisioning, roles and an audit trail behind one API. Start with the quickstart and have a working sign-in this afternoon.