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/stockMovements returning 201 with origin, destination and requestedBy as 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). dateRequested is MM/DD/YYYY in 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 400 if pagination parameters are missing. Always include max and offset.
  • 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 captured POST /api/v1/stockMovements/{id}/updateShipment returning 200 for shipment details (dateShipped, shipmentType, trackingNumber, driverName, expectedDeliveryDate), and updateRequisition is its counterpart for the request side. We have not verified a PUT on /api/v1/stockMovements/{id}, and this page does not document one.