Custom · Server-to-server
Este es el patrón base para cualquier integración custom: tu backend habla con Neuroon usando la Shop API Key (sk_…) y nunca expone esa key al frontend. Los tres flujos servidor-a-servidor son:
- Emitir Widget Token (
POST /api/shops/{id}/widget-token) y cachearlo. El TTL es fijo de 24 h; rótalo a las 23 h. - Sincronizar productos (
POST /api/plugin/shops/{shopId}/products/sync) en lotes. - Trackear conversiones (
POST /api/plugin/shops/{shopId}/track/conversion) cuando el pedido se confirma.
Headers comunes
| Header | Valor | Notas |
|---|---|---|
X-Shop-API-Key | sk_… | Authn server-to-server. Nunca al frontend. |
Origin | URL de tu storefront | Validación adicional para verificación de dominio. |
Content-Type | application/json | Para POST/PUT/PATCH. |
User-Agent | your-app/version | Facilita el filtrado en logs de Neuroon. |
Respuestas comunes
| Status | Significado |
|---|---|
200 / 201 | OK. |
204 | OK sin cuerpo (ej. unverify). |
400 | Validación. Body: { "code": "...", "message": "...", "details": [...] }. |
401 | API key inválida o ausente. |
403 | API key válida pero no autorizada para el Origin o el shopId. |
404 | Shop / producto no existe. |
409 | Conflicto (ej. dominio ya verificado). |
429 | Rate limit. Retry-After (segundos) en el header. |
5xx | Reintentar con backoff exponencial. |
Ejemplo: emitir Widget Token
import fetch from 'node-fetch';
const SHOP_ID = process.env.NEUROON_SHOP_ID;
const API_KEY = process.env.NEUROON_API_KEY;
const API_URL = process.env.NEUROON_API_URL ?? 'https://api.neuroon.ai';
const ROTATE_AFTER_MS = 23 * 60 * 60 * 1000; // TTL fijo 24 h
let cache = null; // { token, issuedAt: epoch ms }
export async function getWidgetToken() {
const now = Date.now();
if (cache && now - cache.issuedAt < ROTATE_AFTER_MS) return cache.token;
const res = await fetch(`${API_URL}/api/shops/${SHOP_ID}/widget-token`, {
method: 'POST',
headers: { 'X-Shop-API-Key': API_KEY, 'User-Agent': 'my-app/1.0' },
});
if (!res.ok) throw new Error(`Token issuance failed: ${res.status}`);
const body = await res.json();
cache = { token: body.token, issuedAt: now };
return cache.token;
}Verificar el dominio (sin plugin)
Si tu integración es custom (no usas el plugin de WordPress), tienes que verificar el dominio una vez antes de poder emitir Widget Tokens. La verificación es lo que ata tu shop.url al dominio que servirá el widget.
Llama a POST /api/plugin/shops/{shopId}/verify con el dominio público del storefront en el body:
curl -X POST "$NEUROON_API_URL/api/plugin/shops/$NEUROON_SHOP_ID/verify" \
-H "X-Shop-API-Key: $NEUROON_API_KEY" \
-H "Content-Type: application/json" \
-H "Origin: https://mitienda.com" \
-d '{"domain":"https://mitienda.com"}'
El campo del body se llama
domain(@NotBlankenShopRequestDTO.java:60). Si envíasurlel backend responde 400.
El backend compara el domain enviado contra el shop.url registrado en el dashboard. Si coinciden, el dominio queda verificado y permite servir tokens. Si difieren:
403 Forbiddencon la envoltura estándar{ timestamp, status, error, message, path }— edita elshop.urlen el dashboard y reintenta.404 Not Foundsi elshopIdno existe o no pertenece a tu API Key.409 Conflictsi el dominio ya estaba verificado por otra tienda — abre un ticket.
Para inspeccionar el estado de verificación sin mutar nada:
curl "$NEUROON_API_URL/api/plugin/shops/$NEUROON_SHOP_ID/verification-data" \
-H "X-Shop-API-Key: $NEUROON_API_KEY"
Devuelve { verificationCode, instructions, verified } (VerificationDataDTO). El POST verify devuelve adicionalmente { shopId, verificationCode, domain, name } (PluginVerificationDataDTO).
En staging puedes verificar tantos dominios como necesites repitiendo el
POST /verifycon cadadomain. En Production cada cambio de dominio requiere actualizar elshop.urlen el dashboard.
Rotación de Widget Tokens
No existe un endpoint de "refresh" para Widget Tokens. Cada Widget Token vive 24 h (app.widget.token.ttl-hours = 24 en application.yml:271). Cuando necesites rotar:
- Llama otra vez a
POST /api/shops/{shopId}/widget-tokencon tu Shop API Key. - Empieza a usar el nuevo token. El anterior sigue siendo válido hasta que expire por TTL — no hay revocación instantánea.
- Implementa caché con margen ≤ 23 h en tu lado (ver patrón al inicio de esta página).
El endpoint
POST /api/tokens/refreshque verás en la referencia OpenAPI no rota Widget Tokens: refresca tokens JWT de usuario del dashboard de Neuroon (TokenController.java:36). No lo uses para tu integración de widget.
Idempotencia y reintentos
| Endpoint | Idempotente por | Reintentos seguros |
|---|---|---|
widget-token | (no) | Sí: cada llamada genera un token nuevo (rota). |
products/sync | externalId por producto | Sí: reenviar el mismo batch no duplica. |
track/conversion | orderId | Sí: reenviar la misma conversión no duplica. |
verify | dominio + shopId | Sí, idempotente. |
Patrón de retry mínimo: 3 intentos con backoff exponencial sobre 429 y 5xx.
Buenas prácticas
- Nunca transmitas la
Shop API Keyal frontend, ni siquiera víaprocess.env.NEXT_PUBLIC_*. - Cachea el Widget Token server-side y refresca con margen de 5 minutos.
- Loguea con
User-Agentespecífico para facilitar diagnóstico. - Maneja
429respetandoRetry-After. - No mezcles entornos: tokens de Development no funcionan contra
api.neuroon.aiy viceversa.
Próximos pasos
- Recipe · Server-to-server token — versión guiada con cache Redis.
- Authentication · Shop API Key.
- Authentication · Widget Token.
- Authentication · Rate Limits.