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, necesitanstock.write. Las recepciones necesitaninbound.readeinbound.write. Los pedidos de compra necesitanpurchasing.readypurchasing.write. Las listas de preparación necesitanfulfilment.ready las tareas de equipotasks.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
unitPriceycurrencysi 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"
}
reservedes todo lo que retienen los pedidos abiertos.availableessellablemenosreserved.lastSequencees el número del último movimiento de stock de ese artículo en ese almacén. Es el mismo número quesequenceen el webhookstock/adjusted.skudebe coincidir exactamente; se ignoran las mayúsculas y minúsculas y los espacios de los extremos.Con
locationIdsolo obtiene los artículos que tienen stock en esa ubicación, ylocationQuantitycontiene 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
stockItemIdosku. Debe coincidir exactamente un artículo.Indique
locationId. Una cuenta sin ubicaciones puede indicarwarehouseIden su lugar, o nada para el almacén por defecto.deltaes el cambio en unidades: positivo suma, negativo resta. No puede ser 0.reasonCodees obligatorio:correction,found,damaged,expired,defective,lost,theft,sampleuother.Para un artículo con lotes, indique
lotCodey, 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(filtrosstate,warehouseId,q),GET /api/v1/receipts/{id},POST /api/v1/receipts(conannounce: truela recepción se anuncia),PATCH /api/v1/receipts/{id},POST .../{id}/lines,POST .../{id}/completeyPOST .../{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}/closeyPOST .../{id}/cancel. El envío funciona exactamente como en PortalX: conemail: trueQrX envía el PDF al proveedor (o ato), como máximo 5 veces por pedido y 30 veces por cuenta y hora (429RATE_LIMIT_EXCEEDED).Artículos y ubicaciones:
POSTyPATCH /api/v1/stock/itemsy/api/v1/stock/locations. Las ubicaciones necesitan Full WMS con huecos. Las filas deGET itemsyGET locationsahora tienenversion.Lecturas:
GET /api/v1/stock/movements,GET /api/v1/picklists,GET /api/v1/counts(con/{id}y/{id}/lines),GET /api/v1/containersyGET /api/v1/tasks(con/{id}). Las tareas de equipo son de solo lectura.Un cambio en un registro existente necesita la
versionque 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/warehousescon 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(confromycursor).Traslados (scope
stock.read, WMS completo con varios almacenes y traslados):GET /api/v1/transfers(filtrosstate,warehouseId,direction) yGET /api/v1/transfers/{id}con las líneas y los datos deshipping. Solo lectura.Pedidos de compra: la creación y
PATCHaceptan los campos de cabecerareference,supplierReference,buyerUserId,internalNote,incoterm,incotermPlace,paymentTermsDays,shippingCost,shippingVatRate,pricesIncludeVat,deliveryAddressypartialDeliveryAllowed, y las líneas aceptandiscountPct,vatRate,note,confirmedQtyyconfirmedOn. Los pedidos devuelven los totales (neto, IVA por tipo, bruto) calculados por QrX. La lista filtra porreference,expectedFromyexpectedTo. Las condiciones quedan fijadas una vez enviado el pedido.Recepciones: la creación y
PATCHaceptansupplierIdpara vincular una recepción a un proveedor; las filas lo devuelven.Artículos de stock y ubicaciones: las escrituras de ubicaciones aceptan
zoneozoneIdy los campos de tamaño y capacidad; las escrituras de artículos aceptanstorage,dangerousGoods,imageUrlyabcClassOverride.POST /api/v1/stock/adjustacepta 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: truey 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 cabeceraIdempotent-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_CONFLICTconRetry-After. Una llamada que se detuvo a medias libera su clave a los 15 minutos.Enviar un pedido de compra con
email: truemanda 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
itemsynextCursor. EnvíenextCursorcomocursorpara la página siguiente. Null significa el final. No hay total.Trate un cursor como un valor opaco.
Con
updatedSincesolo 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 quelimit, incluso ninguna, mientrasnextCursortiene valor. Siga llamando hasta quenextCursorsea 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 enownerCustomerId.Use
partnerCustomerIden la query para quedarse con un socio, ywarehouseIdpara uno de sus almacenes.Las ubicaciones nunca se devuelven:
locationId,GET locationsyPOST adjustresponden 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 tienestock.readostock.write.403
ACCESS_DENIED: el stock de almacén no está activado para la cuenta o, conscope=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 porstockItemId).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_MISSINGoTASKS_SCOPE_MISSING: la clave está restringida y le falta esa parte.404
INBOUND_NOT_FOUND,PURCHASE_ORDER_NOT_FOUNDoNOT_FOUND: el registro no es suyo.409
INBOUND_CONFLICToPURCHASING_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. 400DG_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.
