The rule
Every widget endpoint resolves the key, then checks the requestOrigin against
the workspace. In production, the check runs in this order:
1
Your workspace domain, if one is set
One workspace, one domain. The request origin’s host must match the domain
you set in Settings → Workspace.The comparison strips a leading
www. from both sides, so example.com and
www.example.com are the same. Everything else must match exactly — a
subdomain does not inherit. A request with no Origin header is refused.When a domain is set, this is the whole check. There is no separate list to
configure.2
No domain set — fail closed
Before onboarding sets a domain,
localhost, 127.0.0.1 and ::1 are
allowed implicitly on any port, so local development works.Any other origin must be on the key’s explicit allowlist. A production key
with no domain and no allowlist entries is not open to the internet — it
is refused.Outside production the check does not run at all. A valid key is still
required, but any origin is accepted, so a local host page works against a
locally-run platform on any port regardless of the workspace’s domain. This
never loosens a real deployment.
Getting this right
Set your domain in Settings → Workspace as soon as you have one. Until you do, your key works from localhost and nowhere else, which usually shows up as the widget working in development and silently not appearing in staging.How a refusal looks
Most widget endpoints answer a disallowed origin with403:
CORS
Peeve echoes your origin inAccess-Control-Allow-Origin only when the key
resolves and the origin is allowed. Otherwise it answers *, which — combined
with the endpoint refusing to act — reveals nothing.
Preflight (OPTIONS) is answered *, because it carries no credentials.
Allowed request headers: Content-Type, Authorization, X-Peeve-Key. The
MCP endpoints also allow Mcp-Session-Id.
The MCP endpoints and the artifact endpoint answer
Access-Control-Allow-Origin: *
unconditionally. They are read-only and guide-only, so there is nothing to
protect with an origin check. See the MCP overview.What the origin check is for
The origin check bounds what a browser can do with a publishable key it found in someone else’s page. It is not a general access control, and it is not meant to be — a server can send anyOrigin header it likes.
That is why the server API does not rely on it at all: those
endpoints require a secret key, which is a credential you keep private, and they
send no CORS headers so a page cannot call them in the first place.