The widget is two artifacts served from a single dedicated origin (chat-embed.circuit.ai):
launcher.js: a ~2 KB self-executing script loaded by the customer’s <script> tag. Runs on the host page. Renders the bubble and, on click, injects an <iframe>.
The chat SPA: a React 19 application served inside the iframe at chat-embed.circuit.ai/embed. Communicates with the launcher via versioned postMessage envelopes.
No Circuit code runs on your servers. The launcher does not read or modify your page outside its own DOM elements.
Every request from both the launcher and the SPA carries Authorization: Bearer <apiKey>.
Each key is bound to exactly one agent. It grants access to chat with that agent only: no workspace data, no document access, no admin operations.
Visitors are anonymous. The key represents the agent, not any visitor. No visitor identity is collected or verifiable.
Actions marked admin-only or hidden-in-chat on the agent are stripped server-side before the config and agent responses are sent. They are never exposed to widget visitors regardless of who created the key.
Keys do not expire automatically. Revoke or rotate them from the agent’s Chatbot settings; revocation takes effect immediately.
The API key is visible in your page’s HTML source. The domain allowlist is the control that prevents its use from other sites. A key with an empty allowed domains list rejects all browser requests and cannot be used from any site.
Every key has an Allowed Website Domains list, enforced server-side on every request:
The browser sends an Origin header on cross-origin requests. The server checks the origin’s hostname against the key’s list and rejects requests from unlisted hostnames with 403 Forbidden.
The check fails closed: a key with an empty list rejects all requests.
Matching is by exact hostname, case-insensitive, ignoring port and scheme. example.com does not cover www.example.com. Add each hostname separately.
This prevents other websites from embedding your widget. It does not prevent non-browser clients from calling the API with the key directly; the key’s narrow single-agent scope and rate limit are the controls on that path.
The widget uses X-Session-Token headers to maintain a chat across turns. The widget does not set any cookies.Third-party cookies are blocked or partitioned by most browsers in cross-origin iframe contexts. The SPA uses localStorage instead:
On the first message, the SPA generates a UUID session token, stores it in localStorage under the key circuit-chat-embed:session:<apiKey>, and sends it as X-Session-Token on every subsequent request.
The server responds with X-Session-Token: <token> on session-creating requests, so a client that lost its localStorage entry can pick the token back up.
Sessions are scoped per (apiKey, browser). A session token bound to a different agent key returns 404 rather than crossing to another agent’s chats.
Sessions expire 24 hours after creation.
If a visitor’s browser blocks localStorage (sandboxed iframe, private mode), the widget still works but the chat resets between page loads.
Widget traffic is limited to 60 requests per minute per API key, counted across all visitors using that key. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Requests over the limit receive 429 Too Many Requests.
connect-src: API calls and streaming responses from the visitor’s browser.
img-src data:: only needed if your agent returns inline images in responses.
The Chat Widget HTML section on the Chatbot settings page shows a copyable CSP snippet.The SPA response is served with Content-Security-Policy: frame-ancestors * so the iframe can be embedded on any customer site.
The messages the visitor types, and standard request metadata (IP address, user agent) as with any HTTPS request.
No page content, form data, analytics identifiers, or browsing history is read or transmitted.
Data returned to the visitor:
Agent responses generated from the agent’s configured reference scope, including citations to source documents. Review the agent’s reference scope before launch: anything the agent can search can appear in an answer to an anonymous visitor.
Conversations are stored in your workspace and visible to workspace users with access to the agent.
The launcher and SPA validate every inbound postMessage on both sides. Launcher-to-SPA messages use targetOrigin = https://chat-embed.circuit.ai. The launcher rejects inbound messages whose event.origin does not equal the embed origin or whose event.source is not the iframe’s contentWindow, which prevents sibling frames on the embed origin from spoofing the SPA. The SPA rejects inbound messages whose event.origin does not match the host page’s origin (injected by the launcher in the initial CONFIG), whose event.source is not window.parent, or where the payload’s declared hostOrigin disagrees with the actual event.origin.
Agent’s reference scope contains only content approved for anonymous public access.
API key created with a descriptive name and its allowed domains list populated with exact production hostnames.
Staging and localhost testing uses a separate key.
No cookies are set by the widget. Session continuity relies on localStorage.
CSP updated if applicable.
Key rotation owner identified. Rotation process: create new key, update script tag, revoke old key.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Circuit","colors":{"primary":"#0E7490"},"logo":{"light":"https://mintcdn.com/circuit-8418b84f/aZp0fQmsCx8DgULK/images/logo.svg?fit=max\u0026auto=format\u0026n=aZp0fQmsCx8DgULK\u0026q=85\u0026s=0b64ab7d194d96fcc410e1befd9f8123","dark":"https://mintcdn.com/circuit-8418b84f/aZp0fQmsCx8DgULK/images/logo-dark.svg?fit=max\u0026auto=format\u0026n=aZp0fQmsCx8DgULK\u0026q=85\u0026s=01d3e954b346c6d5a42ccadda7286570","href":"https://circuit.ai"},"favicon":"https://mintcdn.com/circuit-8418b84f/fx8MsrDk1NU7g3Uz/images/favicon.png?fit=max\u0026auto=format\u0026n=fx8MsrDk1NU7g3Uz\u0026q=85\u0026s=5906407080d91117ad3d05d2fab86f95","footer":{"links":[{"header":"Circuit","items":[{"label":"Log In","href":"https://app.circuit.ai/sign-in"},{"label":"circuit.ai","href":"https://circuit.ai"}]},{"header":"Legal","items":[{"label":"Terms of Service","href":"https://circuit.ai/terms-of-service"},{"label":"Privacy Policy","href":"https://circuit.ai/privacy-policy"},{"label":"Accessibility","href":"https://circuit.ai/accessibility"}]}]},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"circuit-8418b84f","hasAudioNarration":false,"actualSubdomain":"circuit-8418b84f","customDomains":["docs.circuit.ai"],"gitSource":{"type":"github","owner":"molecula","repo":"circuit-docs","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6a98c33e6e408a84d280c94f","clientVersion":"0.0.4094","searchFilterCounts":[{"language":"default","tag":"Manage","count":30},{"language":"default","tag":"Integrations","count":17},{"language":"default","tag":"Circuit University","count":15},{"language":"default","tag":"IT \u0026 Integration","count":11},{"language":"default","tag":"Learn Circuit","count":9},{"language":"default","tag":"Agents","count":8},{"language":"default","tag":"Embedded chat","count":8},{"language":"default","tag":"Use cases","count":6},{"language":"default","tag":"Get started","count":5},{"language":"default","tag":"Data management","count":3}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":"footer a[href*=\"mintlify.com\"]{display:none!important}footer>div>div:has(a[href*=\"mintlify.com\"]){display:none!important}button[aria-label=\"More actions\"]{display:none!important}"}],"page":{"pagePath":"it/chat-widget/security","slug":"/it/chat-widget/security","pageMetadata":{"title":"Chat widget security","description":"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.","href":"/it/chat-widget/security"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(_components.p,{children:`This page is written for the security or IT reviewer approving the chat widget for deployment.`}),`\n`,_jsx(Heading,{level:`2`,id:`architecture-summary`,children:`Architecture summary`}),`\n`,_jsxs(_components.p,{children:[`The widget is two artifacts served from a single dedicated origin (`,_jsx(_components.code,{children:`chat-embed.circuit.ai`}),`):`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`launcher.js`}),`: a ~2 KB self-executing script loaded by the customer’s `,_jsx(_components.code,{children:`\u003cscript>`}),` tag. Runs on the host page. Renders the bubble and, on click, injects an `,_jsx(_components.code,{children:`\u003ciframe>`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`The chat SPA: a React 19 application served inside the iframe at `,_jsx(_components.code,{children:`chat-embed.circuit.ai/embed`}),`. Communicates with the launcher via versioned `,_jsx(_components.code,{children:`postMessage`}),` envelopes.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`No Circuit code runs on your servers. The launcher does not read or modify your page outside its own DOM elements.`}),`\n`,_jsx(Heading,{level:`2`,id:`authentication-and-authorization`,children:`Authentication and authorization`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Every request from both the launcher and the SPA carries `,_jsx(_components.code,{children:`Authorization: Bearer \u003capiKey>`}),`.`]}),`\n`,_jsx(_components.li,{children:`Each key is bound to exactly one agent. It grants access to chat with that agent only: no workspace data, no document access, no admin operations.`}),`\n`,_jsx(_components.li,{children:`Visitors are anonymous. The key represents the agent, not any visitor. No visitor identity is collected or verifiable.`}),`\n`,_jsx(_components.li,{children:`Actions marked admin-only or hidden-in-chat on the agent are stripped server-side before the config and agent responses are sent. They are never exposed to widget visitors regardless of who created the key.`}),`\n`,_jsx(_components.li,{children:`Keys do not expire automatically. Revoke or rotate them from the agent’s Chatbot settings; revocation takes effect immediately.`}),`\n`]}),`\n`,_jsx(Warning,{children:_jsx(_components.p,{children:`The API key is visible in your page’s HTML source. The domain allowlist is the control that prevents its use from other sites. A key with an empty allowed domains list rejects all browser requests and cannot be used from any site.`})}),`\n`,_jsx(Heading,{level:`2`,id:`domain-allowlisting`,children:`Domain allowlisting`}),`\n`,_jsx(_components.p,{children:`Every key has an Allowed Website Domains list, enforced server-side on every request:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The browser sends an `,_jsx(_components.code,{children:`Origin`}),` header on cross-origin requests. The server checks the origin’s hostname against the key’s list and rejects requests from unlisted hostnames with `,_jsx(_components.code,{children:`403 Forbidden`}),`.`]}),`\n`,_jsx(_components.li,{children:`The check fails closed: a key with an empty list rejects all requests.`}),`\n`,_jsxs(_components.li,{children:[`Matching is by exact hostname, case-insensitive, ignoring port and scheme. `,_jsx(_components.code,{children:`example.com`}),` does not cover `,_jsx(_components.code,{children:`www.example.com`}),`. Add each hostname separately.`]}),`\n`,_jsx(_components.li,{children:`This prevents other websites from embedding your widget. It does not prevent non-browser clients from calling the API with the key directly; the key’s narrow single-agent scope and rate limit are the controls on that path.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`sessions`,children:`Sessions`}),`\n`,_jsxs(_components.p,{children:[`The widget uses `,_jsx(_components.code,{children:`X-Session-Token`}),` headers to maintain a chat across turns. `,_jsx(_components.strong,{children:`The widget does not set any cookies.`})]}),`\n`,_jsxs(_components.p,{children:[`Third-party cookies are blocked or partitioned by most browsers in cross-origin iframe contexts. The SPA uses `,_jsx(_components.code,{children:`localStorage`}),` instead:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`On the first message, the SPA generates a UUID session token, stores it in `,_jsx(_components.code,{children:`localStorage`}),` under the key `,_jsx(_components.code,{children:`circuit-chat-embed:session:\u003capiKey>`}),`, and sends it as `,_jsx(_components.code,{children:`X-Session-Token`}),` on every subsequent request.`]}),`\n`,_jsxs(_components.li,{children:[`The server responds with `,_jsx(_components.code,{children:`X-Session-Token: \u003ctoken>`}),` on session-creating requests, so a client that lost its `,_jsx(_components.code,{children:`localStorage`}),` entry can pick the token back up.`]}),`\n`,_jsx(_components.li,{children:`Sessions are scoped per (apiKey, browser). A session token bound to a different agent key returns 404 rather than crossing to another agent’s chats.`}),`\n`,_jsx(_components.li,{children:`Sessions expire 24 hours after creation.`}),`\n`,_jsxs(_components.li,{children:[`If a visitor’s browser blocks `,_jsx(_components.code,{children:`localStorage`}),` (sandboxed iframe, private mode), the widget still works but the chat resets between page loads.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`rate-limiting`,children:`Rate limiting`}),`\n`,_jsxs(_components.p,{children:[`Widget traffic is limited to 60 requests per minute per API key, counted across all visitors using that key. Responses include `,_jsx(_components.code,{children:`X-RateLimit-Limit`}),`, `,_jsx(_components.code,{children:`X-RateLimit-Remaining`}),`, and `,_jsx(_components.code,{children:`X-RateLimit-Reset`}),` headers. Requests over the limit receive `,_jsx(_components.code,{children:`429 Too Many Requests`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`content-security-policy`,children:`Content Security Policy`}),`\n`,_jsxs(_components.p,{children:[`The launcher and the iframe share one origin, so one host covers both `,_jsx(_components.code,{children:`script-src`}),` and `,_jsx(_components.code,{children:`frame-src`}),`:`]}),`\n`,_jsx(CodeBlock,{numberOfLines:`5`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`5`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`script-src https://chat-embed.circuit.ai;`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`frame-src https://chat-embed.circuit.ai;`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`connect-src https://api.circuit.ai;`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`img-src data:;`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`script-src`}),`: the launcher script.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`frame-src`}),`: the chat SPA iframe.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`connect-src`}),`: API calls and streaming responses from the visitor’s browser.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`img-src data:`}),`: only needed if your agent returns inline images in responses.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`The Chat Widget HTML section on the Chatbot settings page shows a copyable CSP snippet.`}),`\n`,_jsxs(_components.p,{children:[`The SPA response is served with `,_jsx(_components.code,{children:`Content-Security-Policy: frame-ancestors *`}),` so the iframe can be embedded on any customer site.`]}),`\n`,_jsx(Heading,{level:`2`,id:`data-flow`,children:`Data flow`}),`\n`,_jsx(_components.p,{children:`Data sent from the visitor’s browser to Circuit:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`The messages the visitor types, and standard request metadata (IP address, user agent) as with any HTTPS request.`}),`\n`,_jsx(_components.li,{children:`No page content, form data, analytics identifiers, or browsing history is read or transmitted.`}),`\n`]}),`\n`,_jsx(_components.p,{children:`Data returned to the visitor:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Agent responses generated from the agent’s configured reference scope, including citations to source documents. Review the agent’s reference scope before launch: anything the agent can search can appear in an answer to an anonymous visitor.`}),`\n`]}),`\n`,_jsx(_components.p,{children:`Conversations are stored in your workspace and visible to workspace users with access to the agent.`}),`\n`,_jsx(Heading,{level:`2`,id:`postmessage-security-model`,children:`postMessage security model`}),`\n`,_jsxs(_components.p,{children:[`The launcher and SPA validate every inbound `,_jsx(_components.code,{children:`postMessage`}),` on both sides. Launcher-to-SPA messages use `,_jsx(_components.code,{children:`targetOrigin = https://chat-embed.circuit.ai`}),`. The launcher rejects inbound messages whose `,_jsx(_components.code,{children:`event.origin`}),` does not equal the embed origin or whose `,_jsx(_components.code,{children:`event.source`}),` is not the iframe’s `,_jsx(_components.code,{children:`contentWindow`}),`, which prevents sibling frames on the embed origin from spoofing the SPA. The SPA rejects inbound messages whose `,_jsx(_components.code,{children:`event.origin`}),` does not match the host page’s origin (injected by the launcher in the initial CONFIG), whose `,_jsx(_components.code,{children:`event.source`}),` is not `,_jsx(_components.code,{children:`window.parent`}),`, or where the payload’s declared `,_jsx(_components.code,{children:`hostOrigin`}),` disagrees with the actual `,_jsx(_components.code,{children:`event.origin`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`reviewer-checklist`,children:`Reviewer checklist`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Agent’s reference scope contains only content approved for anonymous public access.`}),`\n`,_jsx(_components.li,{children:`API key created with a descriptive name and its allowed domains list populated with exact production hostnames.`}),`\n`,_jsx(_components.li,{children:`Staging and localhost testing uses a separate key.`}),`\n`,_jsxs(_components.li,{children:[`No cookies are set by the widget. Session continuity relies on `,_jsx(_components.code,{children:`localStorage`}),`.`]}),`\n`,_jsx(_components.li,{children:`CSP updated if applicable.`}),`\n`,_jsx(_components.li,{children:`Key rotation owner identified. Rotation process: create new key, update script tag, revoke old key.`}),`\n`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Chat widget security","description":"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.","href":"/it/chat-widget/security"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Architecture summary","slug":"architecture-summary","depth":2,"children":[]},{"title":"Authentication and authorization","slug":"authentication-and-authorization","depth":2,"children":[]},{"title":"Domain allowlisting","slug":"domain-allowlisting","depth":2,"children":[]},{"title":"Sessions","slug":"sessions","depth":2,"children":[]},{"title":"Rate limiting","slug":"rate-limiting","depth":2,"children":[]},{"title":"Content Security Policy","slug":"content-security-policy","depth":2,"children":[]},{"title":"Data flow","slug":"data-flow","depth":2,"children":[]},{"title":"postMessage security model","slug":"postmessage-security-model","depth":2,"children":[]},{"title":"Reviewer checklist","slug":"reviewer-checklist","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"circuit-8418b84f","siteName":"Circuit","title":"Chat widget security - Circuit","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-03T00:48:19.594Z","originalFileLocation":"it/chat-widget/security.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Circuit\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Chat widget security - Circuit\">\u003cmeta data-mint-head property=\"og:description\" content=\"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Circuit\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.circuit.ai/it/chat-widget/security\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://circuit-8418b84f.mintlify.site/_mintlify/api/og/v2?division=Embedded+chat\u0026amp;title=Chat+widget+security\u0026amp;description=Everything+a+security+review+of+the+Circuit+chat+widget+needs%3A+authentication%2C+sessions%2C+domain+allowlisting%2C+CSP%2C+rate+limits%2C+and+data+flow.\u0026amp;theme=5ce093a41655eb8f50a32ab0\u0026amp;signature=60a753a47c4ffaf6aef9f0a188405932eccfff39c41af7520d04fd68ce47098f\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Chat widget security - Circuit\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://circuit-8418b84f.mintlify.site/_mintlify/api/og/v2?division=Embedded+chat\u0026amp;title=Chat+widget+security\u0026amp;description=Everything+a+security+review+of+the+Circuit+chat+widget+needs%3A+authentication%2C+sessions%2C+domain+allowlisting%2C+CSP%2C+rate+limits%2C+and+data+flow.\u0026amp;theme=5ce093a41655eb8f50a32ab0\u0026amp;signature=60a753a47c4ffaf6aef9f0a188405932eccfff39c41af7520d04fd68ce47098f\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/circuit-8418b84f/cCW3w6ptuoiMCkOs/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Circuit\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0E7490\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.circuit.ai/it/chat-widget/security\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/it/chat-widget/security.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.circuit.ai/#organization\",\"name\":\"Circuit\",\"url\":\"https://docs.circuit.ai\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/circuit-8418b84f/aZp0fQmsCx8DgULK/images/logo.svg?fit=max\u0026auto=format\u0026n=aZp0fQmsCx8DgULK\u0026q=85\u0026s=0b64ab7d194d96fcc410e1befd9f8123\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.circuit.ai#website\",\"name\":\"Circuit\",\"url\":\"https://docs.circuit.ai\",\"publisher\":{\"@id\":\"https://docs.circuit.ai/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.circuit.ai/it/chat-widget/security#webpage\",\"url\":\"https://docs.circuit.ai/it/chat-widget/security\",\"name\":\"Chat widget security\",\"description\":\"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.\",\"dateModified\":\"2026-09-03T00:48:19.594Z\",\"isPartOf\":{\"@id\":\"https://docs.circuit.ai#website\"},\"breadcrumb\":{\"@id\":\"https://docs.circuit.ai/it/chat-widget/security#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.circuit.ai/it/chat-widget/security#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Embedded chat\",\"item\":\"https://docs.circuit.ai/it/chat-widget/overview\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Chat widget security\",\"item\":\"https://docs.circuit.ai/it/chat-widget/security\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.circuit.ai/it/chat-widget/security#article\",\"headline\":\"Chat widget security\",\"name\":\"Chat widget security\",\"description\":\"Everything a security review of the Circuit chat widget needs: authentication, sessions, domain allowlisting, CSP, rate limits, and data flow.\",\"url\":\"https://docs.circuit.ai/it/chat-widget/security\",\"mainEntityOfPage\":{\"@id\":\"https://docs.circuit.ai/it/chat-widget/security#webpage\"},\"dateModified\":\"2026-09-03T00:48:19.594Z\",\"publisher\":{\"@id\":\"https://docs.circuit.ai/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.circuit.ai#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Circuit\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Circuit","nav":{"global":null,"tabs":[{"tab":"Get started","pages":[{"title":"Get started","href":"/introduction"}]},{"tab":"Circuit University","pages":[{"title":"Circuit University","href":"/university/talking-to-an-agent"}]},{"tab":"Manage","pages":[{"title":"Manage","href":"/admin/users-and-permissions"}]},{"tab":"IT \u0026 Integration","groups":[{"group":"Security","pages":[{"title":"Security overview","href":"/it/security"},{"title":"SSO configuration","href":"/it/sso"},{"title":"Integration security and admin consent","href":"/it/integration-security"}]},{"group":"Embedded chat","pages":[{"title":"Chat widget overview","href":"/it/chat-widget/overview"},{"title":"Chat widget quickstart","href":"/it/chat-widget/quickstart"},{"title":"Chat widget configuration","description":"Configure the Circuit chat widget's appearance and behavior through the Circuit admin UI.","href":"/it/chat-widget/configuration"},{"title":"Chat widget security","href":"/it/chat-widget/security"},{"title":"Chat widget API reference","description":"The HTTP endpoints the Circuit chat widget uses: configuration, sending messages, streaming responses, and retrieving history.","href":"/it/chat-widget/api-reference"},{"title":"Chat widget features","href":"/it/chat-widget/features"},{"title":"Chat widget troubleshooting","href":"/it/chat-widget/troubleshooting"},{"title":"Chat widget changelog","href":"/it/chat-widget/changelog"}]}]}]},"basePath":"","title":"Chat widget security - Circuit","buildId":"dpl_42LwxxU1kGN114H4XzwGYXa9ZDah","deploymentId":"dpl_42LwxxU1kGN114H4XzwGYXa9ZDah","dynamics":[],"chunks":[]}