Saltar al contenido principal

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:

  1. 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.
  2. Sincronizar productos (POST /api/plugin/shops/{shopId}/products/sync) en lotes.
  3. Borrar productos (DELETE /api/plugin/shops/{shopId}/products/{externalId}) cuando los retiras de tu catálogo.
  4. Verificar dominio (POST /api/plugin/shops/{shopId}/verify) tras dar de alta una tienda.
  5. Trackear conversiones (POST /api/plugin/shops/{shopId}/track/conversion) cuando el pedido se confirma.

Headers comunes

HeaderValorNotas
X-Shop-API-Keysk_…Authn server-to-server. Nunca al frontend.
OriginURL de tu storefrontValidación adicional para verificación de dominio.
Content-Typeapplication/jsonPara POST/PUT/PATCH.
User-Agentyour-app/versionFacilita el filtrado en logs de Neuroon.
X-Idempotency-Keyhash estable del batchRecomendado en /products/sync: protege contra reintentos duplicados de cron. Ej.: md5(shopId:productIds:syncType).

Respuestas comunes

StatusSignificado
200 / 201OK.
204OK sin cuerpo (ej. delete, unverify).
400Validación. Body: { "code": "...", "message": "...", "details": [...] }.
401API key inválida o ausente.
403API key válida pero no autorizada para el Origin o el shopId.
404Shop / producto no existe.
409Conflicto (ej. dominio ya verificado, drift detectado en sync).
429Rate limit. Retry-After (segundos) en el header.
5xxReintentar 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/sync y PluginProductDTO. 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 parentExternalId apuntando 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 price que mandes tal cual; no infiere taxes. Si tu storefront muestra precios con IVA, manda price con 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.
  • description acepta 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.
  • syncType acepta INCREMENTAL o FULL. Usa INCREMENTAL en el 99 % de casos: es un upsert por externalId. Solo usa FULL durante 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.

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

EndpointIdempotente porReintentos seguros
Sign Widget Token (local)(firma local)Sí: re-firmar genera un token nuevo válido.
products/syncexternalId por producto + X-Idempotency-Key opcionalSí: reenviar el mismo batch no duplica.
products/{externalId} (DELETE)externalIdSí: borrar dos veces devuelve 204.
track/conversionorderIdSí: reenviar la misma conversión no duplica.
verifydominio + shopIdSí, idempotente.

Patrón de retry mínimo: 3 intentos con backoff exponencial sobre 429 y 5xx.

Buenas prácticas

  • Nunca transmitas la Shop API Key al frontend, ni siquiera vía process.env.NEXT_PUBLIC_*.
  • Cachea el Widget Token server-side y refresca con margen de 5 minutos.
  • Loguea con User-Agent específico para facilitar diagnóstico.
  • Maneja 429 respetando Retry-After.
  • No mezcles entornos: tokens de Development no funcionan contra api.neuroon.ai y viceversa.
  • Manda X-Idempotency-Key en 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