Skip to content

πŸŽ›οΈ Widget placement & JS API β€” plus a rebuilt Ask AI

Choose a tag to compare

@chattermate chattermate released this 10 Aug 13:50
c88b508

Released 2026-08-10

Put the widget where you want it and open it from your own button, ask questions in a redesigned command palette, and give every teammate exactly the access their role says they have.

Highlights

Widget placement and a public JS API

The widget is no longer locked to a bubble in the bottom-right corner.

  • Display modes: floating (left or right), a full-height sidebar drawer from either edge, or a search-bar trigger. Custom width, height, offsets and z-index.
  • Your own button: hide the built-in launcher and open the chat from any element β€” a trigger CSS selector or a data-chattermate-open attribute, no JavaScript required.
  • JS API: ChatterMate.open(), close(), toggle(), isOpen(), showLauncher(), hideLauncher(), and on() for the ready, open, close and unread events.
  • ChatterMate.open({ message }) prefills the input without sending it, for "Ask about pricing"-style links.
  • Defaults live in a new Widget placement section under the agent's Customization tab. Anything set in the embed snippet overrides them for that page, so one agent can look different on your pricing page than in your docs.

Full reference: Widget Integration.

Ask AI, rebuilt

The Ask Anything and Aurora designs now open as a centered command palette β€” ⌘K to open, Esc or a click outside to dismiss β€” instead of a chat window in the corner. One input at the top, suggested questions while it's empty, and answers written as prose with their sources listed. It types its answer out as it arrives and sizes itself to what it's showing rather than sitting at a fixed height.

Start a new chat

A new Allow new chat setting, off by default, adds a control that ends the current conversation and starts a clean one. It's hidden while a human agent is handling the chat, and asks for confirmation before discarding what's there.

Roles that mean what they say

A Human Agent could not load any page β€” every navigation bounced to the subscription screen. Fixing it turned up a wider problem: ticking a permission on a role often did nothing, and hiding a menu item did not protect the page behind it.

  • view_agents and view_knowledge are enforced. They were in the role editor and checked nowhere.
  • All /roles endpoints are gated. Previously any authenticated user could grant their own role super_admin.
  • You cannot grant a permission you do not hold yourself.
  • The inbox reads new teammate and agent roster endpoints instead of admin ones, and those return no roles, permissions or agent instructions.
  • webhook_url is no longer returned to non-admins β€” it embeds the secret that authenticates inbound email and SMS.
  • One route-to-permission map now drives both the router guard and the sidebar, which had drifted apart. Denials land on a real /403 page instead of the agent setup wizard.

Chat scope when adding a teammate

Two checkboxes next to the role dropdown on the user form β€” "all AI chats" and "all chats in the organization" β€” so you can set what someone sees without visiting the Roles screen first. Permissions still live on roles: an existing role with the right permissions is reused, and a new one is created only when none matches. The role the rest of the team is on is never widened.

Admins can also reset a teammate's password directly from the Human Agents list.

Mobile app fixes

  • Mark all read now clears everything, not just the 50 notifications the app had loaded β€” the badge could get stuck above 99 with no way down.
  • Notifications can be deleted, individually or all at once.
  • The Android status-bar icon is a proper monochrome badge instead of a grey square.
  • The Create Ticket buttons fit the mobile header instead of spilling out of it.

Fixes

  • Your own avatar no longer breaks an hour into a session. It was rendered from a presigned URL cached at login, which outlived its own signature; it now resolves through a stable endpoint signed per request.
  • Agents answer at the tool-call limit instead of returning "I'm sorry, I didn't quite catch that". Tools are withdrawn and the model replies from what it already retrieved, on both the streaming and non-streaming paths, and parallel tool calls no longer end a run early. The limit is configurable via AGENT_TOOL_CALL_LIMIT.
  • The default guardrail is deny-only and no longer refuses genuine product questions that happen to name third-party software. The dashboard shows the actual rule, editable, instead of a prose description that had drifted from it.
  • End Chat appears immediately after a human takes a conversation over, rather than after a refresh.
  • Connecting HubSpot or Pipedrive resolves its URL at runtime, so self-hosted installs stop being sent to the wrong host and getting "Not authenticated".
  • Widget bundle URLs are versioned and the widget HTML is no-store β€” it embeds a conversation token and was cacheable, which also pinned visitors to stale builds after a release.
  • The socket end_chat event authenticated with a key the widget session doesn't carry, so it never actually closed a session β€” and reported success even when the close failed.
  • Two migrations declared the same parent, leaving two alembic heads; the backend exited before binding its port on every start. The chain is linear again.

Upgrading

Two database migrations run automatically on start. Both are additive or repair-only, so rolling the code back needs no database rollback:

  • add_allow_new_chat_001 β€” adds agent_customizations.allow_new_chat, default off.
  • repair_agent_role_seed_001 β€” repairs starter Agent roles created by the hosted signup path with too few permissions, and orgs left with more than one default role. It matches the bad seed exactly, so a role an admin deliberately narrowed keeps its narrowing. Community installs match nothing.

No configuration changes are required. Existing embeds keep working unchanged: with no placement settings the widget renders exactly as before, and init({ position: { bottom, right } }) and setPosition() behave as they always did.