# user.updated: the profile sync event
Fires whenever the user object changes: name edits, added or removed email addresses or phone numbers, metadata writes, verification status changes. Your handler is a sync function, not a one-shot trigger.
## What to do on receipt
1. Look up your local row by `evt.data.id`. If it does not exist, create it. Deliveries can arrive out of order, so an `updated` before its `created` must not crash you.
2. Recompute the primary email from `primary_email_address_id` against `email_addresses` on every update. Email changes, removals, and re-orderings all arrive here.
3. Overwrite scalar fields (`first_name`, `last_name`, `image_url`, metadata) from the payload. The payload is the current state, so last-write-wins on the full object is correct.
4. If your app sends email to the stored address, diff old vs new. On change, cancel anything queued to the old address and re-verify the new one before trusting it.
5. Return 200.
## The trap
Assuming chronological delivery. Webhook deliveries are at-least-once and can be reordered or replayed. A handler that only applies changes "newer than last seen" using its own clock will drop legitimate updates. Apply the payload as current state; it already is.
## Checklist
- Never key logic on `evt.data.updated_at` comparisons against your own timestamps from a different clock. The payload IS the state.
- Metadata writes from your own backend also fire this event. Make sure your handler ignores its own echoes or you get a write loop.
- user.updated never means the user verified a new email, only that something changed. Check `email_addresses` entries' `verification` status field if verification matters.