Solicitar demo Contactar
API REST · Documentación de integración

API de reducción de inventario

Guía para desarrolladores que integran sistemas externos (POS, e-commerce, ERP) con SmartOrder para descontar stock en la bodega de una tienda autorizada, mediante una API key operativa creada en el panel del sistema.

POST /api/v1/managed/inventory/reduce-by-sku
1

Introducción

Esta API permite descontar stock en la bodega asociada a una tienda autorizada en tu API key. Cada operación registra movimientos con el tipo indicado por ítem (por defecto Consumo) y documento API_INVENTORY_REDUCTION.

Actualmente existe un único endpoint: la reducción se identifica siempre por código externo del producto (external_code).

Importante: la bodega no se envía en el body. El servidor la resuelve automáticamente según el emparejamiento tienda ↔ bodega configurado en tu API key.

Casos de uso típicos

Punto de venta (POS)

Al cerrar un ticket, descuenta los productos vendidos usando el código externo del catálogo.

E-commerce

Cada pedido confirmado reduce stock con el número de orden como reference.

ERP / sistemas propios

Sincroniza consumos y mermas mapeando tus IDs vía external_code.

Producción / mermas

Registra consumo interno o desperdicio clasificado por movement_type.

En una frase

Tú controlas la tienda y los productos; el servidor decide la bodega y registra el movimiento de forma auditable.

2

Requisitos previos

Antes de llamar a este endpoint, tu organización debe tener configurado en el panel del sistema:

  1. 1

    API key de tipo OPERATIVE — una key GENERAL no sirve para esta ruta.

  2. 2

    El módulo inventory_reduction asignado a esa key.

  3. 3

    Productos dados de alta en la tienda (tienda / productos) con código externo configurado desde el dashboard, y stock en la bodega configurada.

  4. 4

    external_id configurado en las tiendas que uses en la integración.

  5. 5

    El tenant (identificador de tu instancia) para enviarlo en el header X-Tenant en cada petición.

Guarda la key al crearla: la key en texto plano (rawKey) solo se muestra una vez al crearla en el dashboard. Guárdala de forma segura; no se puede recuperar después.
3

URL base y tenant

Todas las rutas viven bajo la URL base de la API:

https://api-demo-dev.smart-order.io/api/v1/managed/inventory/...
La URL anterior corresponde al entorno de desarrollo actual (api-demo-dev.smart-order.io) y puede cambiar. Verifica siempre la URL vigente con el administrador de tu instancia.

El tenant ya no se resuelve por subdominio del host. Se resuelve a partir del header X-Tenant, cuyo valor es el identificador (slug) de tu tenant en Smart Order:

HeaderValor
X-TenantSlug/identificador de tu tenant (ej. demo-dev)
Importante: toda petición sin el header X-Tenant (o con un valor que no corresponde a un tenant existente) falla con 401 y el mensaje "Tenant no especificado en el header X-Tenant" o "Tenant no encontrado".
4

Autenticación

Incluye el tenant y la API key en cada petición:

HeaderValor
X-TenantSlug/identificador de tu tenant
X-Api-KeyTu API key completa (texto plano emitido al crearla)
Content-Typeapplication/json

Flujo de validación (en orden)

  1. 1

    TenantMiddleware

    Resuelve el tenant por el header X-Tenant. 401 si no especificado, no encontrado o inactivo.

  2. 2

    ValidateApiKeyMiddleware

    Verifica que la key exista, sea válida y no esté revocada. 401 si está ausente, inválida o revocada.

  3. 3

    RequireOperativeKey

    La key debe ser de tipo OPERATIVE. 403 si no lo es.

  4. 4

    HasApiKeyModule (inventory_reduction)

    La key debe tener asignado el módulo de reducción. 403 si falta.

  5. Validar tienda, productos y stock → descontar

    Responde 200 {"ok": true} o un error 4xx.

CondiciónHTTPMensaje típico
Sin header X-Tenant401Tenant no especificado en el header X-Tenant
X-Tenant no corresponde a un tenant401Tenant no encontrado
Sin header X-Api-Key401Unauthorized
Key inválida, revocada o inexistente401Unauthorized
Key no es OPERATIVE403Esta ruta requiere una API key operativa
Key sin módulo inventory_reduction403La API key no tiene permiso para reducción de inventario
5

Endpoint — Reducir por código externo

Descuenta inventario identificando cada ítem por código externo en la tienda indicada.

POST /api/v1/managed/inventory/reduce-by-sku

Body

CampoTipoReq.Descripción
store_idstringID externo de la tienda (store.external_id)
referencestringReferencia de idempotencia (ver Idempotencia)
itemsarrayLista de productos a descontar (mínimo 1)
items[].external_codestringCódigo externo del producto en la tienda
items[].quantitynumberCantidad a descontar; debe ser > 0
items[].movement_typestringNoTipo de movimiento: withdrawal (Consumo, por defecto), sale (Venta) o waste (Merma). Clasifica el motivo de la reducción.
No envíes warehouse_id en el body. Si la tienda no está autorizada, recibirás 403 con "La tienda no está autorizada para esta API key".

Ejemplo de body

{
  "store_id": "TIENDA-CENTRO-01",
  "reference": "venta-pos-2026-05-15-0042",
  "items": [
    { "external_code": "PROD-001", "quantity": 2.5, "movement_type": "sale" },
    { "external_code": "PROD-002", "quantity": 1 }
  ]
}

Tipos de movimiento

ValorRegistroCuándo usarlo
withdrawalConsumo (por defecto)Consumo interno o salida genérica
saleVentaDescuento por venta (POS / e-commerce)
wasteMermaDesperdicio, caducidad, pérdida

Respuesta exitosa

200 OK

{
  "ok": true
}
6

Idempotencia

El campo reference es obligatorio en cada petición. Evita descontar stock dos veces si reenvías la misma operación (timeouts, reintentos de red, etc.).

Comportamiento

Antes de descontar, el servidor busca un movimiento previo con la misma combinación: (reference, API_INVENTORY_REDUCTION, api-key:<id>).

  • Si ya existe200 con {"ok": true} sin volver a descontar.
  • Si no existe → se procesa la reducción y se registra con esa reference.

Alcance de la idempotencia

La clave es única por: valor de reference, tipo de documento API_INVENTORY_REDUCTION y la API key que realiza la operación (api-key:<uuid>). Dos keys distintas pueden usar la misma reference sin conflicto entre sí.

Recomendación

Usa reference con un ID estable de tu sistema (número de pedido, UUID de transacción, etc.). Si recibes 200 tras un reintento con la misma reference, puedes asumir que la reducción ya se aplicó.

{
  "reference": "mi-sistema:orden-12345",
  "store_id": "...",
  "items": [ /* ... */ ]
}
7

Identificadores

Todos los IDs que envías en el body de este endpoint son IDs externos de tu sistema, mapeados en Smart Order mediante external_id.

Tienda

CampoOrigen en Smart OrderDescripción
store_idstore.external_idIdentificador de tienda en tu ERP/POS

Producto

CampoOrigen en Smart OrderDescripción
items[].external_codeproduct_per_store.skuCódigo externo del producto en la tienda indicada
El código externo debe existir en la tienda indicada. Coordina con el administrador de tu instancia para que configure el external_id de tiendas y productos que vas a usar en la integración.
8

Validaciones del servidor

Además de autenticación y permisos, el servidor valida los datos de cada ítem antes de descontar. Todas estas devuelven 400.

ValidaciónError típico
reference vacío u omitido400 con param reference
Código externo vacíocódigo externo vacío en ítems
Cantidad ≤ 0La cantidad debe ser mayor a 0 para el código externo ...
movement_type inválido400 con param movement_type
Producto no existe en la tiendaEl producto con código externo ... no existe en la tienda
Producto no asignado a la bodega de la keyEl producto con código externo ... no está asignado a la bodega indicada
Stock insuficienteStock insuficiente para el código externo .... Disponible: X, solicitado: Y
store_id desconocidoNo se encontró tienda con el store_id indicado
JSON inválido o campos requeridos faltantes400 con param indicando el campo

Cada movimiento queda registrado con

Tipo: según movement_type de cada ítem (withdrawal → Consumo por defecto, sale → Venta, waste → Merma) · Tipo de documento: API_INVENTORY_REDUCTION · Observación: incluye el nombre de la API key para trazabilidad.

9

Códigos de error

Las respuestas de error son un array JSON. El campo param solo aparece en errores de validación de entrada (HTTP 400).

[
  {
    "message": "Descripción del error",
    "param": "nombre_campo"
  }
]

Tabla de códigos HTTP

CódigoCuándo
200Operación exitosa (o idempotente ya aplicada)
400Body inválido, reglas de negocio de entrada, producto no encontrado, stock insuficiente
401Sin API key, key inválida, tenant no especificado o no encontrado
403Key no operativa, sin módulo, tienda no autorizada, tenant inactivo
500Error interno (p. ej. fallo al resolver tenant)

Mensajes frecuentes de validación (400, con param)

Mensajeparam
store_id inválidostore_id
reference requerido (binding)reference
external_code requerido (binding)external_code
Error de validación · 400
[
  {
    "param": "store_id",
    "message": "store_id inválido"
  }
]
Error de permisos · 403
[
  {
    "message": "La tienda no está autorizada para esta API key"
  }
]
10

Buenas prácticas

Seguridad

  • Nunca expongas la API key en el frontend ni en repositorios públicos.
  • Rota o desactiva keys comprometidas desde el dashboard.
  • Usa HTTPS en producción.

Diseño de integración

  1. 1

    Siempre envía reference con un identificador estable de tu operación.

  2. 2

    Agrupa ítems en una sola petición cuando una venta u operación afecta varios productos (menos latencia, un solo documento de referencia).

  3. 3

    Usa external_code para identificar productos de forma consistente con tu catálogo.

  4. 4

    No asumas el warehouse_id: depende de la configuración de la key; solo controlas la tienda mediante store_id.

  5. 5

    Mapea tiendas autorizadas en tu sistema con los external_id configurados en Smart Order y asociados a la key.

Manejo de errores y reintentos

SituaciónAcción
401Revisar el header X-Tenant y la API key. No reintentar sin corregir credenciales.
403Revisar tipo de key, módulo y tienda en configuración del dashboard.
400 stock insuficienteNo reintentar con los mismos datos; sincronizar stock o ajustar cantidades.
400 producto no encontradoVerificar códigos externos y tienda antes de reintentar.
500 / timeout de redReintentar con la misma reference para evitar doble descuento.
200 tras reintentoTratar como éxito (idempotencia).
El servidor reintenta internamente hasta 3 veces ante conflictos de serialización en PostgreSQL; aun así, conviene que tu cliente reintente 5xx con backoff exponencial.

Pruebas

  1. 1

    Crear una API key OPERATIVE con módulo inventory_reduction y una tienda de prueba.

  2. 2

    Probar reducción con reference fija dos veces → la segunda llamada debe devolver 200 sin cambiar stock adicional.

  3. 3

    Probar tienda no autorizada → 403.

  4. 4

    Probar cantidad mayor al stock → 400 con mensaje de stock disponible.

  5. 5

    Probar petición sin reference400.

11

Ejemplos completos

Sustituye {TENANT} por el slug de tu tenant (ej. mi-empresa), {API_KEY} por tu key operativa, y los IDs externos y códigos por valores reales de tu entorno.

La URL base usada abajo (https://api-demo-dev.smart-order.io/api/v1) corresponde al entorno de desarrollo actual y puede cambiar.

Reducir por código externo

curl -X POST "https://api-demo-dev.smart-order.io/api/v1/managed/inventory/reduce-by-sku" \
  -H "X-Tenant: {TENANT}" \
  -H "X-Api-Key: {API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "SUCURSAL-NORTE",
    "reference": "pos-ticket-10042",
    "items": [
      { "external_code": "BEB-001", "quantity": 2, "movement_type": "sale" },
      { "external_code": "SNK-010", "quantity": 1.5 }
    ]
  }'

Respuesta exitosa

{
  "ok": true
}

Ejemplo de error de validación · 400

[
  {
    "param": "store_id",
    "message": "store_id inválido"
  }
]

Ejemplo de error de permisos · 403

[
  {
    "message": "La tienda no está autorizada para esta API key"
  }
]

Resumen de un vistazo

TemaClave
EndpointPOST /managed/inventory/reduce-by-sku
AutenticaciónHeaders X-Tenant + X-Api-Key
Requisito de keyTipo OPERATIVE + módulo inventory_reduction
BodegaLa resuelve el servidor; nunca se envía en el body
IdempotenciaEnvía reference estable por operación (obligatorio)
Éxito200 {"ok": true}

¿Necesitas ayuda?

Para configurar external_id en tiendas y el código externo (external_code) de cada producto en la pestaña "producto tienda" del dashboard, coordina con el administrador de tu instancia Smart Order.

Solicitar demo