Order Status Workflows
The Unified Order Service (UOS) includes a comprehensive order status management system. This guide explains the available order statuses and the workflows for transitioning between them.
Order Statuses
Orders in the UOS system can have the following statuses:
| Status | Description |
|---|---|
pending | Initial state when order is created |
processing | Order is being processed |
picking | Order is being picked in the warehouse |
picked | Order has been picked and ready for delivery/collection |
retrieving | Order is ready to be retrieved (intermediate state before final fulfillment) |
shipped | Order has been shipped (Home Delivery) |
collected | Order has been collected by customer (Click & Collect) |
completed | Order has been fulfilled |
cancelled | Order has been cancelled |
failed | Order processing failed |
suspended | Order is temporarily suspended |
Status Transition Flow
The following diagram illustrates the typical flow of order statuses:
Home Delivery Flow
pending → processing → picking → picked → retrieving → shipped → completed
Click & Collect Flow
pending → processing → picking → picked → retrieving → collected → completed
Alternative/Direct Paths
picked → completed (direct completion without retrieving)
Status Exit Paths
- Most statuses →
cancelled(Order cancellation) - Most statuses →
failed(Processing failure) - Most statuses →
suspended(Temporary suspension) failed→processing(Retry after failure)completed→cancelled(Late cancellation)
Valid Status Transitions
The system enforces valid status transitions to maintain order integrity. Below are all allowed transitions from each status:
| From Status | Allowed Next Statuses |
|---|---|
pending | processing, cancelled, failed, suspended |
processing | picking, cancelled, failed, suspended |
picking | picked, cancelled, failed, suspended |
picked | retrieving, completed, cancelled, failed, suspended |
retrieving | shipped, collected, cancelled, failed, suspended |
shipped | completed, cancelled, failed, suspended |
collected | completed, cancelled, failed, suspended |
completed | cancelled |
cancelled | (no transitions allowed - terminal state) |
failed | processing (allows retry) |
suspended | pending, processing, picking, cancelled, failed |
Invalid Transitions
Attempting to transition to a status not listed in the table above will result in a 422 Unprocessable Entity error. The system validates all status transitions to prevent invalid order states.
Automatic Sequential Transitions
The UOS system includes automatic sequential transition logic that handles certain multi-step status changes transparently. When a status update requires intermediate steps to maintain valid transition rules, the system automatically executes the required transitions in sequence.
How It Works
When you request a status change that would violate the valid transition rules, the system:
- Detects the invalid direct transition
- Calculates the required intermediate steps
- Executes each transition in sequence
- Returns success with indication of the auto-transition
All intermediate status changes are recorded in the order history and trigger appropriate webhook notifications.
Supported Auto-Transitions
The following auto-transitions are currently supported:
pending → picking
Direct Request: pending → picking
Actual Transitions: pending → processing → picking
When an order in pending status is updated to picking, the system automatically transitions through processing first.
picked → shipped
Direct Request: picked → shipped
Actual Transitions: picked → retrieving → shipped
Force Transitions
For advanced use cases where external systems need to bypass status transition validation (e.g., when syncing order statuses that have already progressed through intermediate steps), UOS supports the X-Force-Transition header.
When to Use Force Transitions
Force transitions are useful when:
- External systems (like CC Platform) have already processed intermediate status steps internally
- Bulk status updates need to sync final statuses without going through each intermediate step
- Status reconciliation between external systems and UOS is required
How It Works
When you include X-Force-Transition: true in your request:
- Forward Transitions: Validation is bypassed, allowing transitions like
picking→completed(skippingpicked,retrieving,shipped) - Backward Transitions: Always rejected with 403 Forbidden, even with the FORCE header
- Loop Prevention: Webhooks back to your system are automatically skipped when
X-Command-Originis set
Example: Force Transition Request
curl -X PATCH "https://api.uos.mandeville.digital/v1/orders/order-123/status" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Force-Transition: true" \
-H "X-Command-Origin: cc-adapter" \
-H "X-Correlation-Id: corr-2025-12-14-001" \
-d '{
"status": "completed",
"metadata": {
"source": "cc_platform",
"updated_by": "naveo-enterprise-adapter",
"reason": "Order completed in CC Platform scheduler"
}
}'
Forward vs Backward Transitions
Forward Transitions (Allowed with FORCE):
- ✅
picking→completed - ✅
picked→completed - ✅
processing→completed - ✅ Any transition that moves forward in the workflow
Backward Transitions (Always Rejected):
- ❌
completed→picking(403 Forbidden) - ❌
picked→processing(403 Forbidden) - ❌ Any transition that moves backward in the workflow
Webhook Behavior
When a forced transition occurs, webhooks include:
{
"event": "order:status_changed",
"caused_by": "cc-adapter",
"correlation_id": "corr-2025-12-14-001",
"data": {
"old_status": "picking",
"new_status": "completed",
"metadata": {
"forced_transition": true,
"updated_by": "naveo-enterprise-adapter"
}
}
}
Loop Prevention: If X-Command-Origin: cc-adapter is set, webhooks to URLs containing "cc-adapter" are automatically skipped to prevent feedback loops.
Security Considerations
- Forward-Only: Only forward transitions can bypass validation
- Authentication: Existing authentication mechanisms apply
- Audit Logging: All forced transitions are logged for security monitoring
- Rate Limiting: Existing rate limits apply to forced transitions
When an order in picked status is updated to shipped, the system automatically transitions through retrieving first.
picking → retrieving (Courier Dispatch)
Direct Request: picking → retrieving
Behavior: Courier dispatch notification - metadata update + FCM notification, status stays picking
When a courier is dispatched during picking, this is handled as a notification event rather than a status change. The system updates courier metadata and sends an FCM notification to the picker app, but the order status remains picking.
Courier Dispatch Response
When a courier dispatch is received during picking, the API returns:
{
"message": "Courier information updated",
"orderId": "order-123",
"status": "picking",
"courier_dispatched": true,
"courier_info": {
"courier_status": "en_route",
"dispatched_at": "2023-11-15T14:30:00Z",
"driver_id": "DRV-123"
}
}
The order status remains picking and an FCM notification is sent to the picker app.
Webhook Notifications
For courier dispatch during picking, an order:updated webhook is sent with courier metadata:
{
"event_type": "order:updated",
"event_id": "evt-001",
"timestamp": "2023-11-15T14:30:00Z",
"data": {
"order_number": "ORD-001",
"status": "picking",
"metadata": {
"courier_info": {
"courier_status": "en_route",
"dispatched_at": "2023-11-15T14:30:00Z"
}
}
}
}
Order History
All auto-transition steps are recorded in the order history. You can view the complete transition path by querying the order's status history:
GET /v1/orders/{orderId}/status-history
Each transition includes metadata indicating whether it was an automatic transition:
{
"history": [
{
"version": 5,
"status": {
"from": "picking",
"to": "picked"
},
"metadata": {
"auto_transition": true,
"reason": "Auto-transition to enable retrieving status"
},
"timestamp": "2023-11-15T14:30:00Z"
},
{
"version": 6,
"status": {
"from": "picked",
"to": "retrieving"
},
"metadata": {
"auto_transition_final": true
},
"timestamp": "2023-11-15T14:30:01Z"
}
]
}
Use Cases
Auto-transitions are particularly useful in the following scenarios:
External System Integration: When external delivery management systems (such as logistics providers) send status updates without knowledge of all intermediate states required by UOS.
Simplified API Usage: Clients can request the final desired status without managing intermediate state transitions manually.
Workflow Consistency: Ensures all orders follow the same state transition paths regardless of the source system, maintaining data integrity and audit trails.
Important Considerations
- Auto-transitions are atomic from the caller's perspective but execute as separate database transactions internally
- Each intermediate status change generates its own webhook notification
- All transitions are recorded in the order history for complete audit trails
- Auto-transitions maintain all validation rules and do not bypass any security checks
- Metadata provided in the original request is preserved and applied to the final status
Status-Specific Metadata
When updating an order's status, certain statuses require specific metadata:
Cancelled Status
When updating to the cancelled status, you must provide a cancellation reason:
{
"status": "cancelled",
"metadata": {
"cancellation_reason": "customer_requested",
"notes": "Customer changed their mind"
}
}
Valid cancellation reasons:
customer_requestedorcustomer_request: Cancelled by customer requestcustomer_service: Cancelled by customer service (CBS)customer_no_show: Cancelled due to customer no-show (SOM)out_of_stock: Cancelled due to items being out of stockfraud_suspected: Cancelled due to suspected fraudulent activity
Picking Status
When updating to the picking status, you must provide a picker ID:
{
"status": "picking",
"metadata": {
"picker_id": "PICKER123"
}
}
Shipped Status
When updating to the shipped status, you can provide shipping information:
{
"status": "shipped",
"metadata": {
"tracking_number": "TRACK123456",
"carrier": "DHL",
"shipping_method": "standard",
"estimated_delivery": "2023-11-15T14:00:00Z",
"packages": [
{
"id": "PKG001",
"weight": "2.5kg",
"dimensions": "30x20x15cm"
}
]
}
}
Retrieving Status
When updating to the retrieving status, the order is ready to be retrieved for final fulfillment (either shipping or customer collection):
{
"status": "retrieving",
"metadata": {
"ready_at": "2023-11-15T12:00:00Z",
"retrieval_location": "Dispatch Area 3",
"prepared_by": "STAFF123"
}
}
Collected Status
When updating to the collected status, you must provide information about who collected the order:
{
"status": "collected",
"metadata": {
"collected_by": "John Smith",
"collected_at": "2023-11-15T13:45:00Z",
"id_verified": true,
"verification_method": "photo_id"
}
}
Suspended Status
When suspending an order, provide a reason:
{
"status": "suspended",
"metadata": {
"suspension_reason": "payment_verification",
"expected_resolution_date": "2023-11-16T12:00:00Z",
"notes": "Waiting for payment verification"
}
}
Status History
Every time an order's status changes, the system records:
- The previous status
- The new status
- When the change occurred
- Who or what initiated the change
- Any associated metadata
This status history is accessible through the API at the /v1/orders/{orderId}/status-history endpoint.
Updating Order Status
Using the Core API
To update an order's status using the Core API, send a PATCH request to the /v1/orders/{orderId}/status endpoint:
curl -X PATCH "https://api.uos.mandeville.digital/v1/orders/order-123/status" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "processing",
"metadata": {
// Status-specific metadata if required
}
}'
Force Transition Header (Advanced)
For systems that need to bypass status transition validation (e.g., when syncing statuses that have already progressed through intermediate steps), you can use the X-Force-Transition header:
curl -X PATCH "https://api.uos.mandeville.digital/v1/orders/order-123/status" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Force-Transition: true" \
-H "X-Command-Origin: your-system-name" \
-H "X-Correlation-Id: corr-123456" \
-d '{
"status": "completed",
"metadata": {
"source": "your_system",
"updated_by": "scheduler"
}
}'
Important Restrictions:
- ✅ Only forward transitions can bypass validation (e.g.,
picking→completed) - ❌ Backward transitions are always rejected, even with the FORCE header (403 Forbidden)
- 🔒 Include
X-Command-Originto prevent webhook loopbacks to your system
See Force Transitions section below for more details.
Response
The status update endpoint returns 202 Accepted when the status update is accepted. The response acknowledges receipt of the update request:
{
"message": "Order status update in progress",
"orderId": "order-123",
"status": "processing",
"forced_transition": false,
"metadata": {
"picker_id": "PICKER-123"
}
}
When using X-Force-Transition: true, the response includes "forced_transition": true:
{
"message": "Order status update in progress",
"orderId": "order-123",
"status": "completed",
"forced_transition": true,
"metadata": {
"status_in_uos": "processing",
"_internal_job_id": "job-abc123"
}
}
Important: The 202 Accepted response indicates the status update has been accepted and queued for processing, not that it has been applied. You should wait for the webhook notification to confirm the status has actually changed.
Webhook Notifications
Status updates are processed asynchronously. To know when a status update has been successfully applied, subscribe to the order:status_changed webhook:
{
"event_type": "order:status_changed",
"event_id": "evt-123",
"timestamp": "2023-11-15T14:30:00Z",
"data": {
"id": "order-123",
"order_number": "ORD-001",
"status": "processing",
"previous_status": "pending",
"metadata": {
"picker_id": "PICKER-123"
}
}
}
Best Practice: Always rely on webhooks for confirmation of status changes, not the API response. This ensures your system has the most accurate and up-to-date order status.
Using the Drone API
To update an order's status using the Drone API, send a GET request to the /dts/drone/order/set_status endpoint:
curl -X GET "https://api.uos.mandeville.digital/dts/drone/order/set_status?orderId=order-123&status=3" \
-H "Authorization: Bearer YOUR_API_KEY"
Picking App Scope Restrictions
The Drone API (picking app) has limited status control for system integrity. It can only set certain statuses:
Allowed Status Codes for Picking App:
2: Picking (STATE_COLLECTING) - Start or continue picking process3: Picked (STATE_COLLECTED) - Complete picking process6: Cancelled (STATE_CANCELLED) - Cancel order during picking operations
Read-Only Status Codes for Picking App:
1: Pending (STATE_OPEN) - Managed by order management systems4: Shipped (STATE_DELIVERING) - Managed by logistics/shipping systems5: Completed (STATE_DELIVERED) - Managed by delivery/collection systems
Valid Transitions for Picking App:
pending(1) →picking(2) Allowedpending(1) →cancelled(6) Allowedprocessing(1) →picking(2) Allowedprocessing(1) →cancelled(6) Allowedpicking(2) →picked(3) Allowedpicking(2) →cancelled(6) Allowedpicked(3) →cancelled(6) Allowed
Forbidden Transitions for Picking App:
- Any transition to
shipped(4) orcompleted(5) Not Allowed - Any transition from
shipped(4) orcompleted(5) Not Allowed - Direct
pending/processing→picked(must go throughpicking) Not Allowed
Error Response Example:
{
"responseType": "failure",
"status": 422,
"message": {
"error": "Picking app transition not allowed: Picking app cannot transition from 'picked' to 'shipped'",
"data": {
"current_status": "picked",
"requested_status": "shipped",
"allowed_transitions": ["cancelled"],
"error_code": "PICKING_APP_TRANSITION_NOT_ALLOWED"
}
}
}
Note that the Drone API uses numeric status codes that map to UOS string statuses with picking app scope restrictions enforced.
Special Status Operations
Marking an Order as Failed
Using the Drone API, you can mark an order as failed with an optional reason:
curl -X GET "https://api.uos.mandeville.digital/dts/drone/order/set_failed?orderId=order-123&reason=Out%20of%20stock" \
-H "Authorization: Bearer YOUR_API_KEY"
Suspending an Order
You can temporarily suspend an order using the Drone API:
curl -X GET "https://api.uos.mandeville.digital/dts/drone/order/set_suspended?orderId=order-123&reason=Customer%20request" \
-H "Authorization: Bearer YOUR_API_KEY"
To resume a suspended order:
curl -X GET "https://api.uos.mandeville.digital/dts/drone/order/unset_suspended?orderId=order-123" \
-H "Authorization: Bearer YOUR_API_KEY"
Real-Time Status Notifications
The UOS provides real-time status notifications through WebSockets. To subscribe to status changes:
- Connect to the WebSocket server at
wss://api.uos.mandeville.digital/ws - Send an authentication message with your API key
- Subscribe to the order status topic
Example WebSocket subscription:
// Example JavaScript WebSocket client
const socket = new WebSocket('wss://api.uos.mandeville.digital/ws');
socket.onopen = () => {
// Authenticate
socket.send(JSON.stringify({
type: 'auth',
apiKey: 'YOUR_API_KEY'
}));
// Subscribe to order status changes
socket.send(JSON.stringify({
type: 'subscribe',
topic: 'orders.status',
filter: {
channel_id: 'channel-123'
}
}));
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Order status changed:', data);
};
Best Practices
- Only update an order to a valid next status in the workflow
- Always provide required metadata for status changes
- Use appropriate API endpoints based on your integration context
- Monitor webhook and WebSocket notifications for status changes
- Check order status history for troubleshooting
- Handle status transitions asynchronously as they may take time to propagate through the system