Embedding the widget
One <script> tag is everything. The loader injects an iframe at the bottom-right of the page, talks to your account's /config endpoint to fetch greeting + colors, and is invisible until the visitor opens the bubble.
The script tag
Paste this into your site's HTML, anywhere inside <head> or <body>. The closing </body> is fine — earlier is fine too. It defers automatically; it will not block your page render.
<script
src="https://usewilow.com/widget/loader.js"
data-api-key="cb_live_..."></script>The API key is created in Admin → API keys. Copy the visible value at creation time — we don't store the raw key, only its hash. If you lose it, mint a new one and rotate.
You'll see the bubble appear in the corner within ~500 ms of page load. If it doesn't, jump to troubleshooting.
Allowed origins — the most common stumble
Every API key has an allowed origins list. The loader sends the page's Origin header on every API call (/widget/config, /widget/conversations/..., etc.) and the server rejects calls from origins not on the list.
If you're seeing a CORS error in the browser console, this is almost always why. Add the origin in Admin → API keys → ⋯ → Edit origins. Format: scheme + host (+ port if non-default), one per line:
https://example.com
https://www.example.com
https://staging.example.com
http://localhost:3000A few rules that catch people out:
http://andhttps://are different origins. If you serve the same site over both (you shouldn't, but), list both.example.comandwww.example.comare different origins. List both if both serve the widget.- Subdomains aren't wildcarded.
https://*.example.comis not valid — list each subdomain explicitly. (We may add wildcard support later; not today.) - Trailing slash doesn't matter — we strip it server-side.
Per-platform install snippets
The script tag is the same across every platform — what differs is where you paste it. Pick yours:
<!-- Plain HTML / static site
Drop into <head> or just before </body> — both work. -->
<script
src="https://usewilow.com/widget/loader.js"
data-api-key="cb_live_..."></script><!-- WordPress (Block editor)
Appearance → Customize → Additional widgets / Header & Footer Code,
OR install a "Insert Headers and Footers" plugin and paste in
"Scripts in Footer". A child theme's footer.php works too. -->
<script
src="https://usewilow.com/widget/loader.js"
data-api-key="cb_live_..."></script><!-- Webflow
Project settings → Custom code → Footer Code, then republish. -->
<script
src="https://usewilow.com/widget/loader.js"
data-api-key="cb_live_..."></script><!-- Shopify
Online Store → Themes → ⋯ → Edit code → theme.liquid,
paste right before </body>, then save. -->
<script
src="https://usewilow.com/widget/loader.js"
data-api-key="cb_live_..."></script><!-- Wix
Settings → Custom Code → Add Custom Code, place in Body — end,
apply to All pages. -->
<script
src="https://usewilow.com/widget/loader.js"
data-api-key="cb_live_..."></script><!-- Squarespace
Site settings → Advanced → Code Injection → Footer. -->
<script
src="https://usewilow.com/widget/loader.js"
data-api-key="cb_live_..."></script>If your platform isn't listed, it'll work the same way as plain HTML — find the "custom HTML / footer code / global script" injection point and paste the tag.
Verifying the install
After publishing the change, open your site in an incognito tab so no cache lies to you:
- The bubble shows in the configured corner (default: bottom-right).
- Click it — the panel opens with your greeting message.
- Send "hi" — you get a streamed reply.
If any of those fails, the Network tab in browser devtools tells you which step broke. Look for failing requests to usewilow.com — the response body usually says exactly what's wrong.
Removing the widget
Just delete the script tag and republish. Already-stored conversations stay in your account; they're not visible to visitors after the widget is gone.