Short answer: model every admin action as a validated, auditable, recoverable state transition; use the user ID after lookup, and put deletion behind an explicit policy check.

This is the constraint that changes the design. A media app adding phone one-time-code login still needs an admin console that can find the right account, correct a profile, or remove access without turning an email typo into an irreversible event. I keep lookup, update, and delete as separate commands, with authorization and an audit record around each one. That separation also gives an eval harness distinct assertions: a lookup may reveal a record, an update may change an allowlisted field, and a deletion request may only advance a state after a second policy decision. Treating those as one generic handler makes it hard to tell which guarantee failed, especially when an operator retries after a browser timeout.

The experiment: where the simple CRUD path breaks

The tempting implementation accepts an email from a support form and pipes it through a generic CRUD handler. It is short, but it makes the email address both a search key and an identity key. Addresses change, aliases collide, and a retry can apply a second state change before an operator notices.