Skip to main content
Use this page when you need to decide who the widget should treat as the same person, and how much trust to place in the identity data you pass in.
If your goal is to get the widget on the page first, start with Install Widget.
Want to see how identity settings look on screen? The Playground lets you toggle visitor data collection and other options live, then copy the matching snippet.

Start With The Right Identity Model

Lowest friction. Best for public support chat where history stays tied to the same browser.
Best when you want name, email, or lightweight personalization without server-issued identity.
Best when signed-in customers should keep history across devices and you need stronger protection against impersonation.

What Changes Between Non-Verified And Verified

Verified customers can keep history across devices. Anonymous and non-verified visitors should be treated as browser-bound or device-bound experiences.
and help you personalize the chat, but they do not prove identity. is the strong identity path.
Browser-provided custom data is useful, but should be treated with caution. Server-authenticated custom data is the safer choice when it affects support decisions.

Two Tokens, Two Jobs

Do not confuse these tokens:

Widget token

This token loads your organization’s widget. Get it from the widget setup flow, then use it when you install the widget.

Authenticated contact token

This token identifies a verified customer. Your backend requests it, then passes it to user.token.

Implementation Paths

1

Choose this path for public visitors

Use this when visitors are not signed in and you want the widget to ask for basic details before chat begins.
2

Turn on built-in collection

Add collectUserData: true to the widget options.
3

Test the welcome flow

Confirm the widget asks for the visitor’s name and email before they enter the chat.
This creates a non-verified identity. It improves context for your team, but it should not be treated as strong proof that the visitor owns that email address.

Connect Each Customer’s Account

For dashboard setup, the customer flow, and approval controls, see Widget connections. This section covers the backend authentication contract. In Actions, add a server and choose Each user — their own account. Authenticate the signed-in customer through your backend as usual. By default, they can connect to every enabled per-user server in your organization:
To enable personal connections, include mcp_access: {} in the same request. To restrict a user or bind an account, provide:
  • Omit mcp_access: preserve the existing chat-only token without an expiry; personal connections are not enabled.
  • Include mcp_access: {} or omit server_ids inside it: allow all enabled per-user servers in this organization, including servers enabled later.
  • Set server_ids to a list: allow only those servers, while they remain enabled.
  • Set server_ids: []: allow none.
Shared team connections and other organizations’ servers are never included. Decide restrictions on your backend; never accept an unchecked list from the browser. account_id is optional for single-account products; include it when users can switch workspaces or accounts. Connections and session history are bound to that signed account. Browser custom data and externalId do not grant access. Pass the result as user.token. Tokens issued with explicit mcp_access expire after one hour. Chat-only tokens issued without it retain their existing non-expiring behavior, including for Widget v4 integrations. For personal connections, have your frontend request a replacement from your authenticated backend before expiry—for example, after 50 minutes—and update the widget’s user.token. A replacement for the same signed owner preserves the current session; changing the signed user or account starts a separate widget context. Renew promptly when returning to a suspended tab, and recheck permissions on every renewal. Existing chat-only tokens must be replaced using explicit mcp_access to enable personal connections. Existing chat-only integrations do not need to add token renewal or update their widget. For the September 15 token-expiry incident, the backend restores chat access for affected, correctly signed tokens issued during the rollout with an empty personal-access grant. This recovery preserves the previous chat authentication behavior without requiring customer code changes, a widget update, or a page reload. It does not restore personal-tool access or apply to explicitly scoped account/server grants. Signature, organization, contact, and session ownership checks still apply. When a tool needs access, the widget shows one Connect action. After approval it continues the request in the background. Users do not need an OpenCX dashboard account. Provider credentials stay on the backend; the organization API key must never be passed to the widget. For customer-managed integrations, point the server definition at your authenticated gateway. Your gateway owns downstream provider credentials and permissions. The widget can open its setup page and retry the tool when the user returns; returning alone does not confirm access. Your gateway must verify the customer’s access when handling that retry. For a shared team account, choose Our team — one shared account and connect it in Actions instead. Starting a new session clears the session’s messages and pending connection prompts, while keeping the same signed user/account’s saved connections. Connection controls and requests require streaming: true and capabilities.connections: true in Widget 5.0.0; omission keeps connections off. Enforce access restrictions separately through your backend’s mcp_access settings. Ownership is fixed when the server is created. Changing a personal server’s URL requires explicit acknowledgement that its users will need to connect again.

Reuse a Customer Connection in Your Backend

Enable Reuse connections in your backend on a server configured as Each user — their own account. This is off by default. Enable it before the customer connects; existing connections need a successful reconnect to produce a readiness notification. OpenCX completes the normal connection flow and manages encrypted credentials and refresh. Your backend receives a readiness notification and can retrieve the current access token. You do not need to build a gateway for this path.
  1. Configure your webhook endpoint in Settings → Webhooks and subscribe to connection.ready if your endpoint filters events.
  2. Create a backend API key with Connection credentials → Retrieve access tokens (connections:tokens). Keep it on your server.
  3. Authenticate widget users as described above. Include account_id when accounts must be isolated.
  4. After the user connects, verify the webhook signature using your endpoint’s signing secret. Deduplicate on event_id and save the connection ID against the matching customer/account.
  5. Retrieve the access token from your backend when needed.
Example webhook payload:
contact_id identifies the OpenCX contact, not an email or your application’s user ID. Map it to your authenticated customer before using the connection. account_id is the value supplied during authentication, or null when omitted.
Successful response (201):
Omit account_id in the request when authentication omitted it. The organization, contact and account must all match. The API checks the connection with its service and uses the same refresh lock as agent calls. expires_at and scope are null if the service did not supply them. Only reusable bearer access tokens are supported; refresh tokens, client secrets and identity tokens are never returned. Widget user tokens cannot call this endpoint. Use the access token only with the service/resource and permissions it was issued for. An MCP access token does not automatically grant access to a separate bookkeeping API. Reusing that API directly requires the provider to issue a token valid for it. Keep retrieved tokens on your backend, out of browsers and logs. OpenCX retains refresh ownership; retrieve a new access token when required instead of maintaining a second refresh loop. Readiness events are saved with the grant and submitted in the background, normally within 30 seconds. Temporary submission failures retry; configured webhook delivery also retries failures at your endpoint. Delivery can be repeated, so handlers must be idempotent. Readiness describes a completed authorization, not a permanent guarantee of access. Reconnects produce a new event; routine token refreshes do not. 403 means the API key or token type is not permitted. 404 means the connection does not match the requested owner or sharing is disabled. 409 with connection_required means the customer must reconnect. 502 means the connected service could not be reached or validated; retry later. Disabling sharing prevents future retrieval, and disconnecting removes the stored grant. Already retrieved access tokens can remain valid until they expire or are revoked by their issuer.

Extra Identity Controls

Scope History With externalId

Use when the same person can belong to multiple accounts, workspaces, or tenants inside your product.
This keeps history aligned to the right account context instead of treating every conversation for that email address as one shared history.

Prefill Or Collect More Fields

name and email are the visitor details the widget can show or collect. fills those fields before the visitor types, while adds more fields to the collection form. If you are using the widget’s built-in data collection flow, you can:
  • prefill name and email with prefillUserData
  • collect additional fields with extraDataCollectionFields
These are good onboarding improvements for public support flows, but they do not replace verified identity.

API Notes For Server-Side Authentication

The widget authentication API is designed for your backend:

1. Call the endpoint

Call POST /widget/authenticate-user from your backend.

2. Use the right API key

Authenticate with an org API key that has .

3. Return or store the token

Store the contact token in your app flow or return it to the frontend.

4. Pass it to user.token

Give the widget the verified token through user.token.

Widget Authentication API

Open the full API contract, request example, and response shape for authenticated contact tokens.

If Your AI Actions Need Request Data

, , and do not identify the visitor. They add request data to the HTTP calls your AI actions make to your own APIs.
Use widget authentication to decide who the visitor is. Use action request fields to decide what your AI action sends to your API. If you need help with that second part, see Authentication.

Renew identity without losing the active session

Widget 5.0.0 keeps the current session when you replace an OpenCX-issued user.token for the same signed owner and access scope. Renew through your authenticated backend and pass the new token into the existing <Widget options={...} />. For the HTML embed, call initOpenScript again with the complete current configuration and renewed user.token; keep the same root and mounted integration. Changing the widget organization, signed contact, account scope or externalId resets the active widget state so the previous owner’s messages do not remain visible. On logout, remove the old verified token before showing anonymous chat. Returning to an account loads only that account’s permitted history. Opaque or unrecognized token formats use a conservative reset rather than assuming the owner is unchanged. The widget does not mint replacement tokens. Chat-only authentication keeps its existing lifetime; upgrading the client does not introduce a new authentication endpoint or make polling require streaming. Test renewal and account switching with your real server-issued tokens, including after a tab has been suspended.

MCP form requests

A connected MCP server can ask the authenticated user to complete a form inside the widget. The form identifies the requesting service and supports text, numbers, booleans, and single- or multiple-choice fields. The user reviews the fields and chooses Submit, Decline, or Cancel. Multiple-choice fields enforce the server’s minimum and maximum selection counts and show a correction message without discarding the selections. Answers are checked against the server’s schema and returned to the original tool call; the tool is not restarted. Requests expire after 50 seconds and cancel without approval. Only the matching user, account, and session can submit an answer. For a simple approval request, users can choose Approve once or Always allow beside it. Remembered approval applies only to the same connected account, server, tool, inputs, and form. Changed requests still ask for approval. Other forms always require a response.

Manage personal connections and approvals

From the widget session list, open Connections and select a service. This screen contains services connected by the signed-in user/account; shared administrator connections are not shown.
  • Saved approvals → Remove approval removes one remembered approval. The next matching request asks again; the service stays connected.
  • Disconnect removes that personal connection’s access and saved approvals. The user must connect again before using it.
  • Successfully reconnecting replaces the saved credentials and clears remembered approvals. Starting or cancelling replacement authorization does not discard the existing credentials.
These settings belong to the signed user/account, so starting a new session does not reset them. Forms must not collect passwords, access tokens, or payment credentials. Use an external connection page for those interactions. MCP form approval is separate from OAuth consent and from any approval policy you configure for actions.

Install Widget

Load the widget with HTML, React, or headless setup.

Configuration

Prefill data, prompts, routing, and advanced widget behavior.

Conversation Sessions

See how identity choices change history and continuity.

Mobile Support

Use verified contact tokens inside mobile WebViews.