Platform · API

Three route families. One base URL.

HAL's API surface splits by audience: widget flows (public), account routes (your session), and the public v1 API (API keys). Pick the right family and the rest is plain REST + JSON.

Base URL

https://api.chatwithhal.com

Route families

  • /api/widget/* — public flows used by the embeddable widget: conversations, identify, stages, CSAT, automations, articles. Project-scoped by app_id.
  • /api/* (account) — dashboard-grade routes authenticated with a bearer token from login. Full workspace management.
  • /api/v1/* — the public integration API, authenticated with a project API key. Plan-gated and rate-limited per project.

Authentication

# account routes
Authorization: Bearer <access-token>

# public v1 routes
X-API-Key: <project-api-secret>

API keys are per project (Settings → API). The key's project scopes every v1 call — there is no cross-project access.

Public v1: contacts & companies

GET    /api/v1/contacts?search=&limit=&offset=
POST   /api/v1/contacts
GET    /api/v1/contacts/{id}
PATCH  /api/v1/contacts/{id}
DELETE /api/v1/contacts/{id}

GET    /api/v1/companies?search=&stage_id=&limit=&offset=
POST   /api/v1/companies          # name required
GET    /api/v1/companies/{id}
PATCH  /api/v1/companies/{id}

Company writes accept deal_value, stage_id, notes, domain, industry, and is_priority. Both resources accept a flat metadata object — plain scalars only, ≤ 50 keys, ≤ 10KB total, replace-on-write.

Public v1: conversations & events

GET   /api/v1/conversations
GET   /api/v1/conversations/{id}     # includes messages
PATCH /api/v1/conversations/{id}     # status / assignee

POST  /api/v1/events/track

events/track records a product event and — when it matches your automation or health-signal rules — feeds pipelines and focus. Attach user_id, email, or company_id so the event lands on the right customer:

curl -X POST "https://api.chatwithhal.com/api/v1/events/track" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $HAL_API_KEY" \
  -d '{
    "name": "invoice.paid",
    "event_key": "inv_789",          # unique key = idempotent
    "value": 149.0,
    "user_id": "usr_123",
    "company": { "name": "Nova Leaf", "domain": "novaleaf.com" }
  }'

Webhooks

Manage per-project webhooks via the API; deliveries POST a JSON envelope with an optional X-Webhook-Signature (HMAC-SHA256 of the raw body using your webhook secret):

GET/POST /api/v1/webhooks
PUT/DELETE /api/v1/webhooks/{id}

{
  "event": "conversation.created",
  "payload": { "...": "event-specific fields" },
  "timestamp": "2026-01-15T10:30:00.000Z"
}

Seventeen event types across conversations, messages, contacts, companies, stage changes, visitor and visitor event tracking, identification, and CSAT — including visitor.event_tracked and company.event_tracked so your systems can react to product signals in real time. Queue-based delivery with retries and a 10-second timeout.

Account routes (selection)

GET/PATCH  /api/me                        # profile, preferences
GET        /api/me/data-export            # full GDPR export
DELETE     /api/me                        # account deletion (30-day grace)
CRUD       /api/apps, /api/contacts, /api/crm-stages
CRUD       /api/articles, /api/article-groups
GET        /api/ai/usage | /api/ai/quota
POST       /api/billing/checkout | /portal | /change-plan

Errors & limits

  • Validation failures return 400 with a specific message; plan-gated features return 402 with upgrade context.
  • v1 is rate-limited per project — back off on 429.
  • Every write accepts idempotency where it matters most: events/track dedupes on event_key.