Identify and track from your backend.
The widget covers browser-side identification. When the signal originates on your server — a signup, a billing webhook, an import — these routes write the same records without a browser in the loop.
The route most teams want
POST /api/widget/identify — the same route the widget uses, callable from any backend. It creates or updates the visitor's contact, links a company, and merges traits:
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č",
"company": { "name": "Nova Leaf", "domain": "novaleaf.com" },
"metadata": { "plan": "trial", "seats": 8 }
}'Request fields
app_id(required) — the project the identity belongs to.user_id— your stable identifier; the primary match key.email— fallback match key; also becomes the contact's email.name,company,metadata— written to the contact/company per the data model.signature— HMAC hash when identity verification is on.stage+stage_update_mode— optional CRM stage withadvance_only(default) orexactsemantics.
Set-properties for updates
Identify is for "this is who I am." For later property changes, use the visitor property routes — they merge single fields without resending the full identity:
PATCH /api/visitors/{id} # update properties
GET /api/visitors/{id} # visitor with contact + companyServer-side events
POST /api/v1/events/track (public API key) records product events against a known customer. Use event_key for idempotency — retried webhooks from your billing provider won't double-count:
{
"name": "plan.upgraded",
"event_key": "sub_789_scale",
"user_id": "usr_123",
"value": 1800
}Events feed automations ("usage limit reached" → lifecycle email), health signals, and the attribution view.
Write semantics
- Matching order:
user_id→email→ create new. - Traits merge; the public API's
metadatareplaces (flat scalars, ≤ 10KB). - Company matching: domain first, then name.
- Stage writes respect advance-only precedence unless
exactis requested.
When to use which surface
- Widget — the user is in the browser and already logged in.
- Server identify — the identity was just established server-side (signup, SSO callback).
- events/track — anything that happens in your product that should move a pipeline, trigger an email, or update health.