Manual Edit Actions
Overview
Optimization produces a plan that is mathematically sound given the constraints it was told about. Real operations then need small, deliberate deviations from that plan — and Manual Edit Actions are how you apply them.
They exist for adjustments after automatic planning has run, not as a substitute for it. Typical reasons to reach for them:
- Fine-tuning a completed plan. Minor corrections to sequence or timing where the plan is good overall but a detail needs changing.
- Local knowledge the model does not have. A driver knows a service road, a loading bay habit, or a customer who is easier to reach at a particular time. Planners encode that judgement directly.
- Driver and planner preferences. Familiar territory, preferred stop order, or an arrangement a team has settled on for reasons that were never modelled as constraints.
- Reacting to the day as it unfolds. A breakdown, a delay, a cancellation, or an urgent order that arrives after planning is done.
The distinguishing feature is that these actions are directive. You state the change you want and the engine applies it, rescheduling arrival times around it. It does not reconsider the plan, and validation is deliberately limited so that a planner's decision is not overruled — several actions skip capacity checks entirely. That makes them precise instruments for small changes, and the wrong tool for reshaping a whole day's plan. For that, adjust your constraints and re-run optimization.
How the API is structured
Every action goes through a single endpoint, POST /api/v2/microservices/edit. The request wraps one action in a common envelope, and the action_type field inside action selects which one:
{
"simulation_id": 15,
"current_time": "2020-01-01T00:00:00+00:00",
"dry_run": true,
"action": {
"action_type": "vehicle_reorder_nodes",
"agent_id": "b45e1905-04ce-451a-ba3e-6b1142608795",
"node_uid_new_sequence": ["1cb32310-...", "228030d2-..."]
}
}
Envelope fields that apply to every action:
| Field | Description |
|---|---|
simulation_id | The simulation whose plan you are editing. Required. |
current_time | The as-at time used to decide what is already in progress and therefore protected. Defaults to now. |
dry_run | Validate and evaluate the edit without writing anything. Use this to preview an edit before committing it. |
notify_assignment | Whether assignment and unassignment changes are pushed on to the driver app. Defaults to false. |
return_full_objects | Return the changed objects in full rather than just their identifiers. Defaults to true — send false if you only want the identifiers. |
enable_fallback | Allow the action to retry less strictly when the first attempt fails. Whether a fallback exists, and what it relaxes, depends on the action. Defaults to true. |
ignore_vehicle_lock | Permit the edit to change the route of a locked vehicle. Defaults to false, which rejects such edits. |
force_use_enhanced_sort | Override the simulation's enhanced-sort setting for this one action. Leave unset to use the simulation's own setting. |
The response reports what changed rather than the new plan itself: changed_nodes_uids, changed_bookings_uids, and changed_vehicles_uids list the objects you need to re-fetch, committed tells you whether the change was written (false for a dry run), and feasible reports the outcome of the constraint checks that the specific action performs. A details object carries whatever else the action has to report — most often rejected_bookings and rejected_nodes, the identifiers the action could not place. Read it whenever you use partial_success, and on the two actions that can prune a booking that misses its window.
Most actions refuse to disturb whatever a driver is doing right now. Once a vehicle has real coordinates, the next node on its route generally cannot be reordered, reassigned, or unassigned — and when node grouping is enabled, that protection covers the whole upcoming group. Expect edits to be rejected on this basis rather than silently reshaping an active route.
Locked vehicles
A vehicle can be marked locked to keep route-changing operations away from it — a planner has settled that route and does not want it disturbed. Before any action runs, the API works out which vehicles it would affect and refuses if any of them are locked.
The check is deliberately broad. A vehicle is treated as affected if the action names it directly (through agent_id, agent_ids, vehicles, vehicle_departure_times, or the keys of vehicle_route_map), or if it merely holds one of the nodes or bookings the action names. So unassigning a booking that happens to sit on a locked vehicle's route is rejected even though the request never mentions that vehicle.
A rejected edit returns 409 Conflict with error code vehicle_route_locked, and the context object lists the offending vehicles under vehicle_ids and vehicle_agent_ids:
{
"error": "The manual action would change a locked vehicle route.",
"error_code": "vehicle_route_locked",
"context": {
"vehicle_ids": [2820211],
"vehicle_agent_ids": ["b45e1905-04ce-451a-ba3e-6b1142608795"]
}
}
Set ignore_vehicle_lock to true in the envelope to bypass the check and edit locked vehicles anyway.
The rest of this page documents each action, grouped by what it changes, including its real-world use, rules, and limitations.
Nearly every action below changes route timing, which invalidates any driver break the optimizer had planned. How each action treats existing breaks varies, and break handling is off unless your deployment enables it — while it is off, the optimizer plans no breaks either. See Driver Breaks and Manual Edits before rolling out manual editing on an operation that relies on breaks.
Routing & Sequencing Adjustments
These actions allow operators to physically restructure how and when vehicles arrive at physical stops.
Re-arrange Stops on a Route
Provides an intuitive override of a driver's ongoing trip to account for human knowledge, such as avoiding a specific congested bridge or taking a familiar detour.
- Action Name:
vehicle_reorder_nodes - Use Case: A dispatcher recognizes a poor algorithmic sequence and physically drags-and-drops the sequence of assigned stops to something more intuitive for the driver, forcing the trip order to match their expectations.
- Limitations & Rules:
- Pickup nodes must structurally remain before their corresponding Dropoff nodes.
- If a vehicle is "live" (has non-zero coordinates), the immediate next assigned node after any completed or in-service node cannot be reordered. (If node grouping is enabled, the entire group is considered the "next upcoming node" and cannot be rescheduled).
- It is possible to specify
COMPLETEDnodes in the sequence, provided they remain grouped at the beginning and do not intertwine withASSIGNEDnodes. - If
strict_time_windowsis enabled, the new manually arranged sequence must satisfy the nodes' static time window constraints, or the edit will be rejected. - Driver breaks are recalculated without their time window constraints, and vehicles in single-break mode lose their break entirely. See reduced-fidelity recalculation.
Rearrange Stops Across Multiple Vehicles
Apply a complete, caller-defined arrangement of stops across several vehicles in a single atomic action, covering moves, reorders, assignments, and unassignments at once.
- Action Name:
vehicle_complex_rearrange - Use Case: A dispatcher works out a better distribution of the afternoon's stops across the fleet — whether by dragging stops between vehicle timelines in a planning UI or from their own rules — and submits the resulting arrangement for several vehicles together, rather than issuing a separate edit per vehicle.
- Payload shape: You supply
vehicle_route_map, a map of vehicleagent_idto the vehicle's complete new node sequence. A single call can therefore rearrange stops within a vehicle, move them between vehicles, assign previously unassigned stops, and unassign stops, all at the same time.
node_attributesAlongside route, each vehicle entry accepts an optional node_attributes array — a list of { "scheduled_ts": ... } objects matched to route by index.
Sending it changes the action's behaviour fundamentally: rescheduling is suppressed entirely. Instead of recomputing arrival times from travel times, the engine writes the timestamps you supply and saves the nodes as-is. Use it when you are replaying a schedule the caller already computed; omit the array to let the route be rescheduled normally.
Each vehicle's route fully replaces its existing sequence. Any node you omit is removed from that vehicle, and if it appears in no other vehicle's route in the same call, it is unassigned and its booking is returned to NEW. Passing an empty route clears the vehicle entirely.
Always send the vehicle's whole intended sequence, not just the stops you changed.
- Limitations & Rules:
- No optimization or balancing is performed. The engine applies the arrangement you submit and reschedules arrival times; it does not evaluate whether the result is efficient or evenly distributed. Deciding the distribution is the caller's job.
- Vehicle capacity limitations are explicitly not validated during this action, so an arrangement can exceed a vehicle's capacity.
COMPLETEDnodes must not be reassigned, and orphaned pickup or dropoff nodes are prohibited.- A dropoff node cannot be reassigned to a different vehicle or placed back into the unassigned pool if its corresponding pickup node has already been completed or is actively being served.
- Within the new routes, a dropoff node must always be placed structurally after its pickup node.
- If a vehicle is "live", the next node on its route must not be reassigned or rescheduled.
strict_time_windowsdefaults totruehere, meaning the action returns an error rather than committing an arrangement that breaks time window constraints.- Every affected route is rescheduled, so driver breaks are recalculated with reduced fidelity. A node moved between vehicles is re-planned as part of its new route; only a node left out of every route has its break discarded outright.
Assignment Management
These actions govern the relationship between orders (bookings) and the vehicles executing them.
Force-Assign Specific Bookings
Eliminate algorithmic distribution for urgent ad-hoc pickups by locking unassigned work to a specific, active driver in the area.
- Action Name:
vehicle_assign_booking_list - Use Case: A planner has a pool of local deliveries that haven't been routed yet, but knows that a specific local contractor with an empty truck is actively waiting for work. Setting hard assignments ensures those particular orders are assigned exactly to that driver.
- Limitations & Rules:
- If a vehicle is "live", nodes cannot be inserted before the node that is next on the ongoing route. Similar to re-ordering, if node-grouping is active, the entire group encompasses the "next upcoming node."
- You can strictly inject new pickups or dropoffs into precise locations in the route by specifying what node to insert before/after. Alternatively, you can have the solver optimize the insertion location based on routing efficiency by omitting these structural parameters.
- Options such as
strict_capacityandstrict_time_windowscontrol whether the solver rejects the assignment if the vehicle is incapable of executing it legally. - Driver breaks are fully re-planned by the solver, honouring all break constraints. See full recalculation.
- If the simulation enables
manual_edit_td_postprocessing_no_offer_if_time_window, a booking whose refined arrival misses its window is pruned and left unassigned instead of being assigned.
Return Bookings to the Unassigned Pool
Quickly relieve an incapacitated driver of their workload so the engine can immediately re-dispatch the orders to healthy vehicles.
- Action Name:
vehicle_unassign_bookings - Use Case: A driver experiences a mid-route breakdown or gets held up unexpectedly at a delivery. The remaining stops on that route are unassigned in bulk so the optimization engine can re-distribute them to other nearby, active drivers to ensure they aren't missed.
- Limitations & Rules:
- Just like routing insertions, if a vehicle is "live", the API will prevent unassigning a booking if the immediate next required node on the driver's route belongs to that booking.
- You can issue partial success directives; meaning if ten bookings are ordered to unassign, and one fails because the driver is currently executing it, the action will successfully unassign the remaining nine rather than reverting the entire payload.
- Driver breaks are discarded from the unassigned nodes, and the breaks on the remainder of the route are recalculated with reduced fidelity.
Order Restructuring
This action changes the shape of the work itself rather than who does it or when.
Split a Booking into Smaller Ones
Break an order that is too large for any single vehicle into pieces that can be carried separately.
- Action Name:
split_booking - Use Case: A customer orders 500 units but no available vehicle has that much free capacity, so the order would go unassigned. The planner splits it into a 300 and a 200 so two vehicles can share the delivery. The same applies when a pallet count exceeds what one truck can legally carry.
- How the split works: The original booking survives as the head of a group and keeps a share of the demand; the remaining pieces become new bookings whose
group_uidis the head'suid. Two strategies decide the division:fixed_demand— you give the absolute demand each resulting booking should carry, as a list of at least two capacity-dimension maps. The first entry applies to the original booking. Rejected if the parts sum to more than the original demand.weighted_demand— you give at least two weights and the demand is divided in proportion to them.minimal_demand_threshold(default1) sets the floor; the action is rejected if any resulting piece would fall below it, or if the threshold is larger than the booking's own demand.
- Limitations & Rules:
- The booking must still be unassigned. It needs a status of
NEW,PREPARED,REJECTED_BY_SYSTEM, orFAIL_TO_BOARD, and it must have exactly one unassigned pickup node and one unassigned dropoff node. A booking already on a route cannot be split — unassign it first. - Optionally,
options.shipment_external_idsandoptions.external_idsassign your own identifiers to the resulting bookings, first item to the original. Each list must have at least as many entries as there are bookings after the split. - Because the booking is unassigned, this action does not touch any vehicle's route or timings. It does clear
break_infoon every node the split produces, so a stale break plan cannot survive on a piece of a booking that no route has planned yet.
- The booking must still be unassigned. It needs a status of
Timing Modifications
These actions manipulate the specific timeframe intervals bounding deliveries.
Adjust Customer Delivery Windows
Respond proactively to customer availability changes, tightening or widening constraints so the routing engine appropriately reprioritizes subsequent driving ETAs without breaking the route sequencing. This action is also capable of shifting vehicle departure time. The combination of shifting time windows and departure time within a single action allows to achieve the required combination of scheduled times and slacks.
- Action Name:
shift_nodes_time_windows - Use Case: A customer calls in indicating they are leaving their house early, and requests their delivery window be tightened. The dispatcher edits the time window boundaries on the individual spot. The solver subsequently recalculates all timings while trying to accommodate the strict boundaries, pushing subsequent downstream ETAs securely without ripping the delivery out of the current order sequence.
- Limitations & Rules:
- Currently, the action only supports editing of a single vehicle and its assigned nodes.
- Nodes must natively possess the
ASSIGNEDstatus and be assigned to the same vehicle. - Adjusted time windows cannot violate or shift beyond the hard time bounds of the physical simulation schedule or the vehicle's shift boundaries.
- Updated time windows are propagated to bookings (
min_pickup_timeandmax_pickup_timeare also updated). - Lateness behaviour is restricted by master simulation parameters (
logistics_api_settings.manual_edit_allow_vehicle_lateandlogistics_api_settings.manual_edit_try_strict_time_windows_request). See the Logistics API Settings documentation for more details. - Driver breaks are fully re-planned by the solver, honouring all break constraints. Because this action shifts the very time windows a break must fit between, breaks may legitimately move to a different node. See full recalculation.
- If the simulation enables
manual_edit_td_postprocessing_no_offer_if_time_window, a booking that the new windows make unreachable is pruned and unassigned rather than left on the route in violation.
Shift an Entire Route Schedule
Rectify cascaded delays globally for a vehicle to ensure subsequent ETAs portrayed to downstream customers accurately reflect real-world vehicle lag.
- Action Name:
vehicle_shift_nodes - Use Case: A driver started their route an hour later than planned because of heavy traffic arriving at the depot. Rather than displaying operations ETAs that appear an hour late for every single customer all day, the dispatcher uses this action to slide the scheduled arrival timestamp of the first node an hour forward, which sequentially updates the scheduled ETAs for every subsequent stop identically.
- Limitations & Rules:
- Nodes are structurally shifted sequentially from the first
ASSIGNEDnode (or a specific designated node inside the route tracker). Because routing engine drive-time calculations govern the transits between nodes dynamically, pushing a stop a full hour forward might alter drive times if traffic patterns differ drastically an hour later. - A manually modified timestamp cannot be placed sequentially before the chronological timestamp of its heading node.
- Only
ASSIGNEDstops can be moved. Attempting to shiftCOMPLETEDorIN_SERVICEnodes will throw an error. - Driver breaks are recalculated without their time window constraints, and vehicles in single-break mode lose their break entirely. Note that a break planned for a specific clock time (such as a scheduled lunch) will not be held in place when the route slides. See reduced-fidelity recalculation.
- If the shift affects only the final stop on the route, no rescheduling occurs at all and driver breaks are left untouched.
- Nodes are structurally shifted sequentially from the first
Cancellations & Data Purging
These actions clean up operational constraints dynamically directly in the system.
Cancel Bookings
Provide a safety valve to securely cancel workflows when real-world logistics (such as inventory shortages) prevent execution.
- Action Name:
invalidate_bookings - Use Case: A customer calls support to completely cancel their order, or the warehouse reports that the order's items are out of stock and cannot be fulfilled today. This performs a soft-deletion of the bookings.
- Limitations & Rules:
- The booking state is flipped natively to
NEWand flagged asis_invalidated. - If the booking was currently attached to an active route, it is ripped out immediately alongside its attached nodes, and the remainder of the vehicle's route is computationally rescheduled in place. No constraints are validated.
- Driver breaks are discarded from the cancelled booking's nodes. Because breaks are attached to nodes rather than bookings, cancelling a booking can remove a break that was serving the whole route.
- The booking state is flipped natively to
Mark Vehicles Unavailable
Ensure operations aren't routed to inactive vehicles while maintaining safe historical logs of the vehicle's existence.
- Action Name:
invalidate_vehicles - Use Case: A vehicle broke down before their shift started, or a contractor called in sick, rendering the unit permanently unusable for the day. This performs a soft-deletion of the vehicle.
- Limitations & Rules:
- By default, this action unassigns all related nodes. Enabling
remove_point_nodeswill ensure all assigned nodes strictly of thePOINTtype are permanently deleted from the database alongside the invalidation. - A vehicle cannot be invalidated if it has already embarked and possesses live lat/long coordinates, or if it has already partially completed a segment of an assigned booking.
- Driver breaks are discarded from every node released by the vehicle. Breaks are re-planned only when those nodes are picked up again by an optimization run or a solver-backed manual edit.
- By default, this action unassigns all related nodes. Enabling
Hard-Delete Vehicles
Wipe faulty testing data or corrupted setup states cleanly before running production configurations.
- Action Name:
delete_vehicles - Use Case: A planner accidentally clicked 'Create' twice or imported a faulty setup payload containing phantom vehicles that must be wiped entirely from the database before routes are calculated.
- Limitations & Rules:
- Similar to validations, you strictly cannot delete a vehicle that has assigned nodes possessing statuses indicating they are currently active:
ASSIGNEDorIN_SERVICE. - Driver breaks are discarded from any node the deleted vehicle was holding.
- Similar to validations, you strictly cannot delete a vehicle that has assigned nodes possessing statuses indicating they are currently active:
Pruning bookings that miss their window
vehicle_assign_booking_list and shift_nodes_time_windows both hand the route to the solver and then, when tdroute_postprocessing_enabled is on, re-time it against time-dependent travel times. That second pass can discover delay the solver never priced in — waiting time behind a truck ban, most commonly — and push a node's arrival past its close_time_ts.
What happens next depends on logistics_api_settings.manual_edit_td_postprocessing_no_offer_if_time_window:
false(default) — the booking stays where the edit put it, with an arrival time that violates its window. The edit succeeds.true— the booking is removed from the route instead. Both its pickup and its dropoff go, the leg the removal opens up is re-measured, and the re-timing runs again over what remains — so pruning one booking can reveal, and prune, another.
Only these two actions act on the violation. Other actions re-time their routes through the same pass, but a booking that misses its window there stays on the route regardless of the setting.
A pruned booking ends up unassigned, whatever state it was in when the action started:
| Field | Value after pruning |
|---|---|
assigned_vehicle | null |
status | NEW |
scheduled_ts | null |
break_info | null |
The booking and node identifiers are reported in the response under details.rejected_bookings and details.rejected_nodes, and the nodes also appear in changed_nodes_uids.
This is the one way an otherwise successful edit leaves a booking worse off than before. vehicle_assign_booking_list reverts the assignment it just made, so the booking you asked to assign comes back unassigned. shift_nodes_time_windows unassigns a booking that was already on the vehicle before the edit — it had no rejection concept at all before this behaviour existed. Neither case is an error response: check details.rejected_bookings rather than assuming a 2xx means every booking stayed on the route.
The optimization pipeline has the same behaviour under its own flag, postprocessing_no_offer_if_time_window, where pruned bookings join the run's rejected bookings with the reason tdroute_postprocessing_time_window_violation and go through normal no-offer handling.
Driver Breaks and Manual Edits
Driver breaks planned by the optimizer are stored per node, under the break_info.driver_break_list property of the Node object. Almost every manual edit action changes route timing, so a stored break schedule may no longer be valid after an edit. What happens to it depends on which action you use — and on one simulation-level setting.
Driver break handling during manual edits is gated by logistics_api_settings.logistics_break_info_handling_enabled, which defaults to false.
While it is false, manual edits do not maintain driver breaks at all: no break parameters are sent to the solver, and break_info is cleared from every node the edit modifies. A route that had a compliant break plan can silently end up with none. Set it to true if your operation depends on planned breaks surviving manual intervention.
Despite the name, this setting is not scoped to manual edits: the optimization pipeline reads the same flag. While it is off, the optimizer sends no dynamic-break parameters for any vehicle and plans no breaks at all, so there is no break plan for an edit to lose. Turning it on is what makes breaks exist in the first place — and what makes the per-action behaviour below meaningful.
Behaviour per action
Assuming logistics_break_info_handling_enabled is true:
| Action | Effect on planned driver breaks |
|---|---|
vehicle_assign_booking_list | Fully re-planned by the solver. |
shift_nodes_time_windows | Fully re-planned by the solver. |
vehicle_reorder_nodes | Recalculated, reduced fidelity. Break time windows are ignored; single-break vehicles lose the break. |
vehicle_shift_nodes | Recalculated, reduced fidelity. Same caveats. Untouched if only the last stop shifts. |
vehicle_complex_rearrange | Recalculated, reduced fidelity on every affected route. Discarded only on nodes left out of every route. |
vehicle_unassign_bookings | Discarded on unassigned nodes; the remaining route is recalculated at reduced fidelity. |
invalidate_bookings | Discarded on the cancelled booking's nodes; the remainder of the route is recalculated at reduced fidelity. |
invalidate_vehicles | Discarded on every node the vehicle releases. Not re-planned. |
delete_vehicles | Discarded on every node the vehicle was holding. Not re-planned. |
split_booking | Cleared on every resulting node. The booking is unassigned by definition, so there is no route timing to preserve. |
The "discard" and "cleared" outcomes above are not gated by the setting — those nodes lose break_info whether or not break handling is enabled, because the node is leaving the route its break was planned against. The same applies to a node pruned for missing its window, and to the lone remaining node of an offline vehicle's route, which cannot be rescheduled at all and so has its stale break data cleared rather than kept.
Full recalculation (assignment and time window edits)
vehicle_assign_booking_list and shift_nodes_time_windows route the affected vehicle through the solver. All six vehicle break parameters are supplied, so the resulting break plan respects the same constraints as a normal optimization run:
dynamic_break_durationdynamic_break_min_path_durationdynamic_break_avg_time_between_breaksdynamic_break_max_latencydynamic_break_start_timedynamic_break_end_time
Exact break start timestamps are then read back from the solver result and written to break_info. Every break in the returned list carries the vehicle's dynamic_break_duration, so per-break durations are always uniform.
Reduced-fidelity recalculation
vehicle_reorder_nodes, vehicle_shift_nodes, vehicle_complex_rearrange, and the surviving remainder of a route after vehicle_unassign_bookings or invalidate_bookings all take this path. It does not invoke the routing solver: the schedule of the existing sequence is recomputed directly from travel times, then breaks are re-derived using the break post-processing pass on that new schedule. Because the vehicle handed to that pass carries only two break fields, only two parameters are honoured:
dynamic_break_durationdynamic_break_avg_time_between_breaks
dynamic_break_min_path_duration, dynamic_break_max_latency, dynamic_break_start_time and dynamic_break_end_time are not applied. An edited route can therefore end up with a break placed outside the window the vehicle is configured to allow, or with a break on a route too short to have warranted one.
This recalculation requires both dynamic_break_duration and dynamic_break_avg_time_between_breaks to be set. A vehicle configured for single break mode leaves dynamic_break_avg_time_between_breaks unset by definition — that is what selects single-break mode — so no break can be re-derived, and the node's existing break_info is cleared instead.
In practice: reordering, shifting, or rearranging the route of a single-break vehicle removes its planned break, and the route will show no break until a full optimization run is performed. Vehicles in multiple breaks mode are unaffected.
Recommendations
- Enable
logistics_break_info_handling_enabledon any project where breaks are a legal or contractual requirement rather than a preference. - Prefer
vehicle_assign_booking_listandshift_nodes_time_windowswhen break compliance matters, since only those two re-plan breaks against the full constraint set. - Treat break schedules as advisory after a reorder, shift, rearrange, or cancellation, and re-run optimization to restore a fully constrained break plan.
- Read breaks back from
break_info.driver_break_listafter every edit rather than assuming the pre-edit plan still holds.