Skip to main content

Endpoint

Upsert an end user record and optionally link them to an existing session. Called automatically when you use identify() in the SDK, but you can also call it directly from your backend.
Backfilling or importing existing users? Every identify call bumps the user’s last_seen to now, so looping it over your user base marks everyone “active today” and corrupts activity data. Use Update / Backfill Users instead, which never touches activity timestamps. See Importing & Backfilling Data.

Authentication

Requires your publishable widget key in the Authorization header:
The org_id is derived from the widget key. You do not need to pass it in the request body.

Request Body

string
required
Your system’s unique identifier for this user.
object
User attributes. Recognized fields (name, email, signed_up_at, renewal_date, renewal_status, contract_term, payment_terms, on_contract, mrr, arr) are stored in dedicated columns. All other fields are stored in custom_fields. See User Traits for recommended fields.Do not send id, external_id, org_id, company_id, created_at, updated_at, first_seen, last_seen, or last_contacted_at. These are managed by Halo and the endpoint returns HTTP 400 if any of them appear in the body. Use signed_up_at to set the date the user signed up in your product. See System-managed fields.
object
Structured context entries for AI consumption. Stored in the context jsonb column. Merged with existing context (new keys overwrite). See Context Entries.
string
If provided, any orphaned transcripts or tickets from this session (created before the user was identified) are retroactively linked to the user record.
string
JWT signed with your Identity Secret (HS256). Required when identity verification is enabled for your workspace. The token’s user_id claim must match the user_id field. If the token carries email or name claims and the traits body omits those fields, the verified claim values are stored on the user record. See Identity Verification.

Recognized Traits

These trait keys are stored in dedicated database columns for filtering and display: All other trait keys are stored as custom fields and are accessible to the AI agent.

Rejected Traits

These keys trigger HTTP 400 if present in the traits body. They are managed by Halo and customer-set values would silently shadow the real columns. See System-managed fields for context.

Example

Response:
The response is a minimal projection of the stored record. Traits, context, custom fields, email, name, revenue, and score fields are not echoed back, because this endpoint is callable with the public widget key. Read full contact data through the dashboard or MCP instead. All timestamps in the response are Postgres timestamptz values serialized as ISO 8601 with microsecond precision and a +00:00 offset.
  • created_at, updated_at, first_seen, and last_seen are read-only Halo-managed timestamps. Do not echo them back into the traits body on subsequent calls (the endpoint returns HTTP 400 if you do).
  • signed_up_at is customer-writable via traits.signed_up_at on a follow-up identify call. On update the column uses COALESCE semantics, so passing null (or omitting the key) preserves the existing value. Pass a new ISO 8601 string to overwrite.

Behavior

  • If a user with the same user_id exists within your organization, it is updated
  • If it doesn’t exist, it is created
  • Traits are merged: existing values are preserved, matching keys are overwritten
  • Context is merged: existing keys are preserved, new keys are added, matching keys are overwritten
  • The combined size of traits and context must not exceed 20,000 bytes
  • When session_id is provided, orphaned transcripts and tickets from that session are retroactively linked to the user
  • An unverified traits.email that already belongs to another contact in your workspace is not used to link or merge into that contact. The user keeps their own record and the address is stored in custom_fields.email. To link a user to an existing contact by email, send a user_token whose verified email claim matches