Your chat widget's API key is public by design
Open any website with a chat widget on it and press Ctrl-U. Search the page source for the word key, or for the vendor's name. It is there — a long string sitting in a script tag, in plain text, in a file the server hands to everybody who asks for the page. That is not a misconfiguration on that site. It is how every browser-embedded chat widget works, ours included, and it will be true of whichever product you are evaluating.
This post is about what follows from that. Not much, if the key is restricted. Rather more if it is not, and the default in this category is not.
Why the key cannot be hidden
A chat widget runs inside your visitor's browser. It has to identify itself to the vendor's servers on every message, and every credential it uses to do that has to be somewhere the browser can read — which is the same place the visitor, and their developer tools, can read it. There is no vault on the client. Obfuscating the string moves it; it does not hide it, because the code that unscrambles it also ships to the browser.
The genuine alternative is to put a server of your own in front of it, so the browser talks to you and you talk to the vendor with a secret key. Some products support that for exactly this reason. It also means running a service, which for a business that wanted a chat widget on a marketing site is a disproportionate answer. So the sane version is the one everyone actually ships: the key is public, and it is made worth very little to anyone who takes it.
What a copy of it is actually worth
Worth being precise here, because this category attracts security language wildly out of proportion to the risk, and overstating it is its own kind of dishonesty. A widget key is not an account credential. In a sanely built product it authenticates one assistant to answer questions and nothing else — it does not read conversations, reach a dashboard, change a setting, retrieve a lead, or touch billing. Confirm that with your vendor rather than assuming it, but that is the normal design.
So the realistic abuse is narrower than "breach" and still worth preventing. Someone lifts the snippet, embeds your assistant on their own site, and serves their visitors answers built from your content, metered against your monthly allowance. Three costs, in ascending order of how much they will annoy you:
- Your quota. Every question they answer is one you paid for. On a credit-priced plan this is a bill; on a flat plan it is your allowance being consumed by traffic that is not yours.
- Your content, working for someone else. The assistant answers out of your pages. A competitor who wanted a fast summary of your pricing, your process and your coverage now has one that updates itself.
- Your name on their answers. The widget carries your greeting, your colours and often your logo, on a site you have never seen. Whatever it says there, it says as you.
None of that is dramatic. All of it is avoidable with a setting most people never open.
The control that matters is the origin check
When a browser makes a request from a page, it sends an Origin header naming the site the page came from. The visitor's browser sets it; the page cannot forge it. That makes it the one piece of evidence a vendor can trust about where a widget is running, and checking it against a list you control is the entire mechanism. A key on an unlisted site fails at that check, before any question is answered.
Every serious product in this category has this. What differs is the default and the matching rules, and both are worth reading before you pick. Checked first-hand against each vendor's own documentation on 3 August 2026:
| Product | Default for a new key | Wildcards | Source (3 Aug 2026) |
|---|---|---|---|
| Zendesk | Restriction available; domains added to an Allowed Domains list | Yes — their page gives *.domain.com for all subdomains | support.zendesk.com |
| DocsBot | Any domain, by default — their wording is "We allow you to embed the widget on any domain by default" | No — "We do not support wildcards at this time"; each subdomain is its own entry | docsbot.ai |
| AnswerWeave | Any domain, until you add one. An empty list accepts every origin. | No. Exact hostname match, up to 50 entries; a wildcard is refused rather than stored. | Our security page |
Two of those three rows, ours included, ship keys that work anywhere until somebody opens the setting. That is not carelessness on anyone's part — publishable keys are already live on customer sites, and a vendor who switched enforcement on for everyone would take those widgets down. But it does mean the protection is opt-in, and opt-in protection that nobody mentions on a marketing page is protection most customers never get. Which is roughly why this post exists.
Wildcards are a convenience with a sharp edge
Exact matching means example.com does not cover www.example.com, and neither covers shop.example.com. Every hostname is its own line. This is tedious if you run twelve subdomains, and it is the first thing people ask for.
The argument for it anyway is what a wildcard does when the checking code does not implement one. If a system stores *.example.com and then compares entries exactly against the hostname, that line matches nothing, ever. It sits in a security setting looking like coverage while providing none — and unlike a typo, it looks correct. The failure is silent and it is discovered, if at all, by someone doing the test in the next section. A product that supports wildcards properly, as Zendesk's documentation describes, has no such problem. A product that accepts the syntax without implementing it has the worst of both.
So when a vendor tells you they support wildcards, it is worth one follow-up: add one, then load the widget from a subdomain it should cover, and from one it should not. Both answers should be the ones you expect.
What rate limits and rotation add, and what they do not
Two things usually come up alongside the origin check, and they are worth understanding as what they are: damage control, not prevention.
A per-key rate limit caps how fast anyone can spend your allowance. It does not stop the theft, it slows it — and set tight enough to stop a determined copier, it also throttles a legitimate traffic spike on your own site. Useful. Not a substitute.
Rotation replaces the key, which invalidates every copy of the old one, including the copies on your own pages. That is the point and also the catch: rotating means editing the snippet everywhere you have embedded it, and any page you miss goes quiet. Rotation is the fix after a problem, not the guard against one. If a vendor offers it as their answer to key exposure, they have answered a different question.
The four-step test
This takes five minutes and works on any vendor, whatever their documentation says. Documentation describes the intended behaviour. This tests the shipped one.
- Open your own site, view source, and find the key in the widget snippet. It will be there. If you cannot find it, search for the vendor's domain — the snippet loads its script from there.
- Paste the whole snippet into a blank HTML file and open it from somewhere that is not your site. A file on your desktop is enough; a scratch page on any other host is closer to the real thing.
- Ask it a question. If it answers, that key works from anywhere on the internet right now, and so does the copy in the page source of your live site.
- Add your real hostnames to the vendor's restriction list, then reload the scratch file and ask again. It should refuse there, and keep working on your own site.
Step four is the one people skip and the only one that proves anything. A setting whose behaviour does not change when you toggle it is not a control, and from outside the product there is no other way to tell those apart. Run it on your current widget before you run it on a new one — the result on the thing already live on your site is the more useful of the two.
Where we land
Ours is in the table above and it is not the flattering row. A new AnswerWeave key accepts every origin until you add one, exactly like DocsBot's. Matching is exact against the hostname the browser reports, up to fifty entries, and a pasted wildcard is refused with an explanation rather than stored as a line that would never match. You can paste a full URL, a scheme, a port or a trailing path and the field reduces it to the hostname, showing you what it is about to save before you commit.
The honest reading of that: we built the control, we did not make it the default, and we are telling you so on a page you can find rather than in a settings tooltip you would have to go looking for. The four-step test works on us. We would rather you ran it.
There is more on this — what the key can reach, how the in-dashboard preview avoids needing one at all, how shared analytics links are scoped and revoked, and what we still do not do — on our security page.
Try it against your own pages
Point it at your site, then ask it something your content does not cover. The free plan needs no card.