Documentation menu
These are pre-release docs from the main branch.

Routing and travel reference

System facts about the routing provider seam, its endpoints, its settings, and the reconciliation surface. For the vendor-facing wire contract see Routing providers.

Provider selection

Environment variableValuesDefault
ROUTING_PROVIDERnone, osrm, ors, mapbox, contractnone
ROUTING_OSRM_URLOSRM base URL—
ROUTING_ORS_URLOpenRouteService base URL—
ROUTING_ORS_API_KEYORS key (hosted instances)unset
MAPBOX_ACCESS_TOKENMapbox tokenunset
MAPBOX_PROFILEdriving, driving-traffic, walking, cyclingdriving
ROUTING_CONTRACT_IDPack id (adapter registers contract:<id>)pack
ROUTING_CONTRACT_URLContract base URL—
ROUTING_CONTRACT_TOKENBearer token for the packunset

A named provider with a missing required value falls back to NONE mode and logs a warning. The active posture is visible at GET /api/dispatch/routing under status: provider_id, provider_label, quality (heuristic or measured), capabilities, modes, suggestions_available, and a typed reason when degraded (no_provider_configured, provider_not_registered).

Org settings

KeyTypeDefaultMeaning
routing.heuristic_minutes_per_mileinteger 1–602Estimate pace in NONE mode
routing.heuristic_overhead_minutesinteger 0–1205Fixed minutes per estimated leg
routing.heuristic_road_factor_percentinteger 100–300125Straight-line → road distance
travel.mileage_rate_minorinteger 0–10000067Reimbursement per unit, minor units
travel.mileage_rate_unitmi | kmmiUnit the rate is quoted per
travel.reimburse_techsbooleantrueInclude mileage in technician reimbursement

Appointment travel fields

travel_minutes is travel time immediately before starts_at. It occupies dispatch capacity through occupied_minutes, and it never shrinks the booked time itself. Provenance rides alongside:

FieldMeaning
travel_fromResolved start-point snapshot (kind, label, site_id, coordinates, source)
travel_distance_metersProvider distance for the leg
travel_providernone, osrm, mapbox, or contract:<id>
travel_qualityheuristic or measured

Manual minutes carry no provider fields. On a move, only provider-stamped estimates recompute.

Endpoints

RoutePurpose
GET /api/dispatch/routing?date=Day travel plans, legs, typed warnings, suggestions
GET /api/dispatch/journey-start?user_id=&date=Resolved start point plus any explicit override
PUT /api/dispatch/journey-startSet the technician-day start point (instant envelope)
GET /api/dispatch/reconciliation?from=&to=&user_id=Mileage/time/expense rollups (max 366 days)
POST /api/dispatch/travel-drafts/{id}/expenseBook a draft's mileage as a ticket expense

Warning kinds: missing_start_point, missing_coordinates, zero_transfer, insufficient_transfer, provider_error. Suggestion kinds: swap_adjacent, reassign. Suggestions require a measured provider. In NONE mode available: false with a typed reason.

Reconciliation semantics

  • Rollups read travel drafts in the window, not time entries. A submitted draft has both.
  • Distance converts to the rate unit before pricing (1 mi = 1.609344 km).
  • reimbursement_minor is the technician total at the org rate, zero when travel.reimburse_techs is off.
  • expense_minor and billable_minor sum linked expenses in the org base currency. Expenses in other currencies count as expensed_count only.
  • One expense per draft. The endpoint is idempotent through the standard envelope. A second call without a key returns a conflict.