Embed the widget
Versioning & cache busting
The widget bundle ships at /widget/widget.js and is
re-built every release. To avoid stale browser / CDN caches on
buyer sites, each build also publishes a
content-hashed twin at
/widget/widget.<hash>.js plus a manifest at
/widget/manifest.json.
Default embed (auto-update)
The snippet the admin sees on /app/agents/{id}
points at the hashed file via the manifest. Each release ships a
new hash, the manifest updates on deploy, and the snippet
automatically resolves to the new file. Buyers don't need to
touch their HTML.
<script async src="https://app.example.com/widget/widget.abc123.js" data-agent="…"></script>
Manifest endpoint
Buyers who want to programmatically fetch the current bundle can
hit GET /widget/manifest.json:
{
"version": "1.3.0",
"hash": "abc123abc123",
"file": "widget.abc123abc123.js",
"url": "/widget/widget.abc123abc123.js",
"generated_at": "2026-05-16T07:11:36Z"
}
The endpoint sets
Cache-Control: public, max-age=60, must-revalidate
so the manifest itself never holds long, but the bundle file can
be served with long-lived Cache-Control headers
because the URL changes per release.
Pinning to a specific version
If you need to lock the bundle on the buyer's side (e.g. to
coordinate a marketing campaign), copy the
file field from the manifest and use it verbatim:
<script async src="https://app.example.com/widget/widget.abc123abc123.js"></script>
The hashed artifact remains accessible after newer versions ship — old hashes are not deleted by the build script.
Fallback
If the manifest is missing (for example a partial deploy), the
snippet falls back to
/widget/widget.js?v=<md5-prefix> so buyers
never get an empty src. We keep the unhashed
widget.js as the canonical artifact too.
Build pipeline
npm run build:widget runs the Vite build, then
invokes bin/widget-postbuild.cjs which:
- Reads
public/widget/widget.js. - Computes a 12-char SHA-256 prefix.
- Copies the bundle to
public/widget/widget.<hash>.js. - Writes
public/widget/manifest.json.
Both artifacts and the manifest are committed to the repo —
the host doesn't run npm run build, the repo
is the deployment artifact.