Skip to main content

Versioning

The SDK follows semantic versioning. Within a major version (e.g. 0.x), every release is backwards compatible unless a ### Breaking section says otherwise. We bump the minor version when adding new APIs, and the patch version for bug fixes. By default customers loading from cdn.haloagents.ai/sdk/latest/ automatically get every new release. To pin to a specific version (recommended for change-controlled environments), see Pinning a specific version.

0.9.0

May 21, 2026

Changed

  • ui: 'manual' and manualOnPaths now allow in-app messages and banners. Manual mode hides the floating launcher and SDK-initiated surfaces (proactive teasers, greeting), but dashboard-configured Active Messages still render. Embedders who want zero overlays can opt out with ui: { inAppMessages: false, banners: false }. See Manual mode.

Added

  • getUserToken callback — fetch a fresh JWT from your backend when the static userToken expires. See Identity Verification.
  • onIdentityExpired callback — fires once when a JWT is expired, missing, or rejected (403). User-scoped requests pause until a fresh token arrives.
  • Fail-closed identity handling — the SDK no longer sends known-expired JWTs or keeps polling ticket endpoints after identity failure.

0.8.0

May 19, 2026

Breaking (SDK requests)

  • /api/sdk/users/identify and /api/sdk/companies/identify now reject system-managed trait keys with HTTP 400. Previously these keys silently landed in custom_fields (e.g. userTraits.created_at) or, on the company side, were ignored entirely while shadowing the real columns. Both behaviors silently broke downstream consumers (segments, automations, the inbox sidebar, the AI prompt). The endpoint now returns { error, reserved_keys: [...], docs_url } so a single 400 names every offending key in one round trip. Rejected on userTraits: id, created_at, updated_at, first_seen, last_seen, last_contacted_at. Rejected on companyTraits: id, created_at, updated_at, health_score, team_size, last_contacted_at. Migration. If you were sending traits.created_at on either endpoint to record “when this user/company signed up in my product”, rename it to traits.signed_up_at. Same ISO 8601 value, same meaning. The new field now populates a dedicated column on both tables, so the value powers the Contacts table, the new “Customer since” column on Companies, segments, automations, broadcasts, email merge fields ({{ signed_up_at }} for users, {{ company.signed_up_at }} for companies), the AI prompt, and the MCP data_search_users / data_search_companies filters. See User Traits and Company Traits. Historical data was preserved: any value previously stored in custom_fields.created_at or custom_fields.intercom_created_at was backfilled into the new signed_up_at column at deploy time.

Added

  • companies.signed_up_at column for the customer-set “when did this company onboard in my product” date. Already existed on end_users; companies now have it too with the same field name and semantics on both endpoints. Auto-populated from Intercom (created_at), HubSpot (properties.createdate), Stripe (customer.created), and CSV import (header patterns: signed_up_at, signup_date, customer_since, member_since, create_date, created, and similar).
  • Customer since column on the Companies dashboard (hidden by default; toggle on via the column chooser). The existing “Added” column was renamed to Added to Halo to make the distinction obvious: “Added to Halo” is when the row landed in our database, “Customer since” is when they actually signed up in your product. Same split appears in the company detail sidebar’s Timeline section.
  • days_since_signup in onboarding rules and Slack mode-determination now prefers signed_up_at, falling back to created_at when not set. Pre-existing Intercom/HubSpot/Stripe-synced companies stop looking brand new the moment the backfill runs.
  • signed_up_at_after / signed_up_at_before filters on the data_search_users and data_search_companies MCP tools so the AI can answer “users who signed up in the last 30 days” or “companies that became customers in Q3 2024” without scanning every row.
  • signed_up_at added to the UserTraits and CompanyTraits TypeScript interfaces so TypeScript consumers get autocomplete for the new field. JSDoc on both interfaces lists the reserved keys that trigger HTTP 400, so the contract is visible at autocomplete time. The pre-0.8 UserTraits.created_at field was removed from the type (the value was always orphaned in custom_fields; keeping it in the types after the route rejection would be worse than no claim).

Fixed

  • Powered-by badge no longer leaks into ticket-detail and live-help views. Three of the chat panel’s view-switch methods forgot to toggle the .ab-powered-by element alongside messages / input / mode toggle, so the badge would either stay visible underneath the ticket detail surface or stay hidden after the user navigated back. Now follows the rest of the chat surface during view transitions.

Backwards compatibility

The breaking change is server-side (the SDK bundle has no behavior change). The CDN bundle is forward-compatible with the new rejection: no client-side code emits traits.created_at, so a customer page loading 0.7.x after the server deploys still works as long as the host app isn’t sending the reserved keys via userTraits / companyTraits. The signed_up_at type addition is purely additive; existing code keeps compiling. Customers loading from /sdk/latest/ automatically get 0.8.0 on the next page load. Version-pinned customers can move to /sdk/0.8.0/ at their own pace using the SRI manifest at /api/sdk/sri. Customers calling the identify endpoints directly from a backend must update before the dashboard deploy.

0.7.0

May 19, 2026

Changed (default behavior)

  • autoOpenUnseen now defaults to false (was true). The chat panel no longer auto-expands on page load when the user has unread messages. The launcher’s badge dot still appears for unread messages; the panel stays closed until the user taps. This matches Intercom’s default and removes the class of “panel auto-opened over my Sign Up button on mobile” reports without requiring any customer config. Migration. Customers who relied on the old auto-expand behavior can opt back in: HaloAgents.init({ ..., ui: { autoOpenUnseen: true } }). When opting in, consider adding manualOnPaths covering your auth/checkout flows so the panel can’t auto-open over a CTA. See Default behavior is quiet.

Added

  • onChatOpen and onChatClose callbacks. Fire once on the closed→open and open→closed transitions of the chat panel, regardless of who initiated the transition (host openChat()/closeChat(), the user tapping the trigger or the panel’s X button, or the SDK auto-opening on unseen messages). Idempotent: calling openChat() while already open does not fire onChatOpen again. Throws inside the callback are caught and routed through onError (if set) so a host bug cannot desync the SDK’s open/close cycle. Primary use case: hosts using ui.trigger: false or ui: 'manual' who need to keep their own “is the chat open?” state in sync with the panel. Replaces the pre-0.7 MutationObserver on .ab-trigger pattern, which does not work without a trigger element. See Tracking open state in manual mode.
  • Auth-page console nag. init() prints a one-time console.warn when it runs on a recognizable auth route (/signin, /signup, /login, /forgot-password, /oauth/*, /auth/*, etc.) AND the customer has explicitly enabled autoOpenUnseen: true without a manualOnPaths carve-out covering the path. Targeted at the narrow case where someone deliberately turned the loud behavior back on but forgot the carve-out. Pure dev-time signal, no telemetry, no production effect. With the new default of autoOpenUnseen: false, this warning won’t fire for most customers.

Backwards compatibility

All changes are additive or strictly safer than 0.6.x. Existing integrations need no code changes. Customers loading from /sdk/latest/ automatically get 0.7.0; customers using the legacy unprefixed path (cdn.haloagents.ai/sdk/haloagents.umd.js) are unaffected, that path still works and serves the latest build.

0.6.0

April 28, 2026 A “production-readiness” release. No new customer-facing UI surfaces; everything here is about catching bugs earlier, debugging faster, and locking down the install path.

Added

  • HaloAgents.getInstance().debug(). Returns a structured snapshot of the SDK’s runtime state (resolved UI flags, agent load state, session info, what’s actually mounted in the DOM, identity presence flags) and prints a human-readable version to the console. The first thing to call when something seems wrong. Safe to share with support since secrets are presence-flagged, not echoed. See Troubleshooting.
  • HaloAgents.version. Current SDK version string. Also surfaced inside debug() output. Useful for confirming which CDN bundle a page is actually loading.
  • Versioned CDN URLs. Every release is now served at both cdn.haloagents.ai/sdk/latest/ (auto-updating) and cdn.haloagents.ai/sdk/<version>/ (immutable). The <version>/ paths are pinnable with SRI integrity hashes for tamper-proof installs.
  • Strict config validation. init() now warns (single grouped console.warn) on unknown config keys, deprecated keys, and obvious typos. Suggests the closest valid key when the typo is within edit distance 2 (catches manualOnPath, triger, userTrait, etc.).

Fixed

  • unmount() (called by destroy()) now cleans up top banners and in-app message overlays in addition to the chat panel host. Without this fix, calling destroy() collapsed the panel but left active-message chrome on the page until full reload.
  • Banners and in-app messages no longer leak when the widget is unmounted between the active-messages fetch starting and resolving (e.g. SPA route change calling destroy() mid-request).

Backwards compatibility

All changes are additive or strictly safer than 0.5.x. Existing integrations need no code changes. Customers loading from /sdk/latest/ automatically get 0.6.0; customers using the legacy unprefixed path (cdn.haloagents.ai/sdk/haloagents.umd.js) are unaffected, that path still works and serves the latest build.

0.5.1

April 28, 2026

Fixed

  • ui: 'manual' and manualOnPaths no longer leak top banners or in-app message overlays. loadActiveMessages was bypassing the UI suppression flags entirely, so any active message configured in the dashboard would still render across the page. Reported by orbitforms.ai.

Added

  • ui.banners and ui.inAppMessages per-surface flags. Both default to true. Implied false by ui: 'manual' and manualOnPaths matches.
  • onBeforeShowBanner({ id, body }) and onBeforeShowInAppMessage({ id, body }) host-veto callbacks. Same shape as the existing onBeforeShowProactive callback: synchronous, return false to suppress this specific message.

0.5.0

April 28, 2026

Added

  • ui config field on HaloAgents.init() to suppress one or more default UI surfaces. Pass 'manual' for full headless mode, or an object for per-surface flags: { trigger, badge, proactive, greeting, autoOpenUnseen }. Replaces the brittle pattern of injecting CSS into the shadow DOM to hide internal classnames.
  • manualOnPaths config field. Glob patterns that switch the widget into manual mode on matching pathnames. Lets a single init() call serve both marketing pages (full chrome) and an authenticated app (host-controlled launcher) without conditional initialization.
  • onBeforeShowProactive(message) callback. Synchronous host veto run before each proactive teaser. Return false to suppress this specific message.

Fixed

  • The auto-open-on-unseen-messages behavior now respects ui.autoOpenUnseen, so apps that mount Halo only behind a custom button no longer have the panel pop open unprompted when an agent replies.

0.4.0

February 2026

Added

  • identify(userId, traits, identity?). Optional third argument lets the host rotate userToken / userHash alongside the user id (e.g. on JWT refresh).
  • The SDK now forwards the configured userToken / userHash on every request as X-User-Token / X-User-Hash headers, not just on chat.

Security (potentially breaking for orgs with identity verification enabled)

The server-side companion to this release gates every user_id-scoped SDK endpoint on a valid user_token JWT (or user_hash) whenever the org has team_secrets.identity_secret configured. Previously only the chat / stream / handoff routes enforced identity; the surrounding read + write endpoints trusted whatever user_id the caller sent. After deploying the matching server release, any widget still on <= 0.3.x will receive 403 responses on these endpoints because the older SDK does not forward the identity headers. Upgrade widgets to 0.4.0 or later (or arrange for the host page to call HaloAgents.identify() with the new identity argument) to restore those endpoints. Orgs without identity_secret configured are unaffected.

Older versions

For releases prior to 0.4.0, see the source repository’s packages/sdk/CHANGELOG.md.