Simple Optimization Workflow with Integration API (Asynchronous)
This guide explains how to use the automated workflow to execute a complete optimization cycle with Intergration API that emulates Statless API. To support long-running optimizations (e.g., > 1 hour), the workflow is split into three actions: Start, Status, and Results.
Why use Integration API as Stateless API?
While the Stateless API offers maximum flexibility, using the Integration API to emulate a stateless workflow provides significant advantages in terms of integration speed and feature access. This approach leverages the robustness of the stateful system while maintaining the simplicity of a "fire-and-forget" optimization request.
Reduced Integration Complexity via Templates
As detailed in the Simulation Templates guide, the Integration API allows you to pre-configure complex optimization settings (cost functions, constraints, vehicle profiles) once as a "template".
- Save Time: You don't need to construct and validate a massive JSON configuration object for every request.
- Maintenance: Operational changes (e.g., adjusting service times or costs) can be made in the template without changing your integration code.
- Automatic Adjustments: The system automatically handles time window shifting for recurring operations.
Access to Superset of Features
The Integration API supports a wider range of capabilities than the raw Stateless API, as outlined in the Introduction:
- Advanced Pipelines: Access to multi-stage optimization pipelines (e.g., wave planning, multi-DC) that are managed by the system.
- Visualization & Debugging: Because the data is temporarily stored as a "Simulation", you can use SWAT's dashboard tools to visualize routes, debug unassigned orders, and analyze performance—capabilities not available with the purely ephemeral Stateless API.
- Persistent Objects: Option to mix ephemeral data (daily orders) with persistent data (fleet master data), reducing payload size.
Simplified Asynchronous Workflow
For long-running optimizations (> 1 hour), the Integration API's native job management (Processors) provides a robust way to track status and retrieve results, which is wrapped by this workflow to feel like a simple async call.
Workflow Overview
The client initiates the optimization, then polls for status, and finally retrieves the results.
Process Flow
API Usage
All API requests must include an API token in the header.
Key: x-api-key
Value: <your_api_token>
Start Action
Initiates the workflow by uploading data and starting the optimization.
For detailed documentation on the underlying APIs, refer to:
- To populate
ordersrefer to Orders Upload. The format of the object is matching withbookingsobject - To populate
vehiclesrefer to Bulk Vehicle Upload. The format of the object is matching withpayload.vehiclesobject forVehicle bulk uploadoperation. - To populate optional Optimization Settings to override default template optimization settings. The format of the object is matching with
optimization_settingsobject
Each order and vehicle must have a unique UID.
The body may contain the following optional parameters:
simulation_template: Specifies a custom simulation template to use, overriding the default auto-selected one.date_of_service: Defines a specific date for the optimization to begin. By default, the optimization's start time is the earliest known timestamp found among the provided vehicles and orders. This timestamp is important for bounding vehicle working hours, particularly in scenarios where the earliest possible time is otherwise unknown for orders and vehicles.
Each optimization run will create a new simulation that will be retained in SWAT system unless it is cleaned up with delete request. SWAT currently doesn't support having multiple simulations with the same start time created from the same template, the data submited may end up in the prevsiouly created simulation. While the results may still be produce, this can break data isolation and result in unexpected outcomes.
Endpoint: POST https://windmill.d.gcprod.swatrider.com/api/r/start_optimization
Request Body:
{
"project": 839,
"orders":
[
{
"uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"demand": {
"fruit": 1
},
"max_pickup_time": "2031-01-01T23:00:00+07:00",
"min_pickup_time": "2031-01-01T00:00:00+07:00",
"max_dropoff_time": "2031-01-01T23:00:00+07:00",
"min_dropoff_time": "2031-01-01T00:00:00+07:00",
"pickup_location_lat": 1.3238751960077857,
"pickup_location_lon": 103.99996384867768,
"pickup_service_time": 0,
"dropoff_location_lat": 1.3258751960077857,
"dropoff_location_lon": 103.79996384867766,
"dropoff_service_time": 300,
"pickup_location_name": "Hub",
"dropoff_location_name": "Grocery shop"
},
{
"uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde64",
"demand": {
"carton": 1
},
"max_pickup_time": "2031-01-01T23:00:00+07:00",
"min_pickup_time": "2031-01-01T00:00:00+07:00",
"max_dropoff_time": "2031-01-01T23:00:00+07:00",
"min_dropoff_time": "2031-01-01T00:00:00+07:00",
"pickup_location_lat": 1.3228751960077858,
"pickup_location_lon": 103.89996384867766,
"pickup_service_time": 0,
"dropoff_location_lat": 1.3238751960077857,
"dropoff_location_lon": 103.78996384867766,
"dropoff_service_time": 300,
"pickup_location_name": "Hub",
"dropoff_location_name": "Grocery shop"
}
],
"vehicles": [
{
"lat": 0,
"lon": 0,
"speed": 0,
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e96978",
"capacity": {
"fruit": 10
},
"routing_engine_settings": {
"curb": true,
"speed": null,
"profile": "van",
"time_factor": 1,
"road_network": "van",
"make_depot_zero": true,
"intermediate_curb": true
}
}
],
"optimization_setting": [],
"inventory": [],
// "simulation_template": 123, - Optional
// "date_of_service": "2031-01-01T17:00:00.000Z" - Optional
}
Response:
{
"status": "started",
"processor_id": "12345",
"simulation_id": "67890"
}
Status Action
Checks the progress of the optimization using the processor_id returned from the Start Action.
Optimization time can be configured in the simulation template and can vary from seconds to hours for a very large optimization problem.
Endpoint: POST https://windmill.d.gcprod.swatrider.com/api/r/optimization_status
Request Body:
{
"processor_id": "12345"
}
Response:
{
"start_time": "2025-09-09T22:00:00",
"end_time": "2025-09-10T06:00:00",
"current_status": "completed",
"id": 12345
}
Results Action
Retrieves the final results once the status is "completed". This returns the assigned nodes and detailed route information for each vehicle, assigned orders and travel legs. rejected_bookings will contain a list of orders that can not be assigned to vehicles.
For detailed documentation on the Node object structure, refer to:
include_navigation is forcing the system to return turn by turn navigation for travel which can be very verbose. If you do not require it, omit this parameter, or set it to false. Travel durations and polylines will still be included, but no turn by turn navigation will be provided.
Endpoint: POST https://windmill.d.gcprod.swatrider.com/api/r/optimization_results
Request Body:
{
"simulation_id": 67890,
"include_navigation": false
}
Path in the route sequence is returned in polyline5 format. Refer to Google official documentation. JSON string with the polyline must be unescaped first to render correct results.
Response:
Details
{
"vehicles": {
"2b1e3b65-2c04-4fa2-a2d7-467901e96978": [
{
"id": 16269806,
"lat": 1.3238751960077857,
"lon": 103.99996384867767,
"uid": "705654c4-f51f-4416-99f4-b8e6d85669ee",
"data": {
"product_kind": null
},
"slack": 0,
"demand": {
"fruit": 1
},
"status": "assigned",
"node_type": "pickup",
"break_info": null,
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"display_name": "Hub",
"geofence_ids": [],
"open_time_ts": "2030-12-31T17:00:00+00:00",
"scheduled_ts": "2030-12-31T17:00:00+00:00",
"service_time": 0,
"time_windows": null,
"close_time_ts": "2031-01-01T16:00:00+00:00",
"location_name": "Hub",
"vehicle_labels": null,
"vehicle_characteristics": {}
},
{
"summary": "Tanah Merah Coast Road, Pan-Island Expressway",
"distance": 30381.7,
"duration": 2454.6,
"polyline": "ydaGsjwyRbR`GtNrE|Bt@hF~A??xCiJDQ?OEOIMSKkDgAwLyDYKUK{Aw@k@[]Oe@Qo@SiGmB}JaDwGuBuIqCiJwCECCCAI@EDW??`AZhF`BRFfBj@TFpGrBzGvBvBr@dGlBhf@pOjA^LBf@N?@H@??XLEJw@vBQh@y@fCITY~@g@bBa@lAELoAzDy@bCeAdDuEhNu@bCSp@Wv@M\\]hAGPGROb@[bASx@G\\W~AKj@Qz@e@|AoBbGm@nB]~@i@~A{AzESf@S`@gDpG_@r@Yf@Ud@EJiB~CYd@W^WVSRWTa@N_@L_@Dc@@_@@_@E]Gi@OoFeBo@SWIsGoB]Gi@Ca@Bk@Fo@Tk@^[\\U`@Sf@EPEXAZ?d@Hb@b@jAf@~@h@t@V\\zCdDNPNRb@j@VVJNTXbBnBr@dAXd@T`@\\~@Ld@NlAJlA?dBGl@Kn@Ot@]dA??Sd@c@r@kBpCaDpEg@v@mA~AW\\u@hAW^eAxAm@~@m@dAkAzBGJCPShBCZCNEJYp@Uh@w@`Bu@hBWdAGVo@xD_BbJUdA[|AADCLCNuArG}@bESbAU~@i@fB_@`AUb@k@j@EF??S^KNUZUVURSPYTi@d@u@l@qC|B[T[VsAhAGDWPGHoAfAy@p@gEjDsEtDiA|@u@j@SNSNCDe@\\??QZWb@A@Yn@Mp@E~@?fAIhCI`@O^SZg@b@gAx@IPANFTfAtAPp@??v@bAxLdOjElFzApBvApBPXJXL\\J`@Jh@Fb@Df@@XBl@?`AErCIzA_@hNM~EKzDKpDKtDCpB?p@@vADhCHlBPhBNxAJ|@L|@Jp@Lv@TfAj@jCJ^Nl@Ph@Pf@hDpJVr@nDtJXt@lEvLZx@vA|D|AhErBtFzAzD|AdE\\t@`AvBfCvFv@~A^dAVz@Pn@Jl@l@~CPx@nCzMt@vDv@zF\\fCNlAL~@t@zGfArJpA`Ll@dFPdBhAtI|CtS\\jEHxAJ|ADhD?\\?D@F?\\@`AJpBBVBXNv@rAxEbAdDh@jBv@fClAxDj@pBNn@FZDTBVFl@Hn@HtAC`CCnCCf@Ip@Mr@CJIVO\\g@lAMVMTOXa@j@MNg@n@s@l@{HlFMLOLeIvFoCfBs@d@gBrAk@j@URe@d@UV_@d@QPi@t@oAdBkApBw@fBs@vBk@`Cq@bDEVGT_@hBkAzDADa@bAKTQ^MZEJWt@Ur@Qx@Id@I~@CNAbA?hAHv@Hj@Lh@H^T|@FVx@xCNh@DV`@dBx@tDXxAJr@Dt@?pAAVCt@C|@_@dLIpCC\\AV]hJQtDIfCAXIjBGzDAlCD|A@`@JzE@t@@XFhDFzBHbBLpANr@Pp@`@rAb@pAf@jArBfE~@hBp@`Bh@bBT|@Jd@|CjNDRR|@b@pBf@hCHh@Db@Bp@Ar@Gp@OlAUj@iBfDk@~BMdB??LZ@LBLHn@Lj@P`@N\\P\\^`@jAr@\\Pd@R\\LRLxB~@d@Pt@VNDb@RhAf@NFD@^PzAj@lAZ`@HD@NDb@J`@JLDv@Rh@PVBd@DR?b@Bf@???Mf@CNOj@_AbEOp@o@lCa@t@IN??g@dCCJWtAGVAFSnAEPGf@o@nDAFALCNCLG`@YrAI`A[~EIrAA\\C`@??YdGSjCShDIhAAN?@KzAC^QbCE~@CXGd@Ql@[r@a@v@_AxAYf@GHEHSZ??GJS\\OV{@tAUd@]j@]h@eAlBQZcC|Da@n@c@x@]n@g@p@INMPKReAfBuDpGYb@a@`AWx@i@tBOl@??PJlAj@n@Xf@XBBXPJFLHf@\\z@j@b@Xb@XRRPRR^R`@NZ@BDLXdADZ??D?F?rC?J?HCFIDiA??QGECCC?C????",
"node_type": "travel",
"navigation": [
{
"mode": "driving",
"name": "",
"distance": 889,
"duration": 151,
"maneuver": {
"type": "depart",
"location": [
103.999303,
1.321889
],
"modifier": "straight",
"bearing_after": 203,
"bearing_before": 0
}
},
{
"mode": "driving",
"name": "",
"distance": 1843.8,
"duration": 303.2,
"maneuver": {
"type": "end of road",
"location": [
103.996204,
1.314521
],
"modifier": "left",
"bearing_after": 112,
"bearing_before": 201
}
},
{
"mode": "driving",
"name": "",
"distance": 1678.6,
"duration": 265.6,
"maneuver": {
"type": "end of road",
"location": [
104.004168,
1.326635
],
"modifier": "right",
"bearing_after": 202,
"bearing_before": 106
}
},
{
"mode": "driving",
"name": "Tanah Merah Coast Road",
"distance": 4163.6,
"duration": 334,
"maneuver": {
"type": "end of road",
"location": [
103.998316,
1.312724
],
"modifier": "right",
"bearing_after": 203,
"bearing_before": 201
}
},
{
"mode": "driving",
"name": "Xilin Avenue",
"distance": 2042.6,
"duration": 154.3,
"maneuver": {
"type": "new name",
"location": [
103.971207,
1.324501
],
"modifier": "straight",
"bearing_after": 298,
"bearing_before": 291
}
},
{
"mode": "driving",
"name": "Simei Avenue",
"distance": 986.2,
"duration": 126.3,
"maneuver": {
"type": "new name",
"location": [
103.954868,
1.332301
],
"modifier": "straight",
"bearing_after": 300,
"bearing_before": 312
}
},
{
"mode": "driving",
"name": "",
"distance": 521.2,
"duration": 41.6,
"maneuver": {
"type": "on ramp",
"location": [
103.948997,
1.338926
],
"modifier": "slight left",
"bearing_after": 302,
"bearing_before": 317
}
},
{
"mode": "driving",
"name": "Pan-Island Expressway",
"distance": 13931.6,
"duration": 689.8,
"maneuver": {
"type": "merge",
"location": [
103.944907,
1.339788
],
"modifier": "slight right",
"bearing_after": 229,
"bearing_before": 250
}
},
{
"mode": "driving",
"name": "Whitley Road",
"distance": 876.2,
"duration": 93.6,
"maneuver": {
"type": "turn",
"location": [
103.829372,
1.326781
],
"modifier": "slight left",
"bearing_after": 243,
"bearing_before": 277
}
},
{
"mode": "driving",
"name": "Bukit Timah Road",
"distance": 332.2,
"duration": 28.1,
"maneuver": {
"type": "turn",
"location": [
103.82554,
1.32037
],
"modifier": "right",
"bearing_after": 288,
"bearing_before": 182
}
},
{
"mode": "driving",
"name": "Bukit Timah Road",
"distance": 676.2,
"duration": 57.6,
"maneuver": {
"type": "merge",
"location": [
103.822749,
1.321403
],
"modifier": "slight right",
"bearing_after": 285,
"bearing_before": 300
}
},
{
"mode": "driving",
"name": "Bukit Timah Underpass",
"distance": 805.9,
"duration": 44.6,
"maneuver": {
"type": "new name",
"location": [
103.816825,
1.322703
],
"modifier": "straight",
"bearing_after": 275,
"bearing_before": 275
}
},
{
"mode": "driving",
"name": "Bukit Timah Road",
"distance": 1028.5,
"duration": 62,
"maneuver": {
"type": "new name",
"location": [
103.809915,
1.324373
],
"modifier": "straight",
"bearing_after": 300,
"bearing_before": 303
}
},
{
"mode": "driving",
"name": "Namly Avenue",
"distance": 435.4,
"duration": 58.2,
"maneuver": {
"type": "turn",
"location": [
103.802095,
1.329249
],
"modifier": "left",
"bearing_after": 208,
"bearing_before": 288
}
},
{
"mode": "driving",
"name": "",
"distance": 151,
"duration": 39.5,
"maneuver": {
"type": "turn",
"location": [
103.799477,
1.326515
],
"modifier": "left",
"bearing_after": 177,
"bearing_before": 254
}
},
{
"mode": "driving",
"name": "",
"distance": 19.8,
"duration": 5.2,
"maneuver": {
"type": "turn",
"location": [
103.799923,
1.325534
],
"modifier": "left",
"bearing_after": 23,
"bearing_before": 94
}
},
{
"mode": "driving",
"name": "",
"distance": 0,
"duration": 0,
"maneuver": {
"type": "arrive",
"location": [
103.800022,
1.325673
],
"modifier": "left",
"bearing_after": 0,
"bearing_before": 73
}
}
],
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"to_node_uid": "f283bf96-36fc-4799-aab7-3d0db6e21386",
"from_node_uid": "705654c4-f51f-4416-99f4-b8e6d85669ee",
"to_booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"from_booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63"
},
{
"id": 16269807,
"lat": 1.3258751960077857,
"lon": 103.79996384867766,
"uid": "f283bf96-36fc-4799-aab7-3d0db6e21386",
"data": {
"product_kind": null
},
"slack": 0,
"demand": {
"fruit": -1
},
"status": "assigned",
"node_type": "dropoff",
"break_info": null,
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"display_name": "Grocery shop",
"geofence_ids": [],
"open_time_ts": "2030-12-31T17:00:00+00:00",
"scheduled_ts": "2030-12-31T17:47:44.400000+00:00",
"service_time": 300,
"time_windows": null,
"close_time_ts": "2031-01-01T16:00:00+00:00",
"location_name": "Grocery shop",
"vehicle_labels": null,
"vehicle_characteristics": {}
}
],
},
"rejected_bookings": [
{
"uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde64",
"pickup_location_lat": 1.3228751960077858,
"pickup_location_lon": 103.89996384867766,
"dropoff_location_lat": 1.3238751960077857,
"dropoff_location_lon": 103.78996384867766,
"pickup_location_name": "Hub",
"dropoff_location_name": "Grocery shop"
}
]
}
Cleanup Action
Optionally, delete the simulation and associated data after results are retrieved.
Endpoint: POST https://windmill.d.gcprod.swatrider.com/api/r/cleanup
Request Body:
{
"simulation_id": "67890"
}
Response:
HTTP OK