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:3000

A few rules that catch people out:

  • http:// and https:// are different origins. If you serve the same site over both (you shouldn't, but), list both.
  • example.com and www.example.com are different origins. List both if both serve the widget.
  • Subdomains aren't wildcarded. https://*.example.com is 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:

  1. The bubble shows in the configured corner (default: bottom-right).
  2. Click it — the panel opens with your greeting message.
  3. 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.