Recipe: Tracking de conversiones end-to-end
El funnel completo en Neuroon tiene tres eventos clave:
track:click— el usuario interactúa con un resultado en el widget. Lo emite el widget automáticamente, sin código tuyo.track:add-to-cart— opcional, derivado del cart bridge.track:conversion— pedido pagado / completado. Tienes que llamarlo tú server-side desde el handler de tu backend.
Por qué la conversión va server-side
Confiar solo en un pixel JS pierde una parte significativa de las conversiones:
| Bloqueo | Impacto típico |
|---|---|
| Adblockers (uBlock, Ghostery, Brave Shields) | 25-40 % en mercados europeos. |
| Safari ITP + iOS Private Relay | impacto residual sobre cookies. |
| CSPs estrictas (banca, gobierno) | bloquean cualquier pixel third-party. |
| Service workers offline | no envían beacons antes de cerrar tab. |
Llamando al endpoint server-to-server desde el handler que confirma el pedido eliminas esos cuatro vectores.
El endpoint
/api/plugin/shops/{shopId}/track/conversionPOST /api/plugin/shops/shop_xxxxxxxx/track/conversion HTTP/1.1
Host: api.neuroon.ai
X-Shop-API-Key: sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Origin: https://your-shop.example
Content-Type: application/json
{
"orderId": "ORD-2026-0001",
"orderValue": 199.95,
"currency": "EUR",
"conversions": [
{ "productId": "SKU-001", "searchLogId": "log_aaa", "quantity": 2, "lineTotal": 79.98 },
{ "productId": "SKU-042", "searchLogId": "log_bbb", "quantity": 1, "lineTotal": 119.97 }
]
}
searchLogIdes obligatorio (@NotBlankenConversionItem,ShopRequestDTO.java:92). Lo emite el widget en cadatrack:clicky lo guarda en una cookie en el navegador del comprador. Tu servidor debe leer esa cookie cuando se confirma el pedido y mapear cadaproductIdcon elsearchLogIdcorrespondiente. SinsearchLogIdla API responde 400. El plugin oficial de WordPress lo hace enwordpress-plugin/neuroon-search/includes/class-neuroon-conversion-tracking.php:335-374.
Idempotencia: Neuroon deduplica por orderId. Reenviar la misma conversión no infla métricas.
Implementaciones
import fetch from 'node-fetch';
export async function trackConversion(order) {
const payload = {
orderId: String(order.id),
orderValue: Number(order.total),
currency: order.currency,
conversions: order.lines.map((l) => ({
productId: String(l.sku),
quantity: Number(l.quantity),
lineTotal: Number(l.unitPrice * l.quantity),
})),
};
try {
const r = await fetch(
`${process.env.NEUROON_API_URL}/api/plugin/shops/${process.env.NEUROON_SHOP_ID}/track/conversion`,
{
method: 'POST',
headers: {
'X-Shop-API-Key': process.env.NEUROON_API_KEY,
'Origin': process.env.NEUROON_ORIGIN,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
},
);
if (!r.ok) console.warn('Neuroon conversion tracking failed', r.status);
} catch (err) {
console.warn('Neuroon conversion tracking error', err);
}
}
// Uso desde tu handler de pedido confirmado
export async function onOrderConfirmed(order) {
// 1) marca el pedido como pagado en tu BD
// 2) trackea (fire-and-forget)
trackConversion(order); // sin await: no bloqueamos la respuesta al cliente
}Outbox para reintentos
Si tu integración no puede tolerar pérdida de eventos (ej. tienda enterprise), persiste el payload en una tabla de outbox y reintenta con un job:
CREATE TABLE neuroon_conversion_outbox (
order_id VARCHAR(64) PRIMARY KEY,
payload JSON NOT NULL,
attempts INT NOT NULL DEFAULT 0,
last_error VARCHAR(1024),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
synced_at TIMESTAMP NULL
);
Job que procesa cada N minutos:
async function flushOutbox() {
const rows = await db.query(`
SELECT * FROM neuroon_conversion_outbox
WHERE synced_at IS NULL AND attempts < 5
ORDER BY created_at LIMIT 100`);
for (const row of rows) {
try {
await trackConversionRaw(row.payload);
await db.query(`UPDATE neuroon_conversion_outbox SET synced_at = NOW() WHERE order_id = $1`, [row.order_id]);
} catch (err) {
await db.query(`
UPDATE neuroon_conversion_outbox
SET attempts = attempts + 1, last_error = $1
WHERE order_id = $2`, [err.message, row.order_id]);
}
}
}
Validar
Tras procesar un pedido de prueba:
# El endpoint no devuelve la conversión en GET, pero puedes verificar que el dashboard
# de Neuroon refleja la nueva conversión en el plazo de unos segundos.
curl -s "https://dev-api.neuroon.ai/api/plugin/shops/$NEUROON_SHOP_ID/products/SKU-001" \
-H "X-Shop-API-Key: $NEUROON_API_KEY"
Errores frecuentes
| Síntoma | Causa | Solución |
|---|---|---|
400 con orderValue must be >= 0 | orderValue negativo (refunds) | Usa el endpoint de tracking de refunds. |
| Conversión no aparece en dashboard | origin no coincide o shopId cruzado de entornos | Verifica Origin y que la API Key sea del entorno correcto. |
| Conversiones duplicadas | Reenvíos sin idempotency key estable | Usa siempre el mismo orderId por pedido (no timestamps). |
Próximos pasos
plugins/dnn/conversion-tracking.plugins/wordpress/admin-dashboard— visualización en panel.- API ·
/track/conversion— referencia completa (cuando esté publicada).