Stock Movements API
Stock movements represent the transfer of goods between locations. They are the primary mechanism for requesting, shipping, and receiving inventory in OpenBoxes.
List Stock Movements#
Retrieve a paginated list of stock movements:
curl -H "X-API-Key: $OB_API_KEY" \
"https://acme.openboxes.cloud/api/v1/stockMovements?max=25&offset=0"
Query Parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
max |
integer | all | Maximum records to return |
offset |
integer | 0 |
Number of records to skip |
direction |
string | None | Filter by direction: INBOUND or OUTBOUND |
receiptStatusCode |
string | None | Filter by receipt status |
exclude |
string | None | Comma-separated statuses to exclude |
Example: List Outbound Movements#
curl -H "X-API-Key: $OB_API_KEY" \
"https://acme.openboxes.cloud/api/v1/stockMovements?direction=OUTBOUND&max=10"
Response#
{
"data": [
{
"id": "sm-001",
"name": "Transfer to Regional Depot",
"description": "Monthly resupply",
"identifier": "SM-00142",
"status": "SHIPPED",
"origin": {
"id": "loc-001",
"name": "Central Warehouse"
},
"destination": {
"id": "loc-002",
"name": "Regional Depot"
},
"dateRequested": "2025-03-01T00:00:00Z",
"dateShipped": "2025-03-05T14:30:00Z",
"requestedBy": {
"id": "user-001",
"name": "Jane Smith"
}
}
]
}
Get Stock Movement Details#
Retrieve a single stock movement with full details including line items:
curl -H "X-API-Key: $OB_API_KEY" \
"https://acme.openboxes.cloud/api/v1/stockMovements/sm-001"
Response#
{
"data": {
"id": "sm-001",
"name": "Transfer to Regional Depot",
"identifier": "SM-00142",
"status": "SHIPPED",
"origin": { "id": "loc-001", "name": "Central Warehouse" },
"destination": { "id": "loc-002", "name": "Regional Depot" },
"dateRequested": "2025-03-01T00:00:00Z",
"requestedBy": { "id": "user-001", "name": "Jane Smith" },
"lineItems": [
{
"id": "li-001",
"product": { "id": "prod-001", "name": "Ibuprofen 200mg", "productCode": "IBU200" },
"quantityRequested": 1000,
"quantityShipped": 1000,
"quantityReceived": null,
"sortOrder": 0
}
]
}
}
Create Stock Movement#
Create a new stock movement request. A successful create returns 201 with the new
movement (its id is the underlying requisition id):
curl -X POST \
-H "X-API-Key: $OB_API_KEY" \
-H "Content-Type: application/json" \
"https://acme.openboxes.cloud/api/v1/stockMovements" \
-d '{
"name": "",
"description": "Emergency Resupply",
"dateRequested": "04/01/2025",
"origin": "loc-001",
"destination": "loc-002",
"requestedBy": "user-001",
"requestType": "ADHOC",
"sourceType": "ELECTRONIC",
"stocklist": { "id": "" },
"approvers": []
}'
Fields#
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | Yes | What the movement is for; this is what the lists display |
origin |
string or object | Yes | The source location. See the note on shapes below |
destination |
string or object | Yes | The destination location |
requestedBy |
string or object | Yes | The requesting user |
dateRequested |
string | Yes | MM/DD/YYYY, not ISO 8601, in every capture we have |
requestType |
string | For a stock request | ADHOC or STOCK. An inbound movement has none, so omit it |
sourceType |
string | No | ELECTRONIC marks the request as app-created, which is what puts it on the dashboard's request tiles |
name, stocklist and approvers are sent empty in the captures above and are
shown exactly as captured: OpenBoxes generates the movement's name itself, and the
other two are populated only when you request from a stocklist or route the
request for approval.
Two association shapes are both accepted. We have captured
POST /api/v1/stockMovementsreturning201withorigin,destinationandrequestedByas bare id strings (the shape the stock-request wizard sends, shown above) and with the nested{ "id": … }objects the read responses use (the shape the inbound and outbound wizards send).dateRequestedisMM/DD/YYYYin all of them. Pick one and keep it consistent; if a create is rejected, try the other shape before assuming the rest of the payload is wrong. We have not established a rule for which shape a given movement type prefers -- only that both have succeeded.
Add and Update Line Items#
Line items are not written through /api/v1/stockMovements/{id}. They go to
the movement's own updateItems sub-endpoint, which takes the full desired set of
line items and reconciles them in one call: adding new ones, updating the ones
that carry an id, and, with removeEmptyItems, dropping empties:
curl -X POST \
-H "X-API-Key: $OB_API_KEY" \
-H "Content-Type: application/json" \
"https://acme.openboxes.cloud/api/v1/stockMovements/sm-001/updateItems" \
-d '{
"id": "sm-001",
"lineItems": [
{
"product": { "id": "prod-001" },
"quantityRequested": "120",
"sortOrder": 100,
"comments": "",
"quantityCounted": 0
}
],
"removeEmptyItems": true
}'
A successful call returns 200. Repeat it with an item's id included to change
that item's quantity. There is no separate endpoint for removing a single line
item, and there is no delete flag on a line item: send updateItems with the
set you want and removeEmptyItems: true.
Change the Status#
Move a stock movement through its workflow with the status sub-endpoint:
curl -X POST \
-H "X-API-Key: $OB_API_KEY" \
-H "Content-Type: application/json" \
"https://acme.openboxes.cloud/api/v1/stockMovements/sm-001/status" \
-d '{ "status": "PENDING_APPROVAL" }'
Stock Movement Statuses#
Stock movements progress through a defined workflow:
| Status | Description |
|---|---|
REQUESTING |
Draft: items are being added |
REQUESTED |
Submitted for fulfillment |
CHECKING |
Being reviewed and validated |
PICKING |
Items are being picked from shelves |
PICKED |
All items picked, ready to pack |
PACKING |
Items being packed for shipment |
PACKED |
Packed and ready to ship |
SHIPPED |
In transit to destination |
RECEIVED |
Delivered and received at destination |
Filtering by Status#
Exclude specific statuses from the list response:
# Get all movements except received ones
curl -H "X-API-Key: $OB_API_KEY" \
"https://acme.openboxes.cloud/api/v1/stockMovements?exclude=RECEIVED"
Important Notes#
- Required parameters for list: The list endpoint may return
400if pagination parameters are missing. Always includemaxandoffset. - Location context: Stock movements are filtered based on the current location: the Default location on the key creator's user record, or the only depot they have access to (see Locations for the exact rule). A key creator with no Default location on a multi-depot organization has no current location, and creating a stock movement fails until an administrator sets one (Configuration ▸ Users ▸ edit ▸ Default location). Outbound movements show shipments FROM your location; inbound movements show shipments TO your location.
- Status transitions: not all transitions are valid, and some depend on how the fulfilling location is configured. Approval, for example, requires that location to support request approval. The API returns an error rather than skipping a step.
- Line items: adding, changing and removing line items all go through
POST /api/v1/stockMovements/{id}/updateItems. The movement's own header fields have their own sub-endpoints. We have capturedPOST /api/v1/stockMovements/{id}/updateShipmentreturning200for shipment details (dateShipped,shipmentType,trackingNumber,driverName,expectedDeliveryDate), andupdateRequisitionis its counterpart for the request side. We have not verified aPUTon/api/v1/stockMovements/{id}, and this page does not document one.