Troubleshooting
The errors you will see when integrating Neuroon and the direct solution.
401 Unauthorized — expired token
Your Widget Token expired (TTL: 24 hours).
Solution:
curl -X POST https://api.neuroon.ai/api/shops/$SHOP_ID/widget-token \
-H "X-Shop-API-Key: $API_KEY"
Cache the new token and replace data-token. If this happens repeatedly, schedule rotation every 23 hours from your server.
401 Unauthorized — invalid Shop API Key
You are calling /api/plugin/shops/... with a key that does not match the shopId in the URL, or the key does not exist.
Verify:
- The key is in the dashboard, in the correct shop.
- The key starts with
sk_and is 35 characters total. - You are NOT mixing Production (
api.neuroon.ai) with Development (dev-api.neuroon.ai).
403 Forbidden — Origin mismatch
The browser is loading the widget from a domain that does not match the registered URL of your shop.
Solution:
- Go to https://neuroon.ai/dashboard → your shop → Settings and check that
URLpoints to your real domain. - If you have multiple domains (staging.yourshop.com + yourshop.com), contact support to add additional origins.
403 Forbidden — Domain not verified
Your domain is not verified yet.
Solution:
- Dashboard → your shop → Verify domain → follow the instructions (add the meta tag or DNS record).
- Call
/api/plugin/shops/{shopId}/verifyagain.
429 Too Many Requests
You exceeded the rate limit of a specific endpoint.
Solution: read the Retry-After header (in seconds) and wait. Implement exponential backoff:
async function withRetry(fn, max = 3) {
for (let i = 0; i < max; i++) {
const res = await fn();
if (res.status !== 429) return res;
const wait = (parseInt(res.headers.get('retry-after')) || 2 ** i) * 1000;
await new Promise(r => setTimeout(r, wait));
}
throw new Error('Rate limit exceeded after retries');
}
See Rate limits for exact per-endpoint values.
CORS error in the browser
Your frontend is calling /api/plugin/shops/... directly from the browser. That does not work and is not meant to: the plugin endpoint is server-to-server.
Solution: move that call to your backend. The browser should only call /api/widget/* with X-Widget-Token. To sync products, generate tokens and track conversions server-side, everything goes from your server.
Widget does not appear / blank screen
First, open the browser console.
| Symptom | Cause | Fix |
|---|---|---|
data-token is empty | Your template is not printing the token | Print the token from your server with the right template engine syntax (<%= token %>, {{ token }}, ${token}) |
Failed to load https://cdn.neuroon.ai/widget.js | CSP blocks the CDN | Add script-src https://cdn.neuroon.ai and connect-src https://api.neuroon.ai to your Content-Security-Policy |
| Widget loads but searches yield 0 results | Products not indexed yet | Wait 2-5 seconds after sync. If still empty after 30 s, check the 200 OK from sync had newProducts: N with N > 0 |
Product synced but not appearing in search
| Verification | How |
|---|---|
Sync returned newProducts > 0 and failed: 0 | Check the JSON response from POST products/sync |
More than 5 s elapsed since 200 OK | Wait. Indexing is eventual (typically 2-5 s) |
externalId and url are not empty | The backend rejects products with empty required fields |
If everything is correct and it doesn't appear in 30 s, contact support with your shopId and the affected externalId.
Errors on the sync endpoint (400)
If you receive 400 Bad Request when syncing, the response body includes errors with details:
{
"totalReceived": 2,
"newProducts": 1,
"failed": 1,
"errors": [
{ "externalId": "abc-123", "error": "name must not be blank" }
]
}
Required validations:
externalIdnot emptynamenot empty, max 512 characters (@Size(max=512)inShopRequestDTO.java:72)price≥ 0currencyISO 4217 (3 letters:EUR,USD,MXN...)- valid
url
Still stuck
Support — include in your message:
shopId- Endpoint you were calling
- Request body (without secrets)
- Response status code and body
- Approximate time (UTC) so we can correlate.