Ir al contenido principal

API de stock y almacén

Para desarrolladores: niveles, artículos, ubicaciones y correcciones de stock, y recursos del almacén como recepciones, pedidos de compra, recuentos, contenedores, almacenes y traslados. Scopes, Idempotency-Key, cursores y errores.

H
Escrito por Hayko

API de stock y almacén

Con la API de stock y almacén, su propio sistema lee y modifica su almacén de QrX: niveles de stock, artículos, ubicaciones, movimientos, recepciones, pedidos de compra, listas de preparación, recuentos, contenedores y tareas de equipo. Este artículo es para desarrolladores. La URL base es https://api.qr-x.net y cada llamada necesita Authorization: Bearer <accessToken>; consulte "Guía rápida para desarrolladores: API pública y webhooks de QrX" para el token. La referencia completa está en qrx.eu/api. Nunca se devuelve un precio, un coste ni un valor de stock.

Acceso y scopes

  • Los niveles de stock, artículos, ubicaciones, movimientos, recuentos y contenedores necesitan stock.read; crear o modificar artículos y ubicaciones, y registrar una corrección, necesitan stock.write. Las recepciones necesitan inbound.read e inbound.write. Los pedidos de compra necesitan purchasing.read y purchasing.write. Las listas de preparación necesitan fulfilment.read y las tareas de equipo tasks.read.

  • Una clave restringida nunca recibe las nuevas partes por sí sola. Marque Compras, Preparación o Tareas en Acceso a la API cuando la clave las necesite. Las líneas de recepción solo muestran unitPrice y currency si la clave también puede leer pedidos de compra.

  • Una clave de API que no está restringida tiene acceso total, así que puede usar la API de stock de inmediato.

  • Para limitar una clave, abra Settings > Business > Merchant Channels, abra el canal de venta, pestaña Settings, bloque Acceso a la API, y marque Leer niveles de stock, artículos, ubicaciones, movimientos, recuentos y contenedores y Ajustar stock y crear o modificar artículos y ubicaciones según necesite. Consulte "Restringir una clave de API (Acceso a la API)".

  • Su cuenta necesita tener activado el stock de almacén de QrX. Sin él, la API responde 403 ACCESS_DENIED.

GET /api/v1/stock/levels

Una fila por artículo de stock y almacén. Query: stockItemId, sku, warehouseId, locationId, updatedSince (ISO 8601), cursor, limit (de 1 a 200, 50 por defecto) y scope.

{
  "items": [{
    "stockItemId": 345, "sku": "TSHIRT-M", "ean": "8712345678906",
    "ownerCustomerId": null, "partnerCustomerId": null,
    "warehouseId": 1, "warehouseCode": "MAIN",
    "onHand": 42, "sellable": 40, "blocked": 2, "reserved": 5,
    "incoming": 0, "backorder": 0, "available": 35,
    "locationQuantity": null,
    "lastSequence": 98231, "updatedAt": "2026-10-11T09:12:03Z"
  }],
  "nextCursor": "1-345"
}

  • reserved es todo lo que retienen los pedidos abiertos. available es sellable menos reserved.

  • lastSequence es el número del último movimiento de stock de ese artículo en ese almacén. Es el mismo número que sequence en el webhook stock/adjusted.

  • sku debe coincidir exactamente; se ignoran las mayúsculas y minúsculas y los espacios de los extremos.

  • Con locationId solo obtiene los artículos que tienen stock en esa ubicación, y locationQuantity contiene las unidades que hay allí.

GET /api/v1/stock/items

Sus artículos de stock, los más recientes primero. Query: sku, ean, updatedSince, cursor, limit (de 1 a 200, 50 por defecto) y scope.

{
  "items": [{
    "id": 345, "sku": "TSHIRT-M", "name": "T-shirt M", "ean": "8712345678906",
    "unit": "piece", "kind": "simple", "trackingMode": "none", "isActive": true,
    "ownerCustomerId": null, "partnerCustomerId": null, "weightG": 180,
    "updatedAt": "2026-10-01T08:00:00Z"
  }],
  "nextCursor": 344
}

GET /api/v1/stock/locations

Las ubicaciones que pueden contener stock, las más recientes primero. Query: warehouseId, cursor, limit (de 1 a 200, 50 por defecto).

{
  "items": [{ "id": 77, "warehouseId": 1, "warehouseCode": "MAIN", "code": "A-01-02",
    "kind": "bin", "zone": "A", "isActive": true }],
  "nextCursor": 76
}

POST /api/v1/stock/adjust

Registra una corrección de stock. La cabecera Idempotency-Key es obligatoria.

POST /api/v1/stock/adjust
Idempotency-Key: erp-count-2026-10-11-0001{ "stockItemId": 345, "sku": null, "warehouseId": null, "locationId": 77, "delta": -3,
  "reasonCode": "correction", "note": "Cycle count by ERP", "lotCode": null, "expiryDate": null }

  • Indique stockItemId o sku. Debe coincidir exactamente un artículo.

  • Indique locationId. Una cuenta sin ubicaciones puede indicar warehouseId en su lugar, o nada para el almacén por defecto.

  • delta es el cambio en unidades: positivo suma, negativo resta. No puede ser 0.

  • reasonCode es obligatorio: correction, found, damaged, expired, defective, lost, theft, sample u other.

  • Para un artículo con lotes, indique lotCode y, cuando el artículo lo necesite, expiryDate.

La respuesta contiene el nuevo nivel del artículo en ese almacén:

{ "movementId": 98231, "stockItemId": 345, "warehouseId": 1, "locationId": 77, "delta": -3,
  "onHand": 42, "sellable": 40, "available": 35, "sequence": 98231, "replayed": false }

Recursos del almacén

Todas las listas usan cursor y limit (1 a 100, 50 por defecto) y responden con items y nextCursor, de lo más reciente a lo más antiguo. Estos recursos son solo los datos de su propio almacén: scope=partner responde 422. Todos los campos de petición y respuesta están en la referencia en qrx.eu/api.

  • Recepciones: GET /api/v1/receipts (filtros state, warehouseId, q), GET /api/v1/receipts/{id}, POST /api/v1/receipts (con announce: true la recepción se anuncia), PATCH /api/v1/receipts/{id}, POST .../{id}/lines, POST .../{id}/complete y POST .../{id}/cancel.

  • Pedidos de compra: GET /api/v1/purchase-orders, GET /api/v1/purchase-orders/{id}, POST /api/v1/purchase-orders (un borrador), PATCH .../{id}, POST .../{id}/lines, POST .../{id}/send, POST .../{id}/close y POST .../{id}/cancel. El envío funciona exactamente como en PortalX: con email: true QrX envía el PDF al proveedor (o a to), como máximo 5 veces por pedido y 30 veces por cuenta y hora (429 RATE_LIMIT_EXCEEDED).

  • Artículos y ubicaciones: POST y PATCH /api/v1/stock/items y /api/v1/stock/locations. Las ubicaciones necesitan Full WMS con huecos. Las filas de GET items y GET locations ahora tienen version.

  • Lecturas: GET /api/v1/stock/movements, GET /api/v1/picklists, GET /api/v1/counts (con /{id} y /{id}/lines), GET /api/v1/containers y GET /api/v1/tasks (con /{id}). Las tareas de equipo son de solo lectura.

  • Un cambio en un registro existente necesita la version que leyó por última vez. Si alguien cambió el registro mientras tanto, recibe 409: vuelva a leerlo e inténtelo de nuevo.

  • Almacenes (scope stock.read, WMS completo): GET /api/v1/warehouses con código, nombre, predeterminado, zona horaria, dirección, contacto, instrucciones de recepción, muelles de carga y GLN; GET /api/v1/warehouses/{id}, .../{id}/hours (horario de recepción y de expedición por día de la semana) y .../{id}/closures (con from y cursor).

  • Traslados (scope stock.read, WMS completo con varios almacenes y traslados): GET /api/v1/transfers (filtros state, warehouseId, direction) y GET /api/v1/transfers/{id} con las líneas y los datos de shipping. Solo lectura.

  • Pedidos de compra: la creación y PATCH aceptan los campos de cabecera reference, supplierReference, buyerUserId, internalNote, incoterm, incotermPlace, paymentTermsDays, shippingCost, shippingVatRate, pricesIncludeVat, deliveryAddress y partialDeliveryAllowed, y las líneas aceptan discountPct, vatRate, note, confirmedQty y confirmedOn. Los pedidos devuelven los totales (neto, IVA por tipo, bruto) calculados por QrX. La lista filtra por reference, expectedFrom y expectedTo. Las condiciones quedan fijadas una vez enviado el pedido.

  • Recepciones: la creación y PATCH aceptan supplierId para vincular una recepción a un proveedor; las filas lo devuelven.

  • Artículos de stock y ubicaciones: las escrituras de ubicaciones aceptan zone o zoneId y los campos de tamaño y capacidad; las escrituras de artículos aceptan storage, dangerousGoods, imageUrl y abcClassOverride. POST /api/v1/stock/adjust acepta sus propios códigos de motivo.

Idempotency-Key

  • Envíe su propio texto único por corrección. Si repite la misma petición con la misma clave, recibe de nuevo la primera respuesta con replayed: true y nada se registra dos veces.

  • La misma clave con otro cuerpo se rechaza con 409 DUPLICATE_IDEMPOTENCY_KEY.

  • Una clave no puede empezar por un prefijo de sistema de QrX como mcp- ni terminar en dos puntos seguidos de dígitos (422).

  • Cada POST y PATCH de los recursos del almacén necesita la cabecera Idempotency-Key (1 a 140 caracteres). La misma clave con el mismo cuerpo en la misma ruta devuelve la primera respuesta, con la cabecera Idempotent-Replayed: true. Una creación repetida de una recepción o un pedido de compra devuelve el registro tal como está ahora.

  • Las respuestas se guardan 30 días. Una llamada fallida (4xx o 5xx) no se guarda, así que puede enviar la misma clave de nuevo.

  • Mientras la primera llamada con una clave sigue en curso, recibe 409 CONCURRENCY_CONFLICT con Retry-After. Una llamada que se detuvo a medias libera su clave a los 15 minutos.

  • Enviar un pedido de compra con email: true manda el correo al proveedor como máximo una vez por clave. Si una llamada falla después de enviar el pedido pero antes del correo, no se envía ningún correo para esa clave: revise el historial de correos del pedido en PortalX y vuelva a enviarlo con una clave nueva.

Cursores y updatedSince

  • Cada lista devuelve items y nextCursor. Envíe nextCursor como cursor para la página siguiente. Null significa el final. No hay total.

  • Trate un cursor como un valor opaco.

  • Con updatedSince solo obtiene las filas modificadas en ese momento o después. El servidor examina hasta 1.000 filas por llamada, así que una página puede tener menos filas que limit, incluso ninguna, mientras nextCursor tiene valor. Siga llamando hasta que nextCursor sea null.

Stock en un socio de fulfillment: scope=partner

Cuando un socio de fulfillment guarda su stock, añada scope=partner a GET levels y GET items para leer su propio stock en sus socios. Sin él obtiene el valor por defecto, scope=warehouse: el stock de sus propios almacenes.

  • Cada fila tiene partnerCustomerId (el almacén del socio) y su propio id en ownerCustomerId.

  • Use partnerCustomerId en la query para quedarse con un socio, y warehouseId para uno de sus almacenes.

  • Las ubicaciones nunca se devuelven: locationId, GET locations y POST adjust responden 422 en este scope.

  • Necesita al menos un socio con acceso activo; su propia cuenta no necesita tener activado el stock de almacén.

Códigos de error

  • 401 UNAUTHORIZED: no hay un token de acceso válido.

  • 403 STOCK_SCOPE_MISSING: la clave está restringida y no tiene stock.read o stock.write.

  • 403 ACCESS_DENIED: el stock de almacén no está activado para la cuenta o, con scope=partner, ningún socio tiene acceso activo.

  • 404 STOCK_NOT_FOUND: el artículo, el almacén o la ubicación no es suyo.

  • 400 IDEMPOTENCY_KEY_REQUIRED: un POST sin la cabecera.

  • 422 VALIDATION_FAILED: datos no válidos, por ejemplo delta 0, un código de motivo desconocido, un cursor no válido, o más de 50 artículos con el mismo SKU o EAN (filtre por stockItemId).

  • 409 DUPLICATE_IDEMPOTENCY_KEY: la misma clave con otro cuerpo.

  • 409 STOCK_CONFLICT: no hay stock suficiente, stock asignado a una lista de preparación, un SKU que coincide con varios artículos, o un artículo inactivo.

  • 409 CONCURRENCY_CONFLICT: envíe de nuevo la misma petición con la misma clave.

  • 429: demasiadas peticiones; espere Retry-After.

  • 403 INBOUND_SCOPE_MISSING, PURCHASING_SCOPE_MISSING, FULFILMENT_SCOPE_MISSING o TASKS_SCOPE_MISSING: la clave está restringida y le falta esa parte.

  • 404 INBOUND_NOT_FOUND, PURCHASE_ORDER_NOT_FOUND o NOT_FOUND: el registro no es suyo.

  • 409 INBOUND_CONFLICT o PURCHASING_CONFLICT: la versión o el estado no permiten el cambio.

  • 429 RATE_LIMIT_EXCEEDED: demasiados correos al proveedor para un pedido de compra.

  • 400 ZONE_UNKNOWN: la zona no es una zona activa de ese almacén. 400 DG_CLASS_UNKNOWN: la clase de mercancías peligrosas no es una clase visible de su cuenta. Un motivo que necesita fotos no se puede usar a través de la API.

Cuándo contactar con nuestro equipo de soporte

Contacte con el soporte de QrX cuando la API responda ACCESS_DENIED aunque el stock de almacén esté activado, o cuando un nivel difiera de PortalX. Indique el requestId de la respuesta, el SKU y la hora. Nunca envíe la clave de API ni el token.

¿Ha quedado contestada tu pregunta?