Identity is the second-most-reported failure class across competitors — stale users, HMAC rejections, session leaks across logins, custom attributes dropped silently.
Evidence
Unified identity model
```ts
identify({
id: string
email?: string
name?: string
phone?: string
// Typed verification — per-provider discriminated union
verification?:
| { kind: "hmac"; hash: string } // Intercom, Chatwoot, Beacon, Gist
| { kind: "jwt"; token: string } // Drift, Freshchat
| { kind: "callback"; getToken: () => Promise } // Zendesk Messenger
}): Promise
```
Rules
- State machine: `anonymous → identified(u1) → identified(u2) → anonymous`. Every transition (a) calls provider-specific reset (b) verifies window object cleared (c) emits `onIdentityChange`.
- `identify()` returns a `Promise` that resolves only when the next ping confirms the new identity.
- Provider config flag `enforceVerification: true` makes `verification` required at the type level. Missing ⇒ compile error.
- Never compute HMACs in the browser. Document that `hash` must come from your server.
- For callback-based flows (Zendesk), invoke `getToken` on 401 refresh.
- Expose `onIdentityChange((prev, next) => …)` so consumers can mirror into their analytics.
Identity is the second-most-reported failure class across competitors — stale users, HMAC rejections, session leaks across logins, custom attributes dropped silently.
Evidence
boot(IntercomProps)results in unknown user devrnt/react-use-intercom#563` — "Running `boot(IntercomProps)` results in unknown user" Runningboot(IntercomProps)results in unknown user devrnt/react-use-intercom#563Unified identity model
```ts
identify({
id: string
email?: string
name?: string
phone?: string
// Typed verification — per-provider discriminated union
verification?:
| { kind: "hmac"; hash: string } // Intercom, Chatwoot, Beacon, Gist
| { kind: "jwt"; token: string } // Drift, Freshchat
| { kind: "callback"; getToken: () => Promise } // Zendesk Messenger
}): Promise
```
Rules