Setup · Identification

Turn anonymous visitors into known customers.

One identify call attaches an email, a contact record, and (optionally) a company to everything that visitor does — chats, page views, product events.

When to identify

Call identify once per session, right after your app knows who the user is — after login, signup, or when restoring a session on page load. Anonymous visitors before that point are tracked automatically and merged into the identity.

From the widget

HalWidget.identify({
  user_id: "usr_123",             // recommended: your stable id
  email: "mia@novaleaf.com",
  name: "Mia Kovač",
  company: { name: "Nova Leaf", domain: "novaleaf.com" },
  plan: "trial"
});

From your server

When the identification happens in a backend flow (signup API, webhook from your billing provider), call the identify route directly — no widget required:

curl -X POST "https://api.chatwithhal.com/api/widget/identify" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "YOUR_PROJECT_ID",
    "user_id": "usr_123",
    "email": "mia@novaleaf.com",
    "name": "Mia Kovač",
    "metadata": { "plan": "trial" }
  }'

What HAL creates

  • Contact — created or updated from email/user_id.
  • Company — created from company.name/company.domain and linked to the contact.
  • Visitor session — current and future page visits attach to the identity.
  • Metadata — flat properties (strings, numbers, booleans) stored on the visitor, visible in the inbox context rail.

Field behavior

Matching order is user_id first, then email. If neither matches an existing contact, a new one is created. Traits merge into the existing record — set() sends partial updates without replacing everything:

HalWidget.set({ seats: 12 });        // merge one property
HalWidget.set({ plan: "scale" });    // ...another later

Identify vs set vs track

  • identify() — "this is who I am." Creates/updates the contact. Once per session.
  • set() — "here are current property values." Merges traits on the known identity.
  • track() — "this just happened." Appends a timestamped event, changes nothing.

Stage sync semantics

When your identify payload or setStage() includes a CRM stage, HAL applies it in advance-only mode by default: stages can move forward along your pipeline order but never backward. This protects won deals from being demoted by a UI regression or stale client state. Pass "exact" as the second argument when a backward move is intentional (e.g. marking churn).

Verify identities

Anyone can call identify with any email from the browser console. If your site acts on identities — showing plan data, moving deals — sign them server-side with HMAC identity verification and optionally reject unsigned claims.