Security model and privacy
This page explains how AgentMySite protects your data and your visitors’ data. It’s written for developers who install the widget and for security reviewers who assess it.
Architecture at a glance
Section titled “Architecture at a glance”AgentMySite has three parts:
- The admin panel (
app.agentmysite.com), where you manage sites, content, tools, and conversations. - The API (
api.agentmysite.com), which serves the admin panel and the widget. - The widget (
widget.agentmysite.com): a small loader script that runs on your page, and a chat app that runs in a sandboxed iframe from the widget host.
Your content is processed by an ingestion worker. It extracts text from your pages and files, splits it into passages, and computes embeddings, which are numeric representations used for search. The text and embeddings are stored in PostgreSQL with the pgvector extension. When a visitor asks a question, the API finds the most relevant passages and asks a large language model (LLM) to answer from them.
Data isolation between customers
Section titled “Data isolation between customers”Each account’s data is isolated from every other account’s:
- Every database query on customer-owned data is limited to the account or site that comes from the authenticated request. A site ID sent in a request body or query string is never trusted.
- If you request another account’s resource in the admin panel, the API returns
404 Not Found, so it doesn’t even confirm that the resource exists. - Automated tests check that one account can’t read another account’s data.
- On the widget side, every request is limited to one site and one visitor. Both come from the signed widget token, never from the request.
Two separate authentication surfaces
Section titled “Two separate authentication surfaces”The admin panel and the widget use different tokens, and neither can stand in for the other.
| Name | Description |
|---|---|
Admin token | Issued after you sign in to the admin panel with an emailed one-time code. Accepted only by admin endpoints. |
Widget token | Issued to a visitor’s browser by POST /widget/session. Covers exactly one site and one visitor. Accepted only by widget endpoints (chat, conversation history, support tickets). |
The two token types are signed with different secrets and carry a different audience (aud) claim. The API checks both, so a widget token is always rejected by admin endpoints, and an admin token is always rejected by widget endpoints. Both are sent as Authorization: Bearer headers. The API doesn’t use cookies to authenticate either surface.
Widget tokens
Section titled “Widget tokens”- The loader sends the site key, the visitor ID, and the page’s
OrigintoPOST /widget/session. - The API checks the rate limits, looks up the site, and checks the
Originagainst the site’s allowed domains. - If every check passes, the API issues a signed token that lasts 5 minutes. When it expires, the widget gets a new one automatically, and the visitor doesn’t notice.
- If the site key is unknown, or the domain isn’t allowed, the API returns the same
401 Unauthorizedresponse, so a caller can’t tell which check failed. The loader then shows nothing.
The token is kept in memory only. It’s passed to the chat iframe by postMessage and is never written to localStorage, cookies, or URLs. The site key never enters the iframe.
Allowed domains
Section titled “Allowed domains”You list the domains where the widget may run in your site settings. The API compares the hostname of the request’s Origin header with that list:
- The match is exact and ignores case. The scheme and port are ignored.
www.example.comandexample.comare different domains. Add each one you use.- Subdomains aren’t included automatically.
The site key is public by design: it’s in your page source. The domain check stops other websites from showing your assistant in visitors’ browsers. Because the site key is public, treat the widget like any public endpoint. Rate limits and your plan’s credits cap how much it can be used.
Rate limits
Section titled “Rate limits”| Name | Default | Description |
|---|---|---|
Session tokens per site key | 60 per minute | Applies to POST /widget/session. |
Session tokens per origin | 30 per minute | Applies to POST /widget/session. |
Chat messages per visitor | 20 per 10 minutes | Includes tool results sent back to the assistant. |
Chat messages per site | 200 per 10 minutes | Across all visitors of one site. |
Message length | 2,000 characters | Longer messages are rejected with 400. |
When a limit is reached, the API responds with 429 Too Many Requests and a Retry-After header, and the widget tells the visitor that the message couldn’t be sent.
The assistant also stops answering when a site has used all its credits for the billing period. Visitors then see “This assistant is unavailable right now. Please try again later or contact us another way.” instead of an answer.
Widget isolation in your page
Section titled “Widget isolation in your page”- Sandboxed iframe. The chat runs in an iframe from
widget.agentmysite.comwithsandbox="allow-scripts allow-same-origin allow-top-navigation-by-user-activation". It can’t read your page’s DOM, cookies, or storage. It can navigate your tab only when the visitor clicks a link. - Checked messages. The loader and the iframe talk only through
postMessage. Each side drops any message that doesn’t come from the expected origin, or doesn’t carry the expected protocol marker and version. - Minimal page footprint. The loader adds a launcher button and the iframe to your page. It stores a random visitor ID and a few settings in your site’s storage (listed in Data the loader stores in the browser).
- Silent failure. If the widget can’t start, it shows nothing and doesn’t throw errors into your page.
- Page address. When the chat opens, the widget records the page’s origin and path. It leaves out the query string and fragment, which can contain personal data or tokens.
AgentActions safeguards
Section titled “AgentActions safeguards”AgentActions run in your page with your session, never on AgentMySite servers:
- The assistant can call only the tools you’ve enabled.
- The arguments are checked against your JSON Schema, and extra properties are never allowed. Calls that fail the check never reach your page.
- Tools that change something ask the visitor to confirm first.
- Your handler’s errors are passed to the assistant as messages, not as stack traces, and handlers time out after 15 seconds.
Your own server is still responsible for authenticating the user and authorizing each action.
Secrets
Section titled “Secrets”All secrets live in server environment variables only: the LLM provider key, the token-signing secrets, the email provider key, and payment credentials. They’re never in the repository or in any script sent to browsers. The admin and widget token secrets must be different values.
If you connect a helpdesk for support handoff, its credentials are encrypted at rest with AES-256-GCM.
Error responses to the widget are generic. A failure while an answer is streaming always produces the same message (“An error occurred. Please try again.”) and never includes internal details.
Content Security Policy
Section titled “Content Security Policy”If your site sends a Content-Security-Policy header, allow these hosts:
| Name | Type | Description |
|---|---|---|
script-src | https://widget.agentmysite.com | Loads the loader script, /loader/widget.js. |
frame-src | https://widget.agentmysite.com | Shows the chat iframe from /iframe/. |
connect-src | https://api.agentmysite.com | The loader requests a chat session from your page. |
Content-Security-Policy: script-src 'self' https://widget.agentmysite.com; frame-src https://widget.agentmysite.com; connect-src 'self' https://api.agentmysite.comMerge these hosts into your existing policy; don’t replace it. Also note:
- Inline styles. The loader adds one small inline
<style>element and an inline style, both for the launcher’s loading spinner. If yourstyle-srcdoesn’t allow'unsafe-inline', the widget still works, but the spinner doesn’t animate. - Nonces. If your
script-srcuses nonces or'strict-dynamic', add your nonce to the widget<script>tag. - Trusted Types. The loader sets the launcher icons with
innerHTML. It doesn’t work on pages that enforcerequire-trusted-types-for 'script'. - Referrer-Policy. The widget doesn’t depend on the
Refererheader, so anyReferrer-Policyworks.
The chat iframe’s own requests to the API come from the widget host, so your policy doesn’t govern them.
What data AgentMySite stores
Section titled “What data AgentMySite stores”| Name | Description |
|---|---|
Account | Your email address (used to sign in with one-time codes), your sites, their allowed domains, and settings. |
Content | Pages AgentMySite crawled and files you uploaded, the text extracted from them, the passages, and their embeddings. Uploaded files are kept in object storage. Text and embeddings are in PostgreSQL with pgvector. |
Conversations | Visitor messages and assistant replies, the sources cited, and AgentActions tool calls and their results, linked to a random visitor ID. |
Visitor details you provide | Anything you pass in data-customer-id, data-customer-name, data-customer-email, or data-shop-domain, plus the hostname of the page the chat started on. |
Support tickets | If you enable support handoff, the contact details and summary the visitor confirms on the ticket card. They’re sent to the helpdesk you connected. |
Usage | Token counts and credit use for each conversation, for billing and your usage meter. |
Tool audit | Each tool call’s name, arguments, and outcome. Tool results aren’t recorded in the audit log. |
Embeddings are computed by AgentMySite’s own ingestion worker with an open-source model (multilingual E5). Your content isn’t sent to a third-party embedding service.
Third parties that process data
Section titled “Third parties that process data”| Provider | Purpose | Data involved |
|---|---|---|
| Anthropic | Large language model that writes answers | Visitor messages, conversation history, relevant passages from your content, tool definitions and results |
| Railway | Hosting for the API, widget, worker, database, and queue | All data stored by the service |
| Cloudflare (R2) | Object storage for uploaded files | Files you upload |
| Resend | Email delivery | Your email address (sign-in codes), and ticket notification emails |
| iCount | Payments and invoicing | Billing details of paying accounts |
| Microlink | Page screenshots for the try-it demo on agentmysite.com | The URL of the site being demoed |
If you connect a helpdesk (such as Clokio) for support handoff, ticket details go to that helpdesk under your own account with it.
Retention, deletion, and agreements
Section titled “Retention, deletion, and agreements”For data retention periods, deletion requests, a data processing agreement (DPA), or a completed security questionnaire, contact us at support@agentmysite.com.
Report a security issue
Section titled “Report a security issue”If you think you’ve found a vulnerability, email support@agentmysite.com with “Security” in the subject line. Please don’t disclose it publicly until we’ve had a chance to fix it.
Was this page helpful?
Thanks for the feedback.