Webhooks
Los webhooks permiten que NinoxNet notifique cambios de artículos hacia tu integración sin depender solamente de consultas periódicas.
Cuándo conviene usarlos
- reducir polling
- mantener stock y precios más actualizados
- acelerar sincronizaciones incrementales
Para una integración nueva, conviene implementar primero la lectura de catálogo y después sumar webhooks como mecanismo de actualización incremental.
Configuración por API BETA
Pueden cambiar de contrato, comportamiento o ser retirados antes de su release definitivo.
Podés administrar tus propios webhooks sin coordinar cada cambio con NinoxNet: alta, edición y baja se hacen con el mismo token de la integración.
Reglas generales:
- un solo webhook por topic
- el único topic disponible hoy es
articulos - el
idlo asigna NinoxNet: es de solo lectura y no cambia nunca. Si lo mandás en el cuerpo del request, se ignora - los cuatro endpoints devuelven la lista completa actualizada
El objeto webhook
export interface Webhook {
id: number; // asignado por NinoxNet, solo lectura
url: string; // absoluta, http o https
topic: string; // "articulos"
activo: boolean;
extra: string | null; // null | "objeto" | "curva" — ver Eventos contemplados
headers: WebhookHeader[];
}
export interface WebhookHeader {
key: string;
value: string;
}
Al crear o editar mandás el mismo objeto sin id:
export interface WebhookGuardar {
url: string; // requerida
topic: string; // requerido
activo?: boolean; // default true
extra?: string | null;
headers?: WebhookHeader[];
}
Listar los webhooks configurados
/integraciones/terceros/config/webhooksDevuelve Webhook[], o [] si no hay ninguno configurado.
curl --request GET \
--url 'https://api.test-ninox.com.ar/integraciones/terceros/config/webhooks' \
--header 'X-NX-TOKEN: {TU_TOKEN}'
Crear un webhook
/integraciones/terceros/config/webhookscurl --request POST \
--url 'https://api.test-ninox.com.ar/integraciones/terceros/config/webhooks' \
--header 'X-NX-TOKEN: {TU_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://test.com",
"topic": "articulos",
"activo": true,
"extra": "objeto",
"headers": [ { "key": "X-API-Key", "value": "test" } ]
}'
Respuesta:
[
{
"id": 1,
"url": "https://test.com",
"topic": "articulos",
"activo": true,
"extra": "objeto",
"headers": [ { "key": "X-API-Key", "value": "test" } ]
}
]
Si ya tenés un webhook para ese topic, el alta se rechaza: editá el existente o eliminalo.
Editar un webhook
/integraciones/terceros/config/webhooks/{id}El id va en la URL. El cuerpo es el mismo de la creación.
curl --request POST \
--url 'https://api.test-ninox.com.ar/integraciones/terceros/config/webhooks/1' \
--header 'X-NX-TOKEN: {TU_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{ "url": "https://test.com/v2", "topic": "articulos", "activo": true, "extra": "objeto" }'
Eliminar un webhook
/integraciones/terceros/config/webhooks/{id}curl --request DELETE \
--url 'https://api.test-ninox.com.ar/integraciones/terceros/config/webhooks/1' \
--header 'X-NX-TOKEN: {TU_TOKEN}'
Eliminar un id que no existe devuelve 400, no un 200 vacío. Si reintentás un DELETE
después de un timeout de red y recibís 400, revisá con un GET antes de asumir que falló.
Validaciones
| Campo | Regla |
|---|---|
url | Requerida. Absoluta, http o https. Máximo 500 caracteres |
topic | Requerido. Sólo articulos |
extra | null, "objeto" o "curva" |
headers | Máximo 10 |
headers[].key | Requerida, máximo 100 caracteres, sin espacios ni caracteres especiales. No se admiten keys repetidas, aunque difieran en mayúsculas |
headers[].value | Requerido, máximo 500 caracteres, sin saltos de línea |
Cualquier validación que falle devuelve 400 con el detalle en mensajes.
Rate limiting
La consulta usa una ventana de 10 segundos en producción (3 en test). Las escrituras —alta, edición y baja— comparten una ventana de 30 segundos en producción (3 en test), porque cada guardado se propaga a toda la infraestructura.
Requisitos del endpoint receptor
Tu endpoint debe:
- aceptar requests
POST - procesar JSON
- responder
200 OKcuando reciba correctamente el evento - responder rápido
Si el endpoint no responde dentro de 10 segundos, el envío puede considerarse fallido.
Eventos contemplados
Cambio de artículo
Informa cambios de stock, precios, propiedades del artículo, variantes y clasificación
comercial. El payload puede enviarse en dos formatos, y el que manda es el campo extra del
webhook.
Formato plano — extra: "curva" o null
ArticuloConCurva[]
Es el formato más práctico para la mayoría de las integraciones: cada variante llega como un registro independiente. Es el comportamiento por defecto.
Formato agrupado — extra: "objeto"
Articulo
Conserva la curva y los tags dentro de un único objeto de artículo.
El formato se cambia editando el webhook: ver Configuración por API.
Stock por depósito (multidepósito informativo)
Si tu integración administra varios depósitos o locales, el canal puede configurarse en modo multidepósito informativo. En ese modo cada evento de artículo incluye, además del stock habitual, el detalle de cada depósito:
export interface StockDeposito {
depositoId: number;
unidades: number;
}
stockDepositos aparece tanto en el formato plano (por variante) como en el agrupado:
{
"articuloId": 123,
"codigo": "REM-01",
"unidades": 14,
"stockDepositos": [
{ "depositoId": 3, "unidades": 14 },
{ "depositoId": 7, "unidades": 6 },
{ "depositoId": 9, "unidades": 0 }
]
}
unidades no es la suma de stockDepositosEn este modo unidades es el stock del depósito principal, no el total. El principal además
aparece como un item más del array. Si necesitás el total, sumalo vos a partir de stockDepositos.
Los depósitos con cero también se informan: es la señal de que el artículo se agotó en ese local. La lista trae siempre un item por cada depósito configurado en la integración, así que podés mapearla directo contra tus sucursales sin adivinar ausencias.
Para saber si tu integración está en este modo, consultá
GET /integraciones/terceros/config: si flujoDeposito vale 3, vas a
recibir stockDepositos, y depositos te dice de qué depósitos se trata. Con cualquier otro valor
el campo no viaja.
Lo configura NinoxNet en el canal, no se activa por API. Requiere plan Corporativo. Si lo necesitás, escribinos por el Chat de Ayuda.
Artículo desactivado
Cuando un artículo deja de participar en la integración, el webhook informa la baja lógica:
{
articuloId: number;
eliminado: true;
}
Recomendaciones de implementación
activo no corta el envíoHoy el campo activo se guarda pero no detiene el envío del topic articulos. Si necesitás
dejar de recibir eventos, eliminá el webhook.
- validar autenticidad del request según la configuración acordada
- registrar cada evento recibido
- procesar de forma idempotente
- desacoplar recepción y procesamiento con una cola o tarea interna, si el volumen lo justifica
- mantener una resincronización completa periódica como mecanismo de respaldo
Estrategia sugerida
sync inicial (GetData) → caché local → updates por webhook → resync completa periódica de respaldo
- sincronización inicial con
GetData - almacenamiento local del catálogo
- actualización incremental por webhook
- resincronización completa periódica respetando la frecuencia mínima de consulta
Relación con el resto de la integración
Los webhooks complementan la integración, pero no reemplazan el alta inicial ni el modelado del catálogo. El punto de entrada general es Integración de terceros y la referencia de tipos está en Esquema de datos.