Skip to content

Security model and privacy

View as Markdown

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.

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.

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.

The admin panel and the widget use different tokens, and neither can stand in for the other.

Token types
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.

  1. The loader sends the site key, the visitor ID, and the page’s Origin to POST /widget/session.
  2. The API checks the rate limits, looks up the site, and checks the Origin against the site’s allowed domains.
  3. 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.
  4. If the site key is unknown, or the domain isn’t allowed, the API returns the same 401 Unauthorized response, 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.

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.com and example.com are 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.

Widget 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.

  • Sandboxed iframe. The chat runs in an iframe from widget.agentmysite.com with sandbox="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 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.

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.

If your site sends a Content-Security-Policy header, allow these hosts:

CSP directives for the widget
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.com

Merge 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 your style-src doesn’t allow 'unsafe-inline', the widget still works, but the spinner doesn’t animate.
  • Nonces. If your script-src uses 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 enforce require-trusted-types-for 'script'.
  • Referrer-Policy. The widget doesn’t depend on the Referer header, so any Referrer-Policy works.

The chat iframe’s own requests to the API come from the widget host, so your policy doesn’t govern them.

Stored data
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.

ProviderPurposeData involved
AnthropicLarge language model that writes answersVisitor messages, conversation history, relevant passages from your content, tool definitions and results
RailwayHosting for the API, widget, worker, database, and queueAll data stored by the service
Cloudflare (R2)Object storage for uploaded filesFiles you upload
ResendEmail deliveryYour email address (sign-in codes), and ticket notification emails
iCountPayments and invoicingBilling details of paying accounts
MicrolinkPage screenshots for the try-it demo on agentmysite.comThe 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.

For data retention periods, deletion requests, a data processing agreement (DPA), or a completed security questionnaire, contact us at support@agentmysite.com.

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.