Multi-domain widgets
Many customers embed the chat widget on more than one site — www.acme.com, support.acme.com, docs.acme.com. This page covers the two halves of that story: getting the widget onto every domain, and knowing which domain each chat came from.
In your Managed Application stack (current release)
Embedding on several domains
The one-line script tag works on any domain listed in your stack’s ALLOWED_ORIGINS setting. List every embedding origin at deploy time (comma-separated, scheme + host, no paths); the widget bundle itself is served to any origin, and only the chat endpoints are gated by the allowlist. Changing the list after install follows the procedure documented in Allowed origins.
All domains share one assistant configuration; data-title / data-color on the script tag are the per-site knobs in the current release.
Which domain did this chat come from?
Each new chat session records the origin of the embedding page — the site the visitor was on when they opened the widget:
- The origin is stamped once, at session creation, from the browser’s
Originheader — and only when that header exactly matches a non-wildcardALLOWED_ORIGINSentry. With the wizard default of*, no origin is recorded (the header would be unverifiable, so we store nothing rather than something forgeable). The literal*is never stored. - Sessions created before this feature (or without a matching origin) simply show
—. - The value is display metadata only. It is never used for authentication or routing.
Where you see it:
| Surface | What shows |
|---|---|
| Dashboard → Top sessions (24h) | An Origin column per session row |
| Dashboard → Search conversations | The origin in each match’s meta line |
GET /api/sessions | An origin field on each session row |
GET /api/sessions/{id}/transcript | An origin field alongside the session id |
Teams agent surface (GET /api/agent/sessions) | An origin field on each session your agents list |
The widget registry (full PointOfContactAI runtime)
The full PointOfContactAI runtime — the Microsoft Teams dashboard experience — goes further with a widget registry: Settings → Widgets (admin only) manages any number of named widgets, each identified by a ?w=<widget-id> key on the embed URL.
Each registered widget binds:
- Primary domain — the canonical domain shown as the chat’s source across the Teams dashboard.
- Allowed origins — the domains permitted to iframe this widget (see the CSP prerequisite below).
- Initial AI agent — chats starting from this widget route to this agent instead of the global default.
- OAuth override (optional) — its own visitor sign-in directory.
- Branding override (optional) — theme and verbiage per widget.
An unknown or disabled widget id is refused when the widget requests its visitor token, so a widget only works where it has been registered and enabled. On the first message, the chat is stamped with the widget’s id, name, and primary domain — a write-once record, so renaming a widget later does not rewrite the provenance of older chats.
Where agents see the source domain
- Chat list — each row shows 🌐 with the source domain (hover for the widget name).
- Open chat header — a
🌐 Source:line with the domain and widget name; when the embedding page’s URL was captured, it appears as a tooltip (informational only — it is best-effort browser metadata, never trusted for auth). - Placeholder states — chats assigned to another agent, AI-handled chats, and closed chats show the domain before you claim or open them.
- Live-list filter — a domain filter appears above the chat lists once chats span two or more domains.
- Closed-chats report — report rows carry the widget id, widget name, and source domain, with a Source domain filter and column in the metrics table. Chats from before the feature (or from an unregistered embed) show
—.
Prerequisite: frame-ancestors on the widget host
The registry embed is an iframe, and browsers block an iframe unless the site hosting the widget sends a Content-Security-Policy header whose frame-ancestors directive lists every embedding origin. Settings → Widgets renders the exact header line to deploy — including the Microsoft Teams host entries, which must be preserved (removing them breaks the Teams personal tab) — with a copy button. Deploy it on the widget host (for example in staticwebapp.config.json) whenever a widget’s allowed origins change.
Settings → Widgets also warns (non-blocking) when two widgets share a primary domain, since that makes “which widget did this chat come from” reporting ambiguous.