Skip to content
Knowledge ERP Docs
API Reference ↗
Inventory

Transfers

Move stock between locations with a structured request, approve, ship, and receive workflow — every unit tracked individually from source bin to destination bin.

An inventory transfer is a formal request to move units from one location to another. Rather than silently teleporting stock, transfers give a source location the chance to approve and assign specific units, then let the destination location receive them into a bin — keeping the audit trail honest and the unit statuses accurate throughout the journey.

Transfers only appear in multi-location accounts. The Transfers menu item is hidden when your account has a single location, because moving stock between bins within one location is done directly on the unit.

The transfer lifecycle #

A transfer moves through a fixed sequence of statuses:

  • Draft — being built; no units are committed yet.
  • Requested — the origin location has been notified. The transfer appears as a pending action for users at the source.
  • Approved — a user at the source location has reviewed the request, assigned specific units to each line, and confirmed shipment.
  • In Transit — the units are physically on the way.
  • Delivered — the shipment has arrived at the destination and is ready to be received.
  • Received — all units have been scanned or confirmed into a destination bin. Units land as In Stock at the new location.
  • Cancelled — the transfer was rejected or abandoned. Cancellation is available from Draft, Requested, and Approved; once a transfer is In Transit or later it cannot be cancelled.

Each status transition is timestamped on the transfer record (requested_at, approved_at, shipped_at, delivered_at, received_at).

Creating a transfer #

New transfers are created from Inventory → Transfers → New Transfer.

  • From Location — where the stock is currently held. Required.
  • To Location — where it needs to go. Must differ from the source. Required.
  • Requested By — the user initiating the request. Optional; defaults to unknown.
  • Status — starts as Draft. Change to Requested to notify the source location.
  • Expected Delivery Date — an optional target date used for planning.
  • Notes — free-text context for the approver.
  • Requested Units (repeater) — one or more lines, each specifying a Product and a requested quantity. You can add as many lines as needed with Add Line.

Save the transfer in Draft to keep editing it, or set the status to Requested when it's ready for the source location to act on.

Approving a transfer #

When a transfer reaches Requested status, users at the source location see a badge on the Transfers menu item. Opening the transfer and clicking Approve opens a dedicated approval screen.

On the approval screen each requested line is shown alongside the available units at the source location, grouped by bin, lot, and expiration date. You assign units to each line by:

  • Clicking individual units to toggle them in or out,
  • Entering a quantity for a group to auto-select that many units from it, or
  • Scanning barcodes — scan a bin barcode first to set context, then scan unit or Product barcodes to assign them one by one.

The screen shows whether each line is satisfied (assigned quantity meets the requested quantity) or still short. You can approve a transfer even if some lines are only partially filled — if the destination location has another source configured with auto-transfer enabled, the remaining shortfall is automatically routed to that source as a new Requested transfer.

Clicking Reject All cancels the transfer entirely and notifies the requester.

Once approved, the assigned units are placed On Hold at the source location and the transfer advances to Approved.

Shipping and delivery #

After approval, mark the transfer In Transit when the units leave the source (optionally recording a tracking number on the edit form). When the shipment arrives at the destination, advance the status to Delivered — this queues it for receiving.

Receiving a transfer #

When a transfer is Delivered, users at the destination location see it flagged for action. Opening it and clicking Receive opens the receiving screen.

Before receiving any units, set the destination bin — either choose from the dropdown or scan a bin barcode. The location's default receiving bin is pre-selected if one is configured. Then receive units by:

  • Scanning a unit barcode — receives that specific unit.
  • Scanning a Product barcode — receives all in-transit units for that Product at once.
  • Clicking Receive on an individual row.
  • Receive Group — receives all in-transit units for a Product/lot/expiration group.
  • Receive All — marks every remaining in-transit unit as received in one click.

Each received unit is moved to the selected destination bin, its status becomes In Stock, and a Transfer movement is written to its history. When every unit is received the transfer status automatically advances to Received.

Transfer lines and units #

A transfer has two levels of detail:

  • Lines — one per Product, recording the requested_quantity and approved_quantity. Lines define what was asked for and what was committed.
  • Transfer Units — one per individual inventory unit selected during approval, linked to a line. Each unit tracks its own status: pending, approved, rejected, in_transit, received, or missing.

Both are visible as tabs on the transfer's view page, alongside the full change history log.

Permissions and visibility #

Transfers respect per-location roles. The transfer list automatically scopes to locations the current user has a role at, and the location context filter narrows it further to transfers touching a specific location. Creating requires create_inventory_transfers at at least one location; editing requires update_inventory_transfers at the relevant source or destination location depending on the current status.

Doing it from the API #

# Create a transfer request with two lines
curl -X POST "https://your-domain.com/api/v1/inventory/transfers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from_location_id": "<source-location-id>",
    "to_location_id": "<destination-location-id>",
    "expected_delivery_date": "2026-06-15",
    "notes": "Restocking for seasonal demand",
    "lines": [
      {"product_id": "<product-id>", "requested_quantity": 10},
      {"product_id": "<another-product-id>", "requested_quantity": 5}
    ]
  }'

# Advance the transfer to "in_transit"
curl -X POST "https://your-domain.com/api/v1/inventory/transfers/{id}/transition" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "in_transit"}'

Filter the list by status, from_location_id, or to_location_id as query parameters. Valid to values for the transition endpoint are: requested, approved, in_transit, delivered, received, and cancelled.