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.
/api/v1/managed/inventory/reduce-by-sku 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).
Casos de uso típicos
Al cerrar un ticket, descuenta los productos vendidos usando el código externo del catálogo.
Cada pedido confirmado reduce stock con el número de orden como reference.
Sincroniza consumos y mermas mapeando tus IDs vía external_code.
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.
Requisitos previos
Antes de llamar a este endpoint, tu organización debe tener configurado en el panel del sistema:
- 1
API key de tipo
OPERATIVE— una keyGENERALno sirve para esta ruta. - 2
El módulo
inventory_reductionasignado a esa key. - 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
external_idconfigurado en las tiendas que uses en la integración. - 5
El tenant (identificador de tu instancia) para enviarlo en el header
X-Tenanten cada petición.
rawKey) solo se muestra una vez al crearla en el dashboard. Guárdala de forma segura; no se puede recuperar después.
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/...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:
| Header | Valor |
|---|---|
X-Tenant | Slug/identificador de tu tenant (ej. demo-dev) |
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".
Autenticación
Incluye el tenant y la API key en cada petición:
| Header | Valor |
|---|---|
X-Tenant | Slug/identificador de tu tenant |
X-Api-Key | Tu API key completa (texto plano emitido al crearla) |
Content-Type | application/json |
Flujo de validación (en orden)
- 1
TenantMiddleware
Resuelve el tenant por el header
X-Tenant. 401 si no especificado, no encontrado o inactivo. - 2
ValidateApiKeyMiddleware
Verifica que la key exista, sea válida y no esté revocada. 401 si está ausente, inválida o revocada.
- 3
RequireOperativeKey
La key debe ser de tipo
OPERATIVE. 403 si no lo es. - 4
HasApiKeyModule (
inventory_reduction)La key debe tener asignado el módulo de reducción. 403 si falta.
- ✓
Validar tienda, productos y stock → descontar
Responde 200
{"ok": true}o un error 4xx.
| Condición | HTTP | Mensaje típico |
|---|---|---|
Sin header X-Tenant | 401 | Tenant no especificado en el header X-Tenant |
X-Tenant no corresponde a un tenant | 401 | Tenant no encontrado |
Sin header X-Api-Key | 401 | Unauthorized |
| Key inválida, revocada o inexistente | 401 | Unauthorized |
Key no es OPERATIVE | 403 | Esta ruta requiere una API key operativa |
Key sin módulo inventory_reduction | 403 | La API key no tiene permiso para reducción de inventario |
Endpoint — Reducir por código externo
Descuenta inventario identificando cada ítem por código externo en la tienda indicada.
/api/v1/managed/inventory/reduce-by-sku Body
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
store_id | string | Sí | ID externo de la tienda (store.external_id) |
reference | string | Sí | Referencia de idempotencia (ver Idempotencia) |
items | array | Sí | Lista de productos a descontar (mínimo 1) |
items[].external_code | string | Sí | Código externo del producto en la tienda |
items[].quantity | number | Sí | Cantidad a descontar; debe ser > 0 |
items[].movement_type | string | No | Tipo de movimiento: withdrawal (Consumo, por defecto), sale (Venta) o waste (Merma). Clasifica el motivo de la reducción. |
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
| Valor | Registro | Cuándo usarlo |
|---|---|---|
withdrawal | Consumo (por defecto) | Consumo interno o salida genérica |
sale | Venta | Descuento por venta (POS / e-commerce) |
waste | Merma | Desperdicio, caducidad, pérdida |
Respuesta exitosa
200 OK
{
"ok": true
}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 existe → 200 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": [ /* ... */ ]
}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
| Campo | Origen en Smart Order | Descripción |
|---|---|---|
store_id | store.external_id | Identificador de tienda en tu ERP/POS |
Producto
| Campo | Origen en Smart Order | Descripción |
|---|---|---|
items[].external_code | product_per_store.sku | Código externo del producto en la tienda indicada |
external_id de tiendas y productos que vas a usar en la integración.
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ón | Error típico |
|---|---|
reference vacío u omitido | 400 con param reference |
| Código externo vacío | código externo vacío en ítems |
| Cantidad ≤ 0 | La cantidad debe ser mayor a 0 para el código externo ... |
movement_type inválido | 400 con param movement_type |
| Producto no existe en la tienda | El producto con código externo ... no existe en la tienda |
| Producto no asignado a la bodega de la key | El producto con código externo ... no está asignado a la bodega indicada |
| Stock insuficiente | Stock insuficiente para el código externo .... Disponible: X, solicitado: Y |
store_id desconocido | No se encontró tienda con el store_id indicado |
| JSON inválido o campos requeridos faltantes | 400 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.
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ódigo | Cuándo |
|---|---|
| 200 | Operación exitosa (o idempotente ya aplicada) |
| 400 | Body inválido, reglas de negocio de entrada, producto no encontrado, stock insuficiente |
| 401 | Sin API key, key inválida, tenant no especificado o no encontrado |
| 403 | Key no operativa, sin módulo, tienda no autorizada, tenant inactivo |
| 500 | Error interno (p. ej. fallo al resolver tenant) |
Mensajes frecuentes de validación (400, con param)
| Mensaje | param |
|---|---|
store_id inválido | store_id |
reference requerido (binding) | reference |
external_code requerido (binding) | external_code |
[
{
"param": "store_id",
"message": "store_id inválido"
}
][
{
"message": "La tienda no está autorizada para esta API key"
}
]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
Siempre envía
referencecon un identificador estable de tu operación. - 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
Usa
external_codepara identificar productos de forma consistente con tu catálogo. - 4
No asumas el
warehouse_id: depende de la configuración de la key; solo controlas la tienda mediantestore_id. - 5
Mapea tiendas autorizadas en tu sistema con los
external_idconfigurados en Smart Order y asociados a la key.
Manejo de errores y reintentos
| Situación | Acción |
|---|---|
| 401 | Revisar el header X-Tenant y la API key. No reintentar sin corregir credenciales. |
| 403 | Revisar tipo de key, módulo y tienda en configuración del dashboard. |
| 400 stock insuficiente | No reintentar con los mismos datos; sincronizar stock o ajustar cantidades. |
| 400 producto no encontrado | Verificar códigos externos y tienda antes de reintentar. |
| 500 / timeout de red | Reintentar con la misma reference para evitar doble descuento. |
| 200 tras reintento | Tratar como éxito (idempotencia). |
Pruebas
- 1
Crear una API key
OPERATIVEcon móduloinventory_reductiony una tienda de prueba. - 2
Probar reducción con
referencefija dos veces → la segunda llamada debe devolver 200 sin cambiar stock adicional. - 3
Probar tienda no autorizada → 403.
- 4
Probar cantidad mayor al stock → 400 con mensaje de stock disponible.
- 5
Probar petición sin
reference→ 400.
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.
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
| Tema | Clave |
|---|---|
| Endpoint | POST /managed/inventory/reduce-by-sku |
| Autenticación | Headers X-Tenant + X-Api-Key |
| Requisito de key | Tipo OPERATIVE + módulo inventory_reduction |
| Bodega | La resuelve el servidor; nunca se envía en el body |
| Idempotencia | Envía reference estable por operación (obligatorio) |
| Éxito | 200 {"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.