Cloudstation

Website Chat Widget

Add a secure AI support chat to your website using a public widget ID without exposing Agents API credentials.

The Website Chat Widget adds a customer-facing AI chat bubble to a website. Visitors can message a selected CloudStation agent from your site without seeing Agents API keys, client IDs, Project IDs, owner IDs, or private agent template IDs.

Use the widget for browser-based chat. Use the Agents API for trusted backend integrations.

The widget is browser-safe because the page only receives a public widget ID and public display configuration. CloudStation keeps routing, Agents API credentials, selected agent template IDs, and signing secrets on the backend.

Agents API vs Website Chat Widget

NeedUse Agents APIUse Website Chat Widget
n8n, Zapier, MakeYesNo
Backend applicationYesNo
Telegram or WhatsApp botYesNo
Public website chat bubbleNoYes
Browser JavaScriptNoYes
Requires x-api-key / x-client-idYesNo
Uses public widget_idNoYes
Can be copied into static HTMLNoYes

How It Works

Website loads the script

Your site loads CloudStation's hosted widget script, usually:

<script src="https://app.cloud-station.io/widgets/chat.js"></script>

Script reads public attributes

The script reads data-widget-id, visual colors, position, and optional labels from the script tag.

Widget fetches public config

The browser calls:

GET /v1/widgets/{widget_id}/config

This returns safe display data such as display name, avatar URL, welcome message, suggested prompts, and whether login is required.

Visitor starts a conversation

The widget calls the backend to create a widget conversation. CloudStation returns a signed widget conversation token that is safe to store in the visitor's browser.

Messages route privately

The browser sends messages using the widget token. CloudStation routes them to the configured Project and selected agent privately.

Assistant replies appear in the widget

The widget polls the conversation and shows its messages in the order CloudStation stored them, so each reply sits under the question it answers. It renders safe markdown in the chat panel.

Slow answers are not dropped. After a minute the widget shows "Still working on it…" and keeps waiting for up to five minutes, or for as long as a teammate has taken over the conversation. After that it shows "No reply yet. It will appear here when it arrives." and the reply appears as soon as it does, while the chat panel is open. Visitors can keep sending messages while a reply is pending.

What the Browser Can See

The browser can see:

  • public widget ID
  • display name
  • avatar URL
  • welcome message
  • suggested prompts
  • visual styling attributes
  • widget conversation token for that visitor/conversation

The browser cannot see:

  • Agents API key
  • client ID
  • Project ID
  • owner user ID
  • selected agent template ID
  • backend routing rules
  • AGENT_WIDGET_TOKEN_SECRET
  • private Project settings

Setup in Project Settings

Open Project Settings

Navigate to your Project and open the settings drawer.

Expand Website Chat Widget

Open the Website Chat Widget section.

Pick the support agent

Choose which CloudStation agent should answer visitor messages.

Customize the chat experience

Set the visitor-facing display name, avatar override, first message (the welcome message), and suggested prompts.

Configure allowed websites

Add each origin where the widget is allowed to run, such as https://example.com and https://www.example.com.

Copy the install code

Paste the script snippet into your website, usually before </body> or in your site builder's custom code area.

Install Snippet

Production example:

<script
  src="https://app.cloud-station.io/widgets/chat.js"
  data-widget-id="customer-support"
  data-api-base-url="https://charlie-back.cloud-station.io"
  data-primary-color="#f97316"
  data-accent-color="#fed7aa"
  data-surface-color="#fffaf3"
  data-position="right"
  data-launcher-label="Ask support">
</script>

Minimal example:

<script
  src="https://app.cloud-station.io/widgets/chat.js"
  data-widget-id="customer-support"
  data-api-base-url="https://charlie-back.cloud-station.io">
</script>

Local development example:

<script
  src="http://localhost:5174/widgets/chat.js"
  data-widget-id="cloudstation-demo-public"
  data-api-base-url="http://localhost:8082"
  data-launcher-label="Ask support">
</script>

The script can be placed in <head> or before </body>. If it is placed in <head>, the widget mounts on the document root because <body> does not exist yet.

Script Attributes

AttributeRequiredDescriptionExample
data-widget-idYesPublic widget ID configured in Project Settings.customer-support
data-api-base-urlNoCloudStation API base URL. Defaults to https://charlie-back.cloud-station.io when omitted.https://charlie-back.cloud-station.io
data-primary-colorNoMain action/launcher color.#f97316
data-accent-colorNoAccent and user bubble color.#fed7aa
data-surface-colorNoChat panel background.#fffaf3
data-text-colorNoMain text color.#20130b
data-muted-colorNoSecondary text color.#7b6150
data-positionNoLauncher side.right or left
data-themeNoTheme hint.light or dark
data-launcher-labelNoText shown beside the launcher icon.Ask support
data-brandingNoWhether to show footer attribution.true or false
data-brand-urlNoFooter brand link.https://cloud-station.io
data-conversation-idle-minutesNoMinutes without a message, sent or received, after which the widget starts a new conversation. Defaults to 60.30

The default footer attribution is:

Built on CloudStation by Charlie

This is product attribution. It does not mean the selected support agent is named Charlie.

Widget Identity: Name and Avatar

The visible agent identity comes from backend widget config.

Display name priority:

  1. Widget custom display name
  2. Selected agent template name
  3. Customer Support

Avatar priority:

  1. Widget custom avatar URL
  2. Selected agent template image
  3. Widget fallback icon

The browser receives only the safe final display name and avatar URL. It does not receive the selected agent_template_id.

Welcome Message and Suggested Prompts

Use the welcome message to explain what visitors can ask.

Good examples:

Hi! I can help with CloudStation setup, billing, integrations, and troubleshooting.
Tell me what you are trying to build and I will point you to the right next step.

Suggested prompts should be short and specific:

How do I deploy an app?
Connect WhatsApp to my project
Where do I find API keys?
Explain pricing

Allowed Websites

Allowed websites protect your widget from being embedded on unauthorized domains. Add origins, not full page URLs.

Good:

https://example.com
https://www.example.com
https://docs.example.com
http://localhost:8899
http://127.0.0.1:8899

Bad:

example.com
https://example.com/pricing
*.example.com
file://...
null

Rules:

  • Include protocol: https:// or http://.
  • Include port for local development.
  • Do not include page paths.
  • Add both apex and www domains if both serve your site.
  • Do not allow null origins.
  • Do not use file:// for testing.

Public vs Login-Required Widgets

Widgets can be public or login-required.

ModeSettingBest for
Public website chatRequire login = falseMarketing sites, docs sites, public support pages.
Authenticated in-app supportRequire login = trueProduct dashboards, internal apps, logged-in customer portals.

If Require login is enabled and a visitor is not authenticated, the backend returns Authentication required and the widget shows an error instead of creating a public conversation. The hosted chat.js script never sends a login token, so login-required widgets only work from a client that sends the visitor's CloudStation bearer token, such as the CloudStation app itself.

Rate Limits

Project Settings exposes widget limits:

LimitMeaning
Messages per user/visitor per dayPer authenticated user when login is required; per anonymous visitor when public.
Messages per minuteBurst protection for a visitor/user.
Total widget messages per dayProject-wide daily cap for this widget.
Max message lengthMaximum length of a single visitor message.

Rate limits are enforced by the backend, not by the browser.

Markdown Rendering

Assistant messages support safe lightweight markdown:

  • **bold**
  • __bold__
  • *italic*
  • inline code using backticks
  • [links](https://example.com)
  • bullet lists
  • numbered lists
  • paragraphs

The widget escapes HTML before applying markdown formatting. It does not render arbitrary HTML from assistant messages.

Conversation Storage and Token Recovery

The widget stores visitor conversation state in localStorage so the thread continues while the visitor moves around your site.

Stored data includes:

  • widget ID
  • conversation ID
  • signed widget conversation token
  • the time of the last message sent or received

What visitors get:

  • History across pages. When a page loads, the widget restores the conversation and shows its messages under the welcome message.
  • Panel state. If the chat panel was open, it stays open on the next page. This is kept per browser tab in sessionStorage.
  • Several tabs, one thread. Every tab of your site shows the same conversation. A message sent in one tab appears in the others, and starting or clearing a conversation in one tab does the same in the others.
  • Idle expiry. After 60 minutes without a message, the widget starts a new conversation. Change this with data-conversation-idle-minutes. The backend token still expires after 30 days, whatever this setting.
  • New chat. The New chat button in the chat header asks for confirmation, then clears the conversation and shows the welcome message again. The next message starts a new conversation.
<script
  src="https://app.cloud-station.io/widgets/chat.js"
  data-widget-id="customer-support"
  data-conversation-idle-minutes="30">
</script>

The widget automatically recovers from stale conversation tokens. If the backend returns a recoverable token or conversation error, the widget clears the stale local conversation, creates a new conversation, and retries once.

Recoverable cases include:

  • invalid conversation token
  • expired conversation token
  • missing conversation token
  • widget mismatch
  • conversation mismatch
  • user mismatch
  • conversation not found

Local Testing

Do not test by double-clicking an HTML file. A file:// page has a browser origin of null, which CloudStation blocks for security.

Bad:

file:///Users/me/Downloads/test.html

Good:

cd ~/Downloads
python3 -m http.server 8899

Then open:

http://localhost:8899/test.html

Add the local origin to allowed websites:

http://localhost:8899
http://127.0.0.1:8899

If someone opens the widget from file://, the widget shows a friendly error explaining that the page must be served through a local web server.

Security Model

The widget uses backend-managed sessions and signed widget tokens.

Important properties:

  • The widget script is public and safe to load from a website.
  • widget_id is public; it is not a secret.
  • Project routing is private on the backend.
  • The selected agent template is private on the backend.
  • Agents API keys are never sent to the browser.
  • CORS is restricted to configured allowed websites.
  • file:// and null origins are rejected.
  • Rate limits are enforced server-side.
  • Conversation tokens are signed by backend secret and scoped to the widget/conversation/user.

The Website Chat Widget is safe for browser use. The Agents API is not. Never replace the widget with raw Agents API calls from a public website.

Troubleshooting

SymptomLikely causeFix
Widget does not appearScript URL failed to load or missing data-widget-idCheck browser console and script tag.
Could not reach the chat APIWrong API base URL, blocked origin, or backend unavailableConfirm data-api-base-url and allowed websites.
Friendly file:// errorPage opened directly from diskServe the page with python3 -m http.server.
Authentication requiredWidget has Require login = trueDisable the login requirement for public websites; chat.js sends no login token.
403 Widget origin is not allowed in the network tabCurrent site origin not in allowed websitesAdd exact origin including protocol and port.
Messages do not sendRate limit, max message length, or token issueCheck backend response and widget settings.
Invalid conversation tokenStale browser conversationWidget should recover automatically; clear localStorage if needed.
Wrong agent name/avatarWidget overrides or selected template changedCheck Project Settings display name, avatar URL, and support agent.
Localhost works but production failsProduction domain missing from allowed websitesAdd https://your-domain.com and https://www.your-domain.com.

Manual localStorage Reset

Visitors reset a conversation with New chat in the chat header. If you need to clear everything a widget stored during testing, run this in the browser console:

for (const key of Object.keys(localStorage)) {
  if (key.includes('cloudstation') || key.includes('widget')) {
    localStorage.removeItem(key);
  }
}

Or clear site data from browser devtools.

Next Steps