Custom · Standalone
If your stack does not fit WordPress or the standard integration is:
- You serve a
<script>with the widget loader from the CDN. - Your backend issues the Widget Token (24 h) from the Shop API Key server-to-server.
- Your frontend receives that token (printed in SSR, fetched from a private endpoint, etc.) and injects it into
data-token.
This covers Rails, Spring, Express, Django, Phoenix, PHP monoliths, static sites with SSR, etc.
Minimum snippet
<div id="neuroon-search"></div>
<script
src="https://cdn.neuroon.ai/widget@0.9.10/widget.js"
integrity="sha384-JTaG/IN0Jj/ImfUj2x5QVMG4HkbFHzui7fTpLtwl1hsP+kY9W8OODeSJRFWN1ZP5"
data-token="WIDGET_TOKEN_HERE"
data-container="#neuroon-search"
data-theme="auto"
data-locale="en"
data-api-url="https://api.neuroon.ai"
crossorigin="anonymous"
async></script>
data-* attributes
| Attribute | Required | Default | Notes |
|---|---|---|---|
data-token | Yes | — | 24 h Widget Token signed by your server with the Shop API Key (HMAC). |
data-container | Yes | — | CSS selector where the widget is mounted. |
data-theme | No | auto | auto | light | dark. |
data-locale | No | autodetect | ISO 639-1 (es, en, fr, de, …). |
data-api-url | No | https://api.neuroon.ai | Switch to Development with https://dev-api.neuroon.ai. |
data-position | No | inline | inline | floating. |
Widget token flow
Your backend signs the token locally using the Shop API Key as HMAC secret. You do not call Neuroon to issue it. Neuroon's backend validates the signature on every widget request.
Format:
Base64URL( shopId : unixTimestamp : HMAC-SHA256(hex)( "shopId:unixTimestamp", secret = shopApiKey ) )
Copy-paste implementations (Node, .NET, Python, PHP) with cache + rotation in Recipe · Server-to-server token.
"Print in SSR" pattern
The simplest form on any server: your render prints the token into the HTML.
<div id="neuroon-search"></div>
<script
src="https://cdn.neuroon.ai/widget@0.9.10/widget.js"
data-token="<%= neuroonWidgetToken %>"
data-container="#neuroon-search"
data-locale="<%= currentLocale %>"
async></script>
Your backend regenerates neuroonWidgetToken every N hours (with N < 24) and persists it in cache (Redis, KV, memory + lock). If the cache is empty, it calls the endpoint server-to-server.
"Fetch from the client" pattern
Useful when you do not control the server-side render (SPA with a separate backend):
// 1. Your own endpoint (in your backend) that returns the current token from your cache.
// NEVER expose the Shop API Key to the frontend.
async function loadNeuroonWidget() {
const r = await fetch('/api/internal/neuroon-token', { credentials: 'include' });
const { token } = await r.json();
const s = document.createElement('script');
s.src = 'https://cdn.neuroon.ai/widget@0.9.10/widget.js';
s.integrity = 'sha384-JTaG/IN0Jj/ImfUj2x5QVMG4HkbFHzui7fTpLtwl1hsP+kY9W8OODeSJRFWN1ZP5';
s.crossOrigin = 'anonymous';
s.async = true;
s.dataset.token = token;
s.dataset.container = '#neuroon-search';
s.dataset.theme = 'auto';
document.head.appendChild(s);
}
document.addEventListener('DOMContentLoaded', loadNeuroonWidget);
The
/api/internal/neuroon-tokenendpoint is exposed by you, not by Neuroon. Its only job is to read the cached token from your server-side cache and return it. The signing happens once every ~23 h. See Recipe · Server-to-server token.
Versioning and SRI
- Always pin the widget version in the URL (
@0.9.10, not@latest) in production. - Pair the version with
integrity="sha384-…"to block tampered binaries. - Recompute the SRI when you bump the version:
curl -s https://cdn.neuroon.ai/widget@VERSION/widget.js \
| openssl dgst -sha384 -binary | openssl base64 -A
Recommended CSP
Content-Security-Policy:
script-src 'self' https://cdn.neuroon.ai;
connect-src 'self' https://api.neuroon.ai;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
Next steps
nextjs— App Router + RSC pattern.nuxt— Nuxt 3 plugin.server-to-server— token issuance in Node, Go, Python, Ruby, .NET, PHP, Java.- Recipe · Server-to-server token — full guide with cache.