Naar de hoofdinhoud

Voorraad- en magazijn-API

Voor ontwikkelaars: voorraad, artikelen, locaties en correcties via api/v1/stock, plus magazijnresources zoals ontvangsten, inkooporders, picklijsten, tellingen, containers, magazijnen en overboekingen. Scopes en foutcodes.

H
Geschreven door Hayko

Voorraad- en magazijn-API

Met de Voorraad- en magazijn-API leest en wijzigt uw eigen systeem uw QrX-magazijn: voorraadniveaus, artikelen, locaties, mutaties, ontvangsten, inkooporders, picklijsten, tellingen, containers en teamtaken. Dit artikel is voor ontwikkelaars. De basis-URL is https://api.qr-x.net en elke aanroep heeft Authorization: Bearer <accessToken> nodig; zie "Snelstart voor ontwikkelaars: QrX Public API en webhooks" voor het token. De volledige referentie staat op qrx.eu/api. Er wordt nooit een prijs, kostprijs of voorraadwaarde teruggegeven.

Toegang en scopes

  • Voorraadniveaus, artikelen, locaties, mutaties, tellingen en containers vereisen stock.read; artikelen en locaties aanmaken of wijzigen en een correctie boeken vereisen stock.write. Ontvangsten vereisen inbound.read en inbound.write. Inkooporders vereisen purchasing.read en purchasing.write. Picklijsten vereisen fulfilment.read en teamtaken tasks.read.

  • Een beperkte sleutel krijgt de nieuwe onderdelen nooit vanzelf. Vink Inkoop, Fulfilment of Taken aan onder API-toegang als de sleutel ze nodig heeft. Ontvangstregels tonen unitPrice en currency alleen als de sleutel ook inkooporders mag lezen.

  • Een API-sleutel die niet is beperkt heeft volledige toegang en kan de Voorraad- en magazijn-API dus direct gebruiken.

  • Om een sleutel te beperken opent u Settings > Business > Merchant Channels, opent u het verkoopkanaal, tabblad Settings, blok API-toegang, en vinkt u Voorraadniveaus, artikelen, locaties, mutaties, tellingen en containers lezen en Voorraad aanpassen en artikelen en locaties aanmaken of wijzigen aan waar nodig. Zie "Een API-sleutel beperken (API-toegang)".

  • Op uw account moet de QrX-magazijnvoorraad aan staan. Zonder antwoordt de API met 403 ACCESS_DENIED.

GET /api/v1/stock/levels

Eén rij per voorraadartikel en magazijn. Query: stockItemId, sku, warehouseId, locationId, updatedSince (ISO 8601), cursor, limit (1 tot 200, standaard 50) en 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 is alles wat open orders vasthouden. available is sellable min reserved.

  • lastSequence is het nummer van de laatste voorraadbeweging van dat artikel in dat magazijn. Het is hetzelfde nummer als sequence in de webhook stock/adjusted.

  • sku moet exact overeenkomen; hoofdletters, kleine letters en spaties aan de randen worden genegeerd.

  • Met locationId krijgt u alleen artikelen die voorraad op die locatie hebben, en locationQuantity bevat de stuks daar.

GET /api/v1/stock/items

Uw voorraadartikelen, de nieuwste eerst. Query: sku, ean, updatedSince, cursor, limit (1 tot 200, standaard 50) en 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

De locaties die voorraad kunnen bevatten, de nieuwste eerst. Query: warehouseId, cursor, limit (1 tot 200, standaard 50).

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

POST /api/v1/stock/adjust

Boekt één voorraadcorrectie. De header Idempotency-Key is verplicht.

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 }

  • Geef stockItemId of sku. Er moet precies één artikel overeenkomen.

  • Geef locationId. Een account zonder locaties mag in plaats daarvan warehouseId geven, of niets voor het standaardmagazijn.

  • delta is de wijziging in stuks: positief telt bij, negatief trekt af. Het mag niet 0 zijn.

  • reasonCode is verplicht: correction, found, damaged, expired, defective, lost, theft, sample of other.

  • Geef voor een artikel met lots lotCode en, wanneer het artikel die nodig heeft, expiryDate.

Het antwoord bevat de nieuwe stand van het artikel in dat magazijn:

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

Magazijnresources

Alle lijsten gebruiken cursor en limit (1 tot 100, standaard 50) en antwoorden met items en nextCursor, nieuwste eerst. Deze resources zijn alleen uw eigen magazijngegevens: scope=partner geeft 422. Alle velden van verzoek en antwoord staan in de referentie op qrx.eu/api.

  • Ontvangsten: GET /api/v1/receipts (filter state, warehouseId, q), GET /api/v1/receipts/{id}, POST /api/v1/receipts (met announce: true wordt de ontvangst aangemeld), PATCH /api/v1/receipts/{id}, POST .../{id}/lines, POST .../{id}/complete en POST .../{id}/cancel.

  • Inkooporders: GET /api/v1/purchase-orders, GET /api/v1/purchase-orders/{id}, POST /api/v1/purchase-orders (een concept), PATCH .../{id}, POST .../{id}/lines, POST .../{id}/send, POST .../{id}/close en POST .../{id}/cancel. Versturen werkt precies als in PortalX: met email: true mailt QrX de pdf naar de leverancier (of naar to), maximaal 5 keer per order en 30 keer per account per uur (429 RATE_LIMIT_EXCEEDED).

  • Voorraadartikelen en locaties: POST en PATCH /api/v1/stock/items en /api/v1/stock/locations. Locaties vereisen Full WMS met bins. De rijen van GET items en GET locations hebben nu version.

  • Lezen: GET /api/v1/stock/movements, GET /api/v1/picklists, GET /api/v1/counts (met /{id} en /{id}/lines), GET /api/v1/containers en GET /api/v1/tasks (met /{id}). Teamtaken zijn alleen-lezen.

  • Een wijziging van een bestaand record vereist de version die u het laatst hebt gelezen. Heeft iemand het record intussen gewijzigd, dan krijgt u 409: lees het opnieuw en probeer het nog eens.

  • Magazijnen (scope stock.read, Volledig WMS): GET /api/v1/warehouses met code, naam, standaard, tijdzone, adres, contact, ontvangstinstructies, laaddocks en GLN; GET /api/v1/warehouses/{id}, .../{id}/hours (ontvangst- en verzendtijden per weekdag) en .../{id}/closures (met from en cursor).

  • Overboekingen (scope stock.read, Volledig WMS met meerdere magazijnen en overboekingen): GET /api/v1/transfers (filter state, warehouseId, direction) en GET /api/v1/transfers/{id} met de regels en de gegevens in shipping. Alleen lezen.

  • Inkooporders: aanmaken en PATCH accepteren de kopvelden reference, supplierReference, buyerUserId, internalNote, incoterm, incotermPlace, paymentTermsDays, shippingCost, shippingVatRate, pricesIncludeVat, deliveryAddress en partialDeliveryAllowed, en regels accepteren discountPct, vatRate, note, confirmedQty en confirmedOn. Orders geven de totalen terug (netto, btw per tarief, bruto) zoals QrX ze berekent. De lijst filtert op reference, expectedFrom en expectedTo. Voorwaarden liggen vast zodra de order is verstuurd.

  • Ontvangsten: aanmaken en PATCH accepteren supplierId om een ontvangst aan een leverancier te koppelen; rijen geven het terug.

  • Voorraadartikelen en locaties: schrijfacties op locaties accepteren zone of zoneId en de velden voor maat en capaciteit; schrijfacties op artikelen accepteren storage, dangerousGoods, imageUrl en abcClassOverride. POST /api/v1/stock/adjust accepteert uw eigen redencodes.

Idempotency-Key

  • Stuur per correctie een eigen unieke tekst. Herhaalt u hetzelfde verzoek met dezelfde sleutel, dan krijgt u het eerste antwoord opnieuw met replayed: true en wordt er niets dubbel geboekt.

  • Dezelfde sleutel met een andere body wordt geweigerd met 409 DUPLICATE_IDEMPOTENCY_KEY.

  • Een sleutel mag niet beginnen met een QrX-systeemprefix zoals mcp- en mag niet eindigen op een dubbele punt gevolgd door cijfers (422).

  • Elke POST en PATCH van de magazijnresources vereist de header Idempotency-Key (1 tot 140 tekens). Dezelfde sleutel met dezelfde body op dezelfde route geeft het eerste antwoord opnieuw, met de header Idempotent-Replayed: true. Een herhaalde aanmaak van een ontvangst of inkooporder geeft het record zoals het nu is.

  • Antwoorden worden 30 dagen bewaard. Een mislukte aanroep (4xx of 5xx) wordt niet bewaard, dus u mag dezelfde sleutel opnieuw sturen.

  • Zolang de eerste aanroep met een sleutel nog loopt, krijgt u 409 CONCURRENCY_CONFLICT met Retry-After. Een aanroep die halverwege is gestopt, geeft zijn sleutel na 15 minuten vrij.

  • Een inkooporder versturen met email: true mailt de leverancier maximaal één keer per sleutel. Mislukt een aanroep nadat de order is verstuurd maar vóór de e-mail, dan volgt er voor die sleutel geen e-mail: controleer de e-mailhistorie van de order in PortalX en verstuur opnieuw met een nieuwe sleutel.

Cursors en updatedSince

  • Elke lijst geeft items en nextCursor terug. Stuur nextCursor terug als cursor voor de volgende pagina. Null betekent het einde. Er is geen totaal.

  • Behandel een cursor als een ondoorzichtige waarde.

  • Met updatedSince krijgt u alleen rijen die op of na dat tijdstip zijn gewijzigd. De server bekijkt maximaal 1.000 rijen per aanroep, dus een pagina kan minder rijen dan limit bevatten, zelfs geen, terwijl nextCursor gevuld is. Blijf aanroepen totdat nextCursor null is.

Voorraad bij een fulfilmentpartner: scope=partner

Bewaart een fulfilmentpartner uw voorraad, voeg dan scope=partner toe aan GET levels en GET items om uw eigen voorraad bij uw partners te lezen. Zonder krijgt u de standaard, scope=warehouse: de voorraad in uw eigen magazijnen.

  • Elke rij heeft partnerCustomerId (het partnermagazijn) en uw eigen id in ownerCustomerId.

  • Gebruik partnerCustomerId in de query om één partner te houden, en warehouseId voor één van zijn magazijnen.

  • Locaties worden nooit teruggegeven: locationId, GET locations en POST adjust antwoorden met 422 in deze scope.

  • U hebt minstens één partner met actieve toegang nodig; op uw eigen account hoeft de magazijnvoorraad niet aan te staan.

Foutcodes

  • 401 UNAUTHORIZED: geen geldig toegangstoken.

  • 403 STOCK_SCOPE_MISSING: de sleutel is beperkt en mist stock.read of stock.write.

  • 403 ACCESS_DENIED: de magazijnvoorraad staat niet aan voor het account, of, met scope=partner, geen partner heeft actieve toegang.

  • 404 STOCK_NOT_FOUND: het artikel, het magazijn of de locatie is niet van u.

  • 400 IDEMPOTENCY_KEY_REQUIRED: een POST zonder de header.

  • 422 VALIDATION_FAILED: ongeldige invoer, bijvoorbeeld delta 0, een onbekende redencode, een ongeldige cursor, of meer dan 50 artikelen met dezelfde SKU of EAN (filter op stockItemId).

  • 409 DUPLICATE_IDEMPOTENCY_KEY: dezelfde sleutel met een andere body.

  • 409 STOCK_CONFLICT: niet genoeg voorraad, voorraad die aan een picklijst is toegewezen, een SKU die bij meerdere artikelen past, of een inactief artikel.

  • 409 CONCURRENCY_CONFLICT: stuur hetzelfde verzoek opnieuw met dezelfde sleutel.

  • 429: te veel verzoeken; wacht op Retry-After.

  • 403 INBOUND_SCOPE_MISSING, PURCHASING_SCOPE_MISSING, FULFILMENT_SCOPE_MISSING of TASKS_SCOPE_MISSING: de sleutel is beperkt en mist dat onderdeel.

  • 404 INBOUND_NOT_FOUND, PURCHASE_ORDER_NOT_FOUND of NOT_FOUND: het record is niet van u.

  • 409 INBOUND_CONFLICT of PURCHASING_CONFLICT: de versie of de status staat de wijziging niet toe.

  • 429 RATE_LIMIT_EXCEEDED: te veel e-mails aan de leverancier voor een inkooporder.

  • 400 ZONE_UNKNOWN: de zone is geen actieve zone van dat magazijn. 400 DG_CLASS_UNKNOWN: de klasse gevaarlijke stoffen is geen zichtbare klasse van uw account. Een reden die foto's vraagt, kan niet via de API worden gebruikt.

Wanneer neemt u contact op met ons supportteam

Neem contact op met QrX support wanneer de API ACCESS_DENIED antwoordt terwijl de magazijnvoorraad aan staat, of wanneer een stand afwijkt van PortalX. Vermeld de requestId van het antwoord, de SKU en het tijdstip. Stuur nooit de API-sleutel of het token mee.

Was dit een antwoord op uw vraag?