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 flujos servidor-a-servidor son:
- Firmar Widget Token localmente con la Shop API Key (HMAC) y cachearlo 24 h con margen de 5 min. Ver Recipe · Server-to-server token.
- Sincronizar productos (
POST /api/plugin/shops/{shopId}/products/sync) en lotes. - Borrar productos (
DELETE /api/plugin/shops/{shopId}/products/{externalId}) cuando los retiras de tu catálogo. - Verificar dominio (
POST /api/plugin/shops/{shopId}/verify) tras dar de alta una tienda. - 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. |
X-Idempotency-Key | hash estable del batch | Recomendado en /products/sync: protege contra reintentos duplicados de cron. Ej.: md5(shopId:productIds:syncType). |
Respuestas comunes
| Status | Significado |
|---|---|
200 / 201 | OK. |
204 | OK sin cuerpo (ej. delete, 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, drift detectado en sync). |
429 | Rate limit. Retry-After (segundos) en el header. |
5xx | Reintentar con backoff exponencial. |
Sync de productos
Semántica del payload
Cuando construyas el body para POST /api/plugin/shops/{shopId}/products/sync, hay reglas que el contrato OpenAPI no hace evidentes pero que rompen la integración si las ignoras:
Schema completo del request en API Reference →
POST /products/syncyPluginProductDTO. Esta sección sólo cubre los puntos no obvios del contrato.
- Variantes después del padre. Si tu producto tiene variantes (talla/color), envía el padre primero en el array y cada variante con
parentExternalIdapuntando al padre. Variantes huérfanas (sin padre en el mismo batch o ya sincronizado) se descartan en silencio. - Precios con o sin impuestos — sé explícito. El backend toma el
priceque mandes tal cual; no infiere taxes. Si tu storefront muestra precios con IVA, mandapricecon IVA. Si los muestra sin, manda sin. Mantén la convención consistente con lo que el cliente final ve, o el dashboard reportará drift de precios. descriptionacepta hasta 5000 caracteres por contrato. Como guía operativa, descripciones muy largas (>2000 chars) suelen empeorar la calidad del ranking — usa la short description si la tienes o trunca a texto plano sin HTML.categories[](lista). Manda la jerarquía completa, de la categoría más específica a la raíz. El backend la usa para construir filtros guiados.brands[](lista). Si tu plataforma tiene un campo de marca dedicado, pásalo aquí. No lo derives del título.syncTypeaceptaINCREMENTALoFULL. UsaINCREMENTALen el 99 % de casos: es un upsert porexternalId. Solo usaFULLdurante una migración inicial o un reset deliberado — borra todo el catálogo en Neuroon antes de reinsertar el batch que mandes. Nunca lo dispares en cron: una FULL accidental vacía el índice mientras dura el sync.- Tamaño del batch: máx 500 productos por request, mín 1.
Detección de conflictos antes de sobrescribir
Si tu integración comparte el catálogo con otra fuente (dashboard de Neuroon, otra integración, edición manual), lee antes de escribir:
async function syncProductSafely(local) {
const remote = await fetch(
`${API_URL}/api/plugin/shops/${SHOP_ID}/products/${local.externalId}`,
{ headers: { 'X-Shop-API-Key': API_KEY } },
).then(r => r.ok ? r.json() : null);
if (remote && hasDriftedFromLastKnown(local.lastSyncedSnapshot, remote)) {
// El producto cambió en Neuroon desde tu último sync — pide confirmación
// antes de sobrescribir, o registra el conflicto para revisión.
return { status: 'conflict', remote };
}
return postSync([local]);
}
Campos que tienen sentido diffear: name, price, salePrice, description, sku, stockQuantity, stockStatus. Sin esta verificación, ediciones hechas en el dashboard (ej. el usuario corrige un precio) se sobrescriben silenciosamente en el siguiente cron sync.
Borrar productos del catálogo
POST /products/sync no contempla deletions — productos que dejas de mandar se quedan en Neuroon. Para retirar un producto, llama explícitamente:
curl -X DELETE "https://api.neuroon.ai/api/plugin/shops/$SHOP_ID/products/$EXTERNAL_ID" \
-H "X-Shop-API-Key: $API_KEY"
Patrón recomendado para integraciones que no tienen señal directa de "producto borrado":
// Job nocturno: detecta SKUs eliminados en tu BD que aún viven en Neuroon
async function reconcileDeletions() {
const localSkus = new Set((await db.query('SELECT external_id FROM products WHERE deleted = false')).map(r => r.external_id));
let cursor = null;
do {
const { products, nextCursor } = await fetch(
`${API_URL}/api/plugin/shops/${SHOP_ID}/products?cursor=${cursor ?? ''}`,
{ headers: { 'X-Shop-API-Key': API_KEY } },
).then(r => r.json());
for (const p of products) {
if (!localSkus.has(p.externalId)) {
await fetch(
`${API_URL}/api/plugin/shops/${SHOP_ID}/products/${p.externalId}`,
{ method: 'DELETE', headers: { 'X-Shop-API-Key': API_KEY } },
);
}
}
cursor = nextCursor;
} while (cursor);
}
Frecuencia recomendada: una vez al día. Sin esto, productos retirados de tu storefront siguen apareciendo en búsqueda.
Firma del Widget Token
Tu servidor firma el token localmente con la Shop API Key como secreto HMAC. No llamas a Neuroon para esto.
Código copy-paste con cache + rotación (Node, .NET, Python, PHP) en Recipe · Server-to-server token.
Idempotencia y reintentos
| Endpoint | Idempotente por | Reintentos seguros |
|---|---|---|
| Sign Widget Token (local) | (firma local) | Sí: re-firmar genera un token nuevo válido. |
products/sync | externalId por producto + X-Idempotency-Key opcional | Sí: reenviar el mismo batch no duplica. |
products/{externalId} (DELETE) | externalId | Sí: borrar dos veces devuelve 204. |
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. - Manda
X-Idempotency-Keyen sync batches: protege contra reintentos duplicados del cron tras un timeout. - Implementa el job de deletions: sin él, tu catálogo en Neuroon crece para siempre.
- Diff antes de sobrescribir si el catálogo se edita desde más de una fuente.
Próximos pasos
- Recipe · Server-to-server token — versión guiada con cache Redis.
- Recipe · Conversion tracking — atribución end-to-end con cookie + searchLogId.
- Authentication · Shop API Key.
- Authentication · Widget Token.
- Authentication · Rate Limits.