Overview
When you integrate with Luminous via the External V1 API, creating a fulfillment order (FO) from a posted sales order and pushing it to your warehouse or OMS involves four main steps:
- Confirm the sales order is posted
- Trigger fulfillment order generation
- Poll until the fulfillment order appears
- Push the fulfillment order to its destination channel
All External V1 requests require an Authorization: Bearer <token> header. Your tenant's base URL is the host configured for your account; all paths below are relative to /external/api/v1.
Step 1 — Confirm the sales order is posted
Fulfillment orders can only be generated for posted sales orders.
GET /sales-orders/{salesOrderId}
In the response, verify that "is_posted": true. If it is false, the order is still in a draft or purgatory state and cannot generate a fulfillment order yet. Once posting is confirmed, proceed to the next step.
Step 2 — Trigger fulfillment order generation
Luminous creates fulfillment orders automatically through a background job. To kick this off via the API, use the refresh endpoint:
POST /sales-orders/{salesOrderId}/refresh
This dispatches an asynchronous job that recalculates order quantities, removes any unpushed fulfillment orders, and auto-generates new ones based on your warehouse priority lists and Smart Fulfillment settings.
Optional request body (JSON):
| Field | Type | Default | When to use |
|---|---|---|---|
reset_skip_inventory | boolean | false | Set to true if the order previously had inventory skipped and you want new FOs to be created |
delete_all_shipments | boolean | false | Set to true to clear existing shipments before refresh |
Successful response (200):
{
"data": {
"success": true,
"message": "Order refresh job dispatched. Processing runs asynchronously on the order-refresh queue.",
"sales_order_id": 123,
"order_number": "ORD-123"
}
}
Note: The job runs in the background. A
200response means the job was queued, not that fulfillment orders have already been created. Proceed to Step 3 to wait for the results.
Why a fulfillment order might not be generated:
- No fulfillment priority list is configured for the product
- Insufficient stock and your Smart Fulfillment setting Always Create is turned off
- The order's
skip_inventoryflag is set (usereset_skip_inventory: trueto clear it)
Step 3 — Poll for the fulfillment order
Because generation is asynchronous, poll the fulfillment orders list endpoint until results appear:
GET /fulfillment-orders?sales_order_id[eq]={salesOrderId}
Repeat this request every few seconds until the response data array contains at least one fulfillment order. Each fulfillment order object includes:
| Field | Meaning |
|---|---|
id | Fulfillment order ID (needed for Step 4) |
order_status | Current status (e.g., Pending, Awaiting_Shipment) |
warehouse_group_id | Which warehouse group was assigned |
destination_oms_id | ID of the destination OMS/fulfillment channel |
pushed_at | null until the FO is pushed |
source_reference_identifier | Remote OMS order ID once pushed |
If the array remains empty after a reasonable wait (typically 30–60 seconds), re-check the reasons listed at the end of Step 2.
Step 4 — Push the fulfillment order to its destination
Once the fulfillment order exists, send it to the assigned warehouse or OMS:
POST /fulfillment-orders/{fulfillmentOrderId}/push
No request body is required. You may optionally send { "debug": true } to include outbound HTTP traffic details in the response (useful for troubleshooting).
Successful response (200):
{
"data": {
"success": true,
"message": "Fulfillment order pushed successfully."
}
}
After a successful push, pushed_at will be set on the fulfillment order and source_reference_identifier will contain the remote system's order ID.
If the push fails (422): The response body contains a links.reconcile URL:
{
"data": {
"success": false,
"message": "...",
"links": {
"reconcile": "https://{your-host}/external/api/v1/fulfillment-orders/123/reconcile-source-reference"
}
}
}
A push failure often means the destination OMS already has an order for this FO (a duplicate). Use the reconcile endpoint to recover:
POST /fulfillment-orders/{fulfillmentOrderId}/reconcile-source-reference
Send an empty body {} to let Luminous automatically look up and bind the matching remote order ID, clearing the error state. Once reconciled, the FO is treated as successfully pushed.
Optional Step — Record a carrier shipment
When the warehouse has shipped the order and you have a tracking number, record the carrier shipment against the fulfillment order:
POST /fulfillment-orders/{fulfillmentOrderId}/shipments
Minimum request body:
{
"ship_date": "2026-07-27",
"tracking_number": "1Z999AA1234567890",
"carrier_code": "ups",
"ship_to": {
"name": "Jane Smith",
"street1": "123 Main St",
"city": "Austin",
"state": "TX",
"postalCode": "78701",
"country": "US",
"residential": true
},
"weight": { "value": 16, "units": "ounces" },
"dimensions": { "units": "inches", "length": 12, "width": 8, "height": 6 },
"items": [
{
"product_id": 1,
"order_item_id": 10,
"weight": { "value": 16, "units": "ounces" },
"quantity": 1,
"unit_price": 29.99
}
]
}
This call is idempotent: if a shipment with the same tracking number and carrier already exists for this sales order, it is returned as-is and linked to the FO rather than duplicated.
Summary: full request sequence
| # | Method & Path | Purpose |
|---|---|---|
| 1 | GET /sales-orders/{id} | Confirm is_posted: true |
| 2 | POST /sales-orders/{id}/refresh | Queue fulfillment order generation |
| 3 | GET /fulfillment-orders?sales_order_id[eq]={id} | Poll until FO appears |
| 4 | POST /fulfillment-orders/{foId}/push | Push FO to warehouse/OMS |
| 4a | POST /fulfillment-orders/{foId}/reconcile-source-reference | Recover from a duplicate-push error |
| 5 (optional) | POST /fulfillment-orders/{foId}/shipments | Record carrier tracking once shipped |
Auto-push alternative
If your fulfillment channel is configured with an Auto-Push delay (set in your channel's integration mapping), Luminous will automatically push fulfillment orders after the configured number of minutes without requiring Step 4. You can still call POST /fulfillment-orders/{foId}/push manually at any time before that delay elapses if you need to push sooner.