Warehouse Transfer Guide
Overview
Warehouse Transfers enable seamless movement of inventory between warehouse locations. The system ensures accurate stock tracking by creating atomic transactions that decrease stock at the source warehouse and increase it at the destination, with complete audit trail preservation through immutable stock movements.
Transfers support business scenarios like warehouse restocking, seasonal redistribution, consolidation, emergency fulfillment, and optimizing inventory across multiple locations.
Table of Contents
- Core Concepts
- Transfer Workflow
- Creating a Transfer
- Transfer Validation
- Stock Movement Records
- Transfer Examples
- Business Rules
- Best Practices
- API Endpoints
Core Concepts
What is a Warehouse Transfer?
A warehouse transfer moves inventory from one warehouse location to another:
Source Warehouse (From) → Destination Warehouse (To)
Main DC: 100 units West Coast: 50 units
Transfer: 25 units
After Transfer:
Main DC: 75 units West Coast: 75 unitsKey Properties:
- Atomic Operation: Both source decrease and destination increase happen in a single database transaction
- Immutable Audit Trail: Creates two stock movement records (transfer_out, transfer_in)
- Immediate Effect: No "in-transit" status - transfer completes instantly
- Reversible: Can be corrected with compensating transfer
Transfer Components
A transfer consists of:
- Product: What is being transferred
- Source Warehouse: Where stock is coming from
- Destination Warehouse: Where stock is going to
- Quantity: How many units to transfer
- Reason: Why the transfer is happening
- User: Who initiated the transfer
Atomic Transaction
Transfers execute in a database transaction, ensuring that both the source decrease and destination increase happen together as a single unit of work.
How it Works:
- Transaction begins
- Decrease stock at source warehouse
- Increase stock at destination warehouse
- If both operations succeed, transaction commits
- If either operation fails, transaction rolls back (nothing changes)
Benefits:
- Data Integrity: Never lose stock in the system
- Consistency: Source decrease always matches destination increase
- Audit Trail: Complete traceability for compliance
Transfer Workflow
Simple Transfer Process
graph LR
A[Initiate Transfer] --> B{Validate}
B -->|Valid| C[Start Transaction]
B -->|Invalid| Z[Error]
C --> D[Decrease Source Stock]
D --> E[Create transfer_out Movement]
E --> F[Increase Destination Stock]
F --> G[Create transfer_in Movement]
G --> H[Commit Transaction]
H --> I[Transfer Complete]Steps:
Validation:
- Product exists
- Source and destination warehouses are different
- Source has sufficient available stock
- Quantity is positive
Transaction Start:
- Database transaction begins
- Ensures atomicity
Source Update:
- Decrease stock at source warehouse
- Create
transfer_outmovement record - Validate stock doesn't go negative
Destination Update:
- Increase stock at destination warehouse
- Create
transfer_inmovement record - Link to source movement via reason
Transaction Commit:
- All changes saved atomically
- Return updated inventory records
Transfer States
Unlike purchase orders or sales orders, transfers are immediate:
- No "Pending" State: Transfer executes immediately
- No "In-Transit" State: Stock instantly available at destination
- No "Approval" Required: Executes on request (permissions enforced via middleware)
Why Immediate?
- Simplicity: No additional state management needed
- Real-time Accuracy: Inventory levels always current
- Operational Speed: No delays waiting for approval/transit
- Reversible: Easy to correct with reverse transfer
For physical transfers that take time (trucks, shipping), consider:
- Recording transfer when shipment arrives (not when it leaves)
- Using notes field to track shipping details
- Creating custom "Transfer Order" model if you need in-transit tracking
Creating a Transfer
Transfer Stock Between Warehouses
Execute a warehouse transfer:
Endpoint: POST /api/v1/operations/inventory/transfer
Request:
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"product_id": 42,
"from_warehouse_id": 1,
"to_warehouse_id": 2,
"quantity": 25,
"reason": "Restocking West Coast warehouse for holiday season",
"reference_number": "TRF-2025-001",
"notes": "Rush transfer - ship via express courier"
}'Request Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
product_id | integer | ✅ Yes | Product to transfer |
from_warehouse_id | integer | No | Source warehouse (null = default) |
to_warehouse_id | integer | No | Destination warehouse (null = default) |
quantity | integer | ✅ Yes | Units to transfer (must be positive) |
reason | string | No | Transfer reason (max 500 chars) |
reference_number | string | No | External reference number |
notes | string | No | Additional details |
Response (200 OK):
{
"message": "Stock transferred successfully",
"data": {
"from": {
"id": 123,
"product_id": 42,
"warehouse_id": 1,
"quantity_on_hand": 75,
"quantity_reserved": 10,
"quantity_available": 65,
"warehouse": {
"id": 1,
"name": "Main Distribution Center",
"code": "MDC"
},
"product": {
"id": 42,
"name": "Laptop - Dell XPS 13",
"sku": "DELL-XPS13-001"
},
"last_movement_at": "2025-01-16T10:30:00.000000Z"
},
"to": {
"id": 124,
"product_id": 42,
"warehouse_id": 2,
"quantity_on_hand": 75,
"quantity_reserved": 5,
"quantity_available": 70,
"warehouse": {
"id": 2,
"name": "West Coast Distribution",
"code": "WCD"
},
"product": {
"id": 42,
"name": "Laptop - Dell XPS 13",
"sku": "DELL-XPS13-001"
},
"last_movement_at": "2025-01-16T10:30:00.000000Z"
}
}
}Response Fields:
from: Updated inventory at source warehouse (after decrease)to: Updated inventory at destination warehouse (after increase)
Default Warehouse Transfers
Transfer from/to the default warehouse using null:
From default warehouse to specific warehouse:
{
"product_id": 42,
"from_warehouse_id": null,
"to_warehouse_id": 2,
"quantity": 25,
"reason": "Stock new West Coast location"
}From specific warehouse to default warehouse:
{
"product_id": 42,
"from_warehouse_id": 2,
"to_warehouse_id": null,
"quantity": 25,
"reason": "Consolidate to main warehouse"
}Cannot transfer within default warehouse:
{
"product_id": 42,
"from_warehouse_id": null,
"to_warehouse_id": null,
"quantity": 25
}
// ❌ Error: Cannot transfer stock within the same warehouseTransfer Validation
Validation Rules
The system enforces these validation rules:
1. Product Validation
{
"product_id": 99999
}
// ❌ Error: Selected product does not existProduct must exist in the system.
2. Warehouse Validation
{
"from_warehouse_id": 99999
}
// ❌ Error: Source warehouse does not exist
{
"to_warehouse_id": 99999
}
// ❌ Error: Destination warehouse does not existBoth warehouses must exist (if specified).
3. Different Warehouses
{
"from_warehouse_id": 1,
"to_warehouse_id": 1,
"quantity": 25
}
// ❌ Error: Source and destination warehouses must be differentCannot transfer within the same warehouse.
{
"from_warehouse_id": null,
"to_warehouse_id": null,
"quantity": 25
}
// ❌ Error: Cannot transfer stock within the same warehouse.
// Please specify different warehouses.Both null values represent the default warehouse.
4. Quantity Validation
{
"quantity": 0
}
// ❌ Error: Quantity must be at least 1
{
"quantity": -5
}
// ❌ Error: Quantity must be at least 1
{
"quantity": "abc"
}
// ❌ Error: Quantity must be a whole numberQuantity must be a positive integer.
5. Sufficient Stock
{
"product_id": 42,
"from_warehouse_id": 1,
"quantity": 500
}
// Source warehouse only has 100 available units
// ❌ Error: Stock cannot be negative.
// Current: 100, Adjustment: -500Source warehouse must have enough available stock (not reserved).
Error Responses
Validation Error (422 Unprocessable Entity):
{
"message": "The given data was invalid.",
"errors": {
"product_id": [
"Selected product does not exist"
],
"quantity": [
"Quantity must be at least 1"
],
"to_warehouse_id": [
"Source and destination warehouses must be different"
]
}
}Insufficient Stock (500 Internal Server Error):
{
"message": "Stock cannot be negative. Current: 100, Adjustment: -500"
}Not Found (404 Not Found):
{
"message": "Inventory not found for product 42 in warehouse 1"
}Stock Movement Records
Transfer Creates Two Movements
Each transfer creates two immutable stock movement records:
1. Transfer Out (Source Warehouse)
{
"id": 890,
"product_id": 42,
"warehouse_id": 1,
"movement_type": {
"value": "transfer_out",
"label": "Transfer Out"
},
"is_inbound": false,
"is_outbound": true,
"quantity": -25,
"quantity_before": 100,
"quantity_after": 75,
"warehouse": {
"id": 1,
"name": "Main Distribution Center",
"code": "MDC"
},
"reason": "Transfer to warehouse 2",
"reference_type": null,
"reference_id": null,
"created_by": 5,
"creator": {
"id": 5,
"name": "Warehouse Manager",
"email": "manager@example.com"
},
"created_at": "2025-01-16T10:30:00.000000Z"
}2. Transfer In (Destination Warehouse)
{
"id": 891,
"product_id": 42,
"warehouse_id": 2,
"movement_type": {
"value": "transfer_in",
"label": "Transfer In"
},
"is_inbound": true,
"is_outbound": false,
"quantity": 25,
"quantity_before": 50,
"quantity_after": 75,
"warehouse": {
"id": 2,
"name": "West Coast Distribution",
"code": "WCD"
},
"reason": "Transfer from warehouse 1",
"reference_type": null,
"reference_id": null,
"created_by": 5,
"creator": {
"id": 5,
"name": "Warehouse Manager",
"email": "manager@example.com"
},
"created_at": "2025-01-16T10:30:00.000000Z"
}Key Points:
- Both movements have same
created_attimestamp - Both movements created by same user
- Quantities are inverse: -25 (out) and +25 (in)
- Reason automatically includes warehouse context
- Movements are permanently linked via product and timestamp
Viewing Transfer Movements
Get all transfer movements for a product:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/product/42/history?limit=100" \
-H "Authorization: Bearer {token}"Filter by transfer type:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?movement_type=transfer_out" \
-H "Authorization: Bearer {token}"
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?movement_type=transfer_in" \
-H "Authorization: Bearer {token}"Get warehouse transfer activity:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/warehouse-activity?warehouse_id=1&days=30" \
-H "Authorization: Bearer {token}"Transfer Examples
Example 1: Seasonal Restocking
Scenario: Transfer laptops from main warehouse to West Coast for holiday rush
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"product_id": 42,
"from_warehouse_id": 1,
"to_warehouse_id": 2,
"quantity": 50,
"reason": "Holiday season demand - West Coast",
"reference_number": "HOLIDAY-2025-WC",
"notes": "Expected surge in online orders from California"
}'Effect:
- Main DC: 100 → 50 units
- West Coast: 30 → 80 units
- Two movement records created
- Inventory immediately available for West Coast orders
Example 2: Emergency Fulfillment
Scenario: Transfer stock to fulfill urgent order in different location
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"product_id": 15,
"from_warehouse_id": 3,
"to_warehouse_id": 2,
"quantity": 10,
"reason": "Emergency transfer for SO-12345",
"reference_number": "URGENT-SO-12345",
"notes": "Customer needs by EOD - ship overnight"
}'Example 3: Warehouse Consolidation
Scenario: Close seasonal warehouse, move all stock back to main
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"product_id": 42,
"from_warehouse_id": 4,
"to_warehouse_id": 1,
"quantity": 125,
"reason": "Seasonal warehouse closure - consolidating inventory",
"reference_number": "CONSOLIDATE-2025-Q1",
"notes": "Summer popup location closing"
}'Example 4: Damage Replacement
Scenario: Transfer stock to replace damaged units at another location
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"product_id": 8,
"from_warehouse_id": 1,
"to_warehouse_id": 2,
"quantity": 5,
"reason": "Replacement for damaged units at WC warehouse",
"reference_number": "DMG-REPLACE-001",
"notes": "Flood damage at West Coast - replacing units"
}'Example 5: Balanced Distribution
Scenario: Rebalance inventory across warehouses for optimal service levels
# Transfer 1: Main DC → East Coast
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"product_id": 42,
"from_warehouse_id": 1,
"to_warehouse_id": 3,
"quantity": 30,
"reason": "Monthly rebalancing - optimizing service levels",
"reference_number": "REBAL-2025-01-EC"
}'
# Transfer 2: Main DC → West Coast
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"product_id": 42,
"from_warehouse_id": 1,
"to_warehouse_id": 2,
"quantity": 30,
"reason": "Monthly rebalancing - optimizing service levels",
"reference_number": "REBAL-2025-01-WC"
}'Business Rules
Rule 1: Atomic Execution
Transfers are all-or-nothing:
Success Scenario (Both operations succeed):
Transfer: Main DC (100 → 75) ✓
Transfer: West Coast (50 → 75) ✓
Result: Transfer completeFailure Scenario (Either operation fails):
Transfer: Main DC (100 → 75) ✓
Transfer: West Coast ERROR ✗
Result: Transaction rolled back, Main DC still has 100 unitsRule 2: Stock Cannot Go Negative
Source warehouse must have sufficient available stock:
Warehouse Stock: 100 units
Reserved: 25 units
Available: 75 units
✅ Can transfer: 75 units or less
❌ Cannot transfer: 76+ unitsReserved stock cannot be transferred.
Rule 3: Immediate Effect
Stock is immediately available at destination:
Before Transfer:
- Source: 100 units available
- Destination: 50 units available
After Transfer (25 units):
- Source: 75 units available ✅ Instant
- Destination: 75 units available ✅ InstantNo "in-transit" or "pending" state.
Rule 4: Immutable Audit Trail
Transfer movements cannot be modified or deleted. The system enforces immutability to maintain a complete audit trail.
Restrictions:
- Cannot update movement records
- Cannot delete movement records
- All movements are permanent
Solution - Reverse Transfer:
If you need to undo a transfer, create a compensating transfer in the opposite direction:
POST /inventory/transfer{
"from_warehouse_id": 2,
"to_warehouse_id": 1,
"quantity": 25,
"reason": "Reverse transfer - original was incorrect"
}Rule 5: Warehouse Validation
Source and destination must be different:
✅ Valid:
- From: Warehouse 1, To: Warehouse 2
- From: null (default), To: Warehouse 2
- From: Warehouse 1, To: null (default)
❌ Invalid:
- From: Warehouse 1, To: Warehouse 1
- From: null, To: nullBest Practices
1. Use Descriptive Reasons
Good:
{
"reason": "Holiday season restocking - projected 30% demand increase in West region"
}Bad:
{
"reason": "transfer"
}2. Include Reference Numbers
Link transfers to business processes:
{
"reference_number": "REBAL-2025-Q1-001",
"notes": "Part of quarterly rebalancing initiative"
}3. Verify Stock Before Transfer
Check available stock before initiating:
# Get inventory levels
GET /inventory/show?product_id=42
# Verify source has enough available stock
# Then initiate transfer
POST /inventory/transfer4. Document Physical Transfers
Use notes for shipping details:
{
"notes": "Shipped via FedEx Ground - Tracking: 1Z999AA10123456784 - ETA: 2025-01-20"
}5. Monitor Transfer Activity
Regularly review transfer movements:
# Weekly transfer audit
GET /stock-movements?movement_type=transfer_out&from=2025-01-01&to=2025-01-07
# Warehouse-specific transfers
GET /stock-movements/warehouse-activity?warehouse_id=1&days=306. Plan Bulk Transfers
For multiple products, execute sequentially with error handling:
const products = [42, 43, 44, 45];
const results = [];
for (const productId of products) {
try {
const response = await transferStock({
product_id: productId,
from_warehouse_id: 1,
to_warehouse_id: 2,
quantity: 25,
reason: "Bulk seasonal transfer"
});
results.push({ productId, status: 'success', data: response });
} catch (error) {
results.push({ productId, status: 'failed', error: error.message });
}
}
console.log('Transfer Results:', results);7. Reverse Transfers for Corrections
If you need to undo a transfer, create a compensating transfer in the opposite direction:
Original transfer:
POST /inventory/transfer{
"from_warehouse_id": 1,
"to_warehouse_id": 2,
"quantity": 25,
"reason": "Seasonal restock"
}Reverse transfer:
POST /inventory/transfer{
"from_warehouse_id": 2,
"to_warehouse_id": 1,
"quantity": 25,
"reason": "Reverse transfer - original was incorrect quantity"
}8. Coordinate with Physical Movement
Option A: Record After Arrival
Ship physical goods → Arrive at destination → Record transferOption B: Track Separately
Record transfer immediately → Track shipment in notes fieldChoose based on your business needs.
API Endpoints
Transfer Operations
| Method | Endpoint | Description |
|---|---|---|
| POST | /inventory/transfer | Transfer stock between warehouses |
Related Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /inventory/show?product_id={id} | View inventory across warehouses |
| GET | /stock-movements | View transfer movements |
| GET | /stock-movements/product/{productId}/history | Product transfer history |
| GET | /stock-movements/warehouse-activity | Warehouse transfer activity |
Base URL: https://api.crm.test/api/v1/operations/
Related Guides
- Inventory Management - Multi-warehouse inventory tracking
- Stock Movement Tracking - Audit trail and history
- Physical Counts - Inventory reconciliation
- Warehouse Management - Warehouse configuration
- Product Management - Product catalog
Next Steps
- Execute Transfer - Try transferring stock between warehouses
- View Movements - Check the created transfer_out and transfer_in records
- Monitor Activity - Review warehouse transfer activity
- Plan Rebalancing - Optimize inventory distribution across locations
- Setup Automation - Consider automated transfers based on demand patterns