Saltar al contenido
Catálogo

Inventario

Lee y ajusta el stock de cada variante en varias ubicaciones con Admin API.

Lleva el stock del «Polo de algodón» en Almacén Lima y Tienda Miraflores: lee cada nivel, ajústalo sin pisar cambios ajenos y actualiza muchos niveles en un lote.

Requisitos

  • Un token de Admin en ECOMIQ_TOKEN. Ver Inicio rápido.
  • Los permisos settings:read para las ubicaciones, inventory:read para leer stock e inventory:update para cambiarlo.
  • Variantes con trackInventory: true y al menos una ubicación activa.

Lista las ubicaciones

Pide las ubicaciones activas: solo ellas aceptan ajustes de stock. Guarda el id de cada una junto a su code.

curl "https://api.ecomiq.pe/api/v1/fulfillment/locations?isActive=true&limit=50" \
  -H "Authorization: Bearer $ECOMIQ_TOKEN"
Respuesta (extracto)
{
  "data": [
    {
      "id": "7204558912004325378",
      "name": "Almacén Lima",
      "code": "ALM-LIMA",
      "type": "warehouse",
      "isActive": true,
      "isDefault": true
    },
    {
      "id": "7204558912004325380",
      "name": "Tienda Miraflores",
      "code": "TDA-MIRAFLORES",
      "type": "store",
      "isActive": true,
      "isDefault": false
    }
  ],
  "hasNextPage": false,
  "nextCursor": null
}

Si hasNextPage es true, pide la siguiente página con nextCursor. Ver Paginación.

Lee el stock por ubicación

POST /v1/inventory/levels devuelve un nivel por variante y ubicación. locationIds es opcional: sin él, recibes todas las ubicaciones con nivel. Este ejemplo lee el SKU POL-001-AZ-M del Polo de algodón.

curl -X POST https://api.ecomiq.pe/api/v1/inventory/levels \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "variantIds": ["7204558912004325377"],
    "locationIds": ["7204558912004325378", "7204558912004325380"]
  }'
Respuesta (extracto)
[
  {
    "id": "7204558912004325390",
    "variantId": "7204558912004325377",
    "locationId": "7204558912004325378",
    "onHand": 12,
    "committed": 2,
    "unavailable": 0,
    "available": 10,
    "incoming": 0,
    "version": 4
  },
  {
    "id": "7204558912004325391",
    "variantId": "7204558912004325377",
    "locationId": "7204558912004325380",
    "onHand": 3,
    "committed": 0,
    "unavailable": 0,
    "available": 3,
    "incoming": 0,
    "version": 2
  }
]

Si una ubicación no aparece, la variante aún no tiene nivel ahí.

CampoQué cuenta
onHandUnidades físicas en la ubicación.
committedUnidades reservadas, por ejemplo para pedidos.
unavailableUnidades que no se pueden vender.
availableLo que se puede vender: onHand - committed - unavailable.
incomingUnidades por recibir. No cuentan en onHand hasta que llegan.
versionVersión del nivel. Sube con cada cambio.

Ajusta un nivel

Envía la variante, la ubicación y la version leída en expectedVersion. Si la variante no tiene nivel en esa ubicación, envía 0: la API crea el nivel.

  • mode: "adjust" suma quantity al valor actual. Usa un número negativo para restar.
  • mode: "set" fija el valor. quantity no puede ser negativa.
  • target: "onHand" cambia las unidades físicas.
  • target: "available" cambia lo vendible. La API calcula onHand sumando committed y unavailable.

Este ejemplo registra el ingreso de cinco unidades en Almacén Lima.

curl -X POST https://api.ecomiq.pe/api/v1/inventory/adjust \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "variantId": "7204558912004325377",
    "locationId": "7204558912004325378",
    "target": "onHand",
    "mode": "adjust",
    "quantity": 5,
    "reason": "restock",
    "note": "Ingreso de cinco unidades.",
    "expectedVersion": 4
  }'
Respuesta
{
  "id": "7204558912004325390",
  "variantId": "7204558912004325377",
  "locationId": "7204558912004325378",
  "onHand": 17,
  "committed": 2,
  "unavailable": 0,
  "available": 15,
  "incoming": 0,
  "version": 5,
  "createdOn": "2026-09-12T14:05:00Z",
  "updatedOn": "2026-10-04T15:30:00Z"
}

La respuesta trae la nueva version.

reason queda en el historial de movimientos. Valores y campos en Establecer o ajustar existencias.

Resuelve un conflicto

Cada cambio sube la version, también la reserva de un pedido. Con una versión vieja, la API responde 409 Inventory.Changed y no aplica nada.

En este ejemplo leíste la versión 2 de Tienda Miraflores. Luego un pedido reservó una unidad y la versión pasó a 3. Tu ajuste con 2 recibe:

Respuesta de error
{
  "title": "Conflict",
  "status": 409,
  "detail": "Inventory changed. Reload and retry.",
  "code": "Inventory.Changed"
}

Lee el nivel otra vez y reintenta con la nueva version:

# 1. Lee la version actual: ahora es 3
curl -X POST https://api.ecomiq.pe/api/v1/inventory/levels \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "variantIds": ["7204558912004325377"],
    "locationIds": ["7204558912004325380"]
  }'

# 2. Reintenta con esa version
curl -X POST https://api.ecomiq.pe/api/v1/inventory/adjust \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "variantId": "7204558912004325377",
    "locationId": "7204558912004325380",
    "target": "onHand",
    "mode": "adjust",
    "quantity": -1,
    "reason": "damage",
    "note": "Una unidad con falla.",
    "expectedVersion": 3
  }'
Respuesta (extracto)
{
  "locationId": "7204558912004325380",
  "onHand": 2,
  "committed": 1,
  "available": 1,
  "version": 4
}

Con set, confirma que el valor sigue siendo correcto antes de reintentar. Si una petición se quedó sin respuesta, lee el nivel antes de repetir un adjust: el primer intento pudo aplicarse.

Actualiza en lote

POST /v1/inventory/batch recibe hasta 100 filas. Cada fila lleva variante, ubicación, mode, quantity y expectedVersion. reason y note valen para todo el lote.

Este conteo fija tres niveles. La última fila es POL-001-AZ-L, sin nivel en Tienda Miraflores: por eso envía expectedVersion: 0.

conteo.json
{
  "items": [
    {
      "variantId": "7204558912004325377",
      "locationId": "7204558912004325378",
      "mode": "set",
      "quantity": 16,
      "expectedVersion": 5
    },
    {
      "variantId": "7204558912004325377",
      "locationId": "7204558912004325380",
      "mode": "set",
      "quantity": 3,
      "expectedVersion": 4
    },
    {
      "variantId": "7204558912004325379",
      "locationId": "7204558912004325380",
      "mode": "set",
      "quantity": 6,
      "expectedVersion": 0
    }
  ],
  "reason": "inventoryCount",
  "note": "Conteo de fin de mes."
}
curl -X POST https://api.ecomiq.pe/api/v1/inventory/batch \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  --data @conteo.json
Respuesta
{
  "results": [
    {
      "index": 0,
      "id": "7204558912004325377",
      "status": "updated",
      "errors": []
    },
    {
      "index": 1,
      "id": "7204558912004325377",
      "status": "updated",
      "errors": []
    },
    {
      "index": 2,
      "id": "7204558912004325379",
      "status": "failed",
      "errors": [
        {
          "field": null,
          "code": "Inventory.Changed",
          "message": "Inventory changed. Reload and retry."
        }
      ]
    }
  ]
}

La API responde 200 con el resultado de cada fila en results[], en el orden recibido. Cada fila se aplica por separado: una con versión vieja o ubicación inactiva falla y las demás siguen. Vuelve a leer los niveles de las filas failed y reenvíalas con su version nueva.

  • Para más de 100 filas, sube un archivo a POST /v1/inventory/imports o divide en lotes de hasta 100.
  • Envía Idempotency-Key y repite la misma clave al reintentar el mismo lote.
  • Para sincronizar desde tu ERP, usa set: repetir una fila set no suma unidades dos veces.

El lote siempre cambia onHand

Las filas no aceptan target. Para fijar available, usa POST /v1/inventory/adjust.

Errores

EstadocodeQué hacer
400Inventory.OnHandUnderflow, Inventory.AvailableUnderflowEl cambio deja el stock en negativo: revisa quantity.
404Locations.NotFoundLa ubicación no existe o está inactiva.
409Inventory.ChangedLee el nivel otra vez y reintenta con la nueva version.
409Inventory.NotTrackedLa variante no controla stock: activa trackInventory.

Siguientes pasos

Última actualización:

En esta página