CRM

CRM in HAL keeps pipeline context close to support and execution.

Use CRM when founder-led sales or a small revenue motion should influence the operator loop, not live in a separate tool with no connection to support or Work.

Core records

  • Companies hold deal context, stage, value, and account-level notes
  • Contacts hold person identity, tags, and contact-specific metadata
  • Visitors are the widget-side identity that can be linked to contacts and companies
  • Stages define project-specific pipeline progression and ordering

How data usually enters CRM

Widget identify

Best when your app already knows the signed-in user and should push contact and company context into HAL during chat.

Account API routes

Best when an authenticated HAL user or internal backend manages CRM records directly through account routes.

Public API key routes

Currently limited to conversations and contacts under /api/v1; they are not the main write surface for visitor-company sync.

Choose the right route

  • POST /api/widget/identify: identify a visitor and optionally merge contact metadata, company metadata, linked company IDs, and stage context
  • POST /api/widget/set-properties: merge additional visitor/contact properties after identify
  • POST /api/widget/track: record visitor or company milestone events with a name and optional scalar value
  • POST /api/widget/set-stage: update the active or explicitly targeted company stage for an identified visitor
  • POST /api/widget/set-deal: update deal value and optional stage on the active or explicitly targeted company
  • POST /api/contacts and PATCH /api/contacts/:id: create or update contact records directly as an authenticated HAL user
  • POST /api/companies and PATCH /api/companies/:id: create or update company records directly as an authenticated HAL user

Important behavior

  • Deal value lives on the company, not the contact
  • Widget-side stage writes default to advance_only, so they do not downgrade a later manually-set stage
  • setStage() and setDeal() require the visitor to already be linked to a contact and company, which usually means identifying with an email first
  • Use company.id during identify() to establish the active company context for later widget CRM writes
  • Pass company_id on later company-scoped widget writes when the contact can belong to multiple companies or the user can switch accounts
  • track() is for point-in-time milestones; use metadata writes for current-state attributes
  • set-properties updates visitor/contact metadata only; it does not update company metadata

How writes behave

  • Identify creates or matches contact: when identify() includes an email, HAL finds the existing contact by project and email or creates one if needed
  • Identify can create a placeholder company: if the contact has no linked companies yet, HAL creates or reuses a placeholder company so the contact has account context immediately
  • Real company links replace placeholders carefully: if later identify calls link the contact to a real company by ID, HAL removes the placeholder only when it is safe to do so
  • Metadata is merged: contact and company metadata are merged key-by-key rather than replacing the entire object
  • Contact and company linking is many-to-many: one contact can belong to multiple companies, and one company can have multiple contacts
  • Company targeting is deterministic: widget company writes use explicit company_id first, then the active company from identify(company.id), then a single linked company

Stage movement semantics

  • advance_only compares stage order and skips a write when the company is already in a later stage
  • advance_only still applies when the requested stage is ahead of the current stage or when no stage is set yet
  • exact writes the requested stage directly, including intentional downgrades
  • Manual dashboard moves are still the highest-authority mental model; widget stage writes should usually be treated as lower-authority signals unless your backend is the source of truth

Contact and company addition semantics

  • Manual POST /api/contacts creates only the contact; it does not automatically create a company
  • Manual POST /api/companies creates only the company; contacts are linked separately
  • Widget identify is the main automatic path that can create a contact and create or attach company context in one flow
  • If identify sends company.id or company_ids, HAL links the contact to those existing company records
  • company_ids adds links, but only company.id establishes the active company context for later widget CRM writes
  • If identify sends only a company.name and the contact currently has only a placeholder company, HAL upgrades that placeholder in place

How CRM affects HAL

Stage movement

Meaningful stage changes can affect the active focus lane.

Stalled opportunities

HAL can recommend sales actions when warm pipeline is losing momentum.

Shared context

Support and CRM signals can reinforce each other instead of drifting apart.

Related docs