Identify
Sessions are anonymous by design. When the visitor tells you who they are - signs in, subscribes, checks out - you can say so, and the session is linked to a contact.
signals.identify({ external_id: 'usr_123', traits: { first_name: 'Jane', lifecycle_stage: 'customer' },});At least one of email or external_id is required. Returns a promise resolving to { success, contact_id, linked_to_session } or { success: false, error }.
What happens on the server
Section titled “What happens on the server”- The contact is found by
external_idif you sent one, otherwise byemail(most recently seen first). If nothing matches, a contact is created. - Traits you sent are written onto the contact. Existing values are updated; keys you did not send are left alone.
- The session gets the contact’s id, once. A session that is already linked to a contact keeps that link; a second
identifywith a different person updates the second person’s contact but does not re-link the session. - Two events are recorded on the session:
contact(created or updated, on every call) andidentify(the first time the session is linked).
Calling identify twice with the same payload is a no-op the second time.
Traits
Section titled “Traits”Traits are snake_case and map onto contact fields. Anything else goes under meta.
| Trait | Notes |
|---|---|
first_name, last_name | |
phone, company, job_title, website | |
country, city | Default from the session’s geo when not supplied |
lifecycle_stage | Free string. The app’s own pickers offer lead, subscriber, customer, user. Defaults to user on create. |
meta | Object of arbitrary key/value pairs, merged into the contact’s meta |
first_name: 'Jane', lifecycle_stage: 'customer', meta: { plan: 'pro', seats: 5 },});Top-level keys that are not in the table are dropped. Put them in meta.
Global Privacy Control
Section titled “Global Privacy Control”A browser sending Sec-GPC: 1 is never identified. The server answers { success: true, contact_id: null, linked_to_session: false, gpc: true } so your code sees a quiet no-op, the session stays anonymous, and analytics continue. See Privacy Controls.
Where contacts live
Section titled “Where contacts live”Identified people are listed under Contacts in the property, with their sessions, lifecycle stage and state. Deleting a contact redacts it in place so the sessions it touched keep working. The full model, including the API and Automations paths for creating contacts, is on Contacts.
Identify from the server
Section titled “Identify from the server”If sign-in happens on your backend, identify from there instead of shipping the email through the page:
POST /v2/property/contacts/identifyon the API, with the session id from Session Handoff.- The PHP SDK can identify automatically on Laravel’s
Loginevent.
Server-side identify accepts the same traits and follows the same set-once rule. It also works without a session id, in which case the contact is created or updated but nothing is linked.