Custom · Standalone
If you don't use the official WordPress plugin, 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 | — | Opaque Widget Token issued by Neuroon (24 h). |
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
The token is issued by Neuroon, not by your backend. Your backend just requests the token with its Shop API Key (
sk_…) and hands it to the frontend.
/api/shops/{id}/widget-tokenPOST /api/shops/shop_xxxxxxxx/widget-token HTTP/1.1
Host: api.neuroon.ai
X-Shop-API-Key: sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Response:
{
"token": "wt_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
}
The response only carries
token. TTL is fixed at 24 h from issuance; rotate at 23 h for safety.
"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);
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';
If you are working in Development, add https://dev-api.neuroon.ai to connect-src.
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.