Skip to main content

Stock and Warehouse Webhooks

H
Written by Hayko

Stock and Warehouse Webhooks

QrX can tell your own system when stock changes, when an item runs low and when an order is picked.

It can also tell your system about warehouse documents: receipts, purchase orders, counts, containers, team tasks and more pick list and order events. See "Warehouse Document Events" below.

This article is for developers. It covers the events stock/adjusted, stock/low and fulfilment/picked. For signatures, retries and delivery logs see "Webhooks and API Keys in PortalX" and "Developer Quickstart: QrX Public API and Webhooks".

Subscribe to the Events

  1. Go to Settings > Advanced > Webhooks and open or create a webhook.

  2. In the event list, open the group Stock and tick stock/adjusted and stock/low.

  3. Open the group Fulfilment and tick fulfilment/picked.

  4. The sample payload of each event shows a full example. Use Customize payload to choose the fields that are sent.

  5. For warehouse documents, open the groups Receipts, Purchase orders, Counts, Containers, Team tasks and Transfers and tick the events you need. The extra pick list and order events are in the group Fulfilment.

  6. Save the webhook.

Through the API you add the same event names to eventTypes in POST /api/v1/webhooks. The stock events need QrX warehouse stock on your account, or stock that a fulfilment partner holds for you.

stock/adjusted

Sent when the stock of an item changes in a warehouse: a receipt, a pick, a shipment, a return, a count, a move or a correction. You get one event per stock item, warehouse and operation, never one per scan. A change is announced about one minute after it was booked.

{
  "eventId": "stock/adjusted:1:98231",
  "occurredAt": "2026-10-11T09:12:03Z",
  "movementId": 98231,
  "sequence": 98231,
  "stockItemId": 345, "sku": "TSHIRT-M", "ean": "8712345678906",
  "ownerCustomerId": null,
  "partnerCustomerId": null,
  "warehouseId": 1, "warehouseCode": "MAIN",
  "fromLocationCode": "A-01-02", "toLocationCode": null,
  "reason": "adjusted",
  "documentType": "adjust", "documentId": 5521,
  "movementCount": 1,
  "quantity": 3,
  "delta": -3,
  "onHand": 42, "sellable": 40, "available": 35
}

  • eventId: the key to de-duplicate on. Treat it as an opaque text; do not parse it.

  • sequence: a number that always goes up per item and warehouse. It equals lastSequence of GET /api/v1/stock/levels right after the movement.

  • movementId, occurredAt: the newest stock movement in the event and its time (UTC).

  • ownerCustomerId: the merchant the stock is held for; null for your own stock. partnerCustomerId: null, except in the owner copy described below.

  • fromLocationCode, toLocationCode: the location the units left and entered. Null means outside stock (supplier, customer, correction) or several locations in one event.

  • reason: for example received, putaway, moved, adjusted, shipped, returned, scrapped, blocked, unblocked. Treat a value you do not know as a generic movement.

  • documentType, documentId, movementCount: the stock document behind the change and how many movements the event sums up.

  • quantity: units moved. delta: change of the stock on hand in this warehouse; 0 for a move, a pick or a put-away inside the warehouse.

  • onHand, sellable, available: the level of the item in this warehouse right after the movement.

stock/low

Sent once each time an item drops to its low stock alert level or below, at the same moment as the bell notification. It is not repeated while the item stays low. See "Low Stock Alerts" for how to set an alert.

{
  "eventId": "stock/low:12:4",
  "occurredAt": "2026-10-11T09:13:00Z",
  "ruleId": 12, "crossing": 4,
  "stockItemId": 345, "sku": "TSHIRT-M", "ean": "8712345678906", "name": "T-shirt M",
  "ownerCustomerId": null,
  "partnerCustomerId": null,
  "warehouseId": null, "warehouseCode": null,
  "threshold": 10, "available": 7
}

  • ruleId: the alert. crossing: counts the alerts of this rule (1, 2, 3 and so on).

  • warehouseId, warehouseCode: null when the alert counts all warehouses together.

  • threshold: the alert level. available: the available units when the alert fired.

fulfilment/picked

Sent once per order when the phone pick is completed: the moment the picker finishes the pick list in the QrX app, every order on that pick list gets one event. A paper pick list, or one that is only worked on screen, does not send it: for those orders the first event is fulfilment/packed.

{
  "eventId": "fulfilment/picked:881:120045",
  "occurredAt": "2026-10-13T10:02:11Z",
  "sessionId": null,
  "transactionId": 120045,
  "transactionKey": "TR-8F3K2M",
  "orderId": "1001",
  "picklistId": 881,
  "pickedAt": "2026-10-13T10:02:11Z",
  "pickedByUserId": "b6c0...",
  "hasShorts": false
}

  • transactionId, transactionKey, orderId: the order, as in fulfilment/packed.

  • picklistId: the pick list. Null when a fulfilment partner picked the order for you.

  • pickedAt: when the pick list was completed. hasShorts: true when at least one line of this order was picked short.

  • pickedByUserId: the picker. It is off by default; switch it on with Customize payload. Null when a fulfilment partner picked.

  • sessionId: always null for this event.

Warehouse Document Events

Every event below has an eventId (opaque, de-duplicate on it) and occurredAt. The sample payload in the editor shows every field. No event contains prices or costs.

  • Receipts: receipt/created, receipt/line_received (one event per booked receipt, with the received lines) and receipt/completed. Fields include receiptId, receiptNumber, state, warehouseCode, purchaseOrderId, reference, supplierName and totals.

  • Purchase orders: purchase_order/created, purchase_order/sent, purchase_order/received (also when a dropship order is settled) and purchase_order/closed (with reason). Fields include purchaseOrderId, purchaseOrderNumber, state, supplierName, expectedOn and quantity totals.

  • Counts: count/completed (the count is submitted) and count/approved, with countId, kind, state and totals such as counted lines and lines with a variance.

  • Containers: container/created, container/moved (with the previous location) and container/emptied (with the target location, lines and quantity).

  • Team tasks: task/created and task/completed, with taskId, title, status, dueAt and the object the task is about.

  • Fulfilment: picklist/assigned (a pick list was given to a picker), picklist/cancelled, picklist/batch_created (a batch run made several pick lists; it lists their ids) and fulfilment/snoozed (an order was snoozed, or its snooze was cleared; snoozed tells which).

  • Transfers: transfer/sent, transfer/received (one event per receipt at the destination, with documentId and the received lines), transfer/completed and transfer/cancelled. Fields include transferId, transferNumber, state, the from and to warehouse, reference, expectedOn, sentAt, completedAt, shipping (carrier, tracking number and link, packages, weight) and quantity totals. They belong to no sales channel.

  • Receipt events now carry supplierId when the receipt is linked to a supplier. Purchase order events add reference, supplierReference, supplierConfirmedAt and partialDeliveryAllowed, still without prices.

De-duplicate and Keep the Order

  • Every event is delivered at least once, so the same event can arrive twice. Store the eventId values you processed and skip one you have seen.

  • Events can arrive out of order. For stock/adjusted, store the last sequence per stock item and warehouse and ignore an event with a lower number.

  • To start, or to recover after your endpoint was down, read the levels with GET /api/v1/stock/levels and compare lastSequence. After a long interruption, stock changes older than 6 hours are no longer announced as stock/adjusted.

Delivery Rules

  • A stock event belongs to no sales channel. stock/adjusted and stock/low go to every active webhook of your account that ticked the event, also to a webhook that is set to one channel.

  • fulfilment/picked follows the sales channel of the order, like fulfilment/packed, and needs the pack station on your account.

  • A large burst of stock events, for example after an import or a full count, is spread over the following minutes. It never delays your order and fulfilment events.

  • A paused webhook skips events; they are not queued.

  • The events of receipts, purchase orders, counts, containers, team tasks, fulfilment/snoozed and the inbound events belong to no sales channel either. Every active webhook of your account that ticked them receives them, also a webhook that is set to one channel, and also when the pack station is off. The receiver is the account whose warehouse document it is.

  • The pick list events follow the sales channel of the pick list, like picklist/created.

  • A webhook saved less than 15 seconds ago can miss an event of that moment.

When a Fulfilment Partner Holds Your Stock

As the owner of the stock you get your own copy of the stock events for your own items, as long as the partner's access is active. The field names are the same, with these differences:

  • partnerCustomerId is the partner warehouse and ownerCustomerId is your own account.

  • fromLocationCode, toLocationCode, documentType and documentId are always null. You never receive locations.

  • The eventId differs from the one your partner receives.

  • stock/low only comes from your own low stock alerts, never from the alerts of your partner.

When to Contact Our Support Team

Contact QrX support when stock events do not arrive although the event is ticked, the webhook is active and your endpoint returns 2xx. Include the webhook name, the SKU, the warehouse and the time of the stock change.

Did this answer your question?