---
key: understand-route-road-calculations
title: Understand route road calculations
audience: developer
section: api/getting-started
routes: []
permissions: ["schedule.view"]
since: 0.0.1
updated: 0.0.1
status: published
summary: Interpret road times, missing calculations and spending limits without treating unknown driving as zero.
keywords: [routing, Google Routes, traffic, drive time, geometry, spending]
related: [api-reference, configure-daily-route-arrangements]
---

Read route driving through the Schedule API. The operation reference and required grants are in [[api-reference]]. Route settings are described in [[configure-daily-route-arrangements]].

## Read driving results

For a technician, request exactly one local working day. Stops, endpoints,
commitments, warnings, road legs and totals describe one fingerprint:

```http
GET /v1/schedule/daily-routes/technicians/sres_572986262cb340c2/days/2026-10-03
X-Organization-ID: 1
```

A leg can have this shape when road access is refused:

```json
{
  "from_uid": "sbook_c01a920b35764987",
  "to_uid": "sbook_218e031e78c64426",
  "departure": "2026-10-03T14:00:00Z",
  "duration_seconds": null,
  "distance_meters": null,
  "quality": "unavailable",
  "reason": "provider_access_denied",
  "encoded_polyline": null
}
```

`route:start` and `route:finish` identify endpoints within this snapshot, not
addressable records. A recurrence occurrence uses `recur:<parent-uid>:<epoch>`.
The date is interpreted in the resource's scheduling timezone, with overnight
shifts and daylight-saving boundaries. Never connect unrelated days.

Null means unknown. Do not substitute zero for duration, distance or a total.
`unreachable` with `no_road_route` remains unreachable. Road time and road
geometry may be available independently. A missing physical visit stays visible
and blocks a complete assessment; a remote commitment still consumes time.
The route ignores calendar item-type/search filters and includes all authorized
commitments. Restricted work produces an incomplete-day warning, without
revealing its customer or location.

## Explicit visit flexibility

Read or replace `/v1/schedule/daily-routes/visits/{booking_uid}/constraints`.
A replacement needs `expected_version` and `expected_plan_version` from the
visit, and the `schedule.edit` permission. It changes only typed constraints,
not appointment times. Exact appointments are fixed unless `movable_time` is
explicitly enabled. `keep_time`, in-progress/completed work and archived
customers keep their times. `movable_date` requires explicit `allowed_dates`.
Customer availability and existing arrival windows both apply; `time_confirmed`
does not imply customer agreement. An explicit duration has
`duration_source: "explicit"`. Resource and dependency references are opaque
uids. Cross-type references answer 404; stale edits answer 409.

## Calculation quality

- `road`: static driving from Google Routes.
- `predicted`: traffic-aware driving using that leg's actual departure instant.
- `approximate`: explicitly requested straight-line distance with a road allowance and assumed speed. This is not a verified road journey.
- `unavailable`: calculation could not be obtained. The reason distinguishes configuration, provider access, temporary errors, calculation limits and spending outcomes.
- `unreachable`: no drivable route was returned.

Predicted driving needs a timezone-aware future departure. Historic traffic is unavailable; Nova never silently changes a past departure to now. Static calculations omit departure time. Coordinates must be finite and within geographic bounds, including valid zero coordinates. If the provider uses a static fallback for requested traffic, quality is `road` with `provider_static_fallback`; it is not presented as predicted traffic.

When displaying road results outside a Google map, keep Google Maps attribution next to those results. Follow [the provider attribution policy](https://developers.google.com/maps/documentation/routes/policies).

## Provider setup and spending

The server uses its configured Maps or geocoding credential with the Google Routes API enabled. Credential presence alone does not prove access. A Routes access rejection reports `provider_access_denied`; check the enabled API, billing and key restrictions without exposing the key to a client.

The existing Maps spending governor reserves the full requested matrix rectangle in element units, including any diagonal cells billed in that request. Selected road geometry reserves one request unit. All share the address-lookup daily budget and provider switch. Hold, defer and park outcomes remain unavailable; they do not become failed customer appointments or automatic retries.

One calculation is bounded to 4,000 units and 120 provider requests. Consecutive drives buy adjacent pairs instead of an all-stops square matrix. Exact point, traffic mode and departure matches deduplicate only within that request. Nova no longer stores road results in the obsolete global 30-day travel cache. Render an encoded road polyline when supplied; do not present a straight connecting segment as road directions.


## Review daily improvements

`POST /api/schedule/route-proposals/improve` takes one opaque `resource_uid`,
`working_day`, and optional `exclude_visit_uids`. An excluded visit stays in the
assessment at its current time. The result names every exact before/after time,
assignments, retained customer commitments, expiry and nullable road comparison.
It says `no_changes` when no shorter feasible schedule was found.

```json
{"resource_uid":"sres_a1b2c3d4e5f6a1b2","working_day":"2026-10-06","exclude_visit_uids":[]}
```

A successful preview carries `uid`, `status: "preview"`, `version: 1`,
`expires_at`, `changes`, and request-only `comparisons`. Preview does not move
appointments or send customer updates. The stored proposal contains business
versions and appointment changes, without Google road matrices or geometry.
`GET /api/schedule/route-proposals/{proposal_uid}` reads those exact changes
without buying or replaying cached road information.

`POST /api/schedule/route-proposals/{proposal_uid}/apply` accepts:

```json
{"expected_version":1,"accept_exact_changes":true,"communication_acknowledged":false}
```

Application rechecks every affected daily snapshot, permissions, canonical work
and booking versions and fresh road feasibility. It returns `status: "applied"`
and an advanced `version`. New bookings, changed availability/settings/locations,
expiry and concurrent changes return a conflict requiring a new review. The
existing scheduling writers save all accepted changes in one transaction;
realtime updates and the existing customer-update process run after commit.
Customer-committed changes require explicit review of that communication process.

`POST /api/schedule/route-proposals/{proposal_uid}/undo` uses the same request
shape with the applied version. Undo requires every affected day's post-apply
snapshot to remain unchanged; it never overwrites someone else's later edit.
The review and undo windows are ten minutes. These operations require the
existing scheduling edit permission and preserve work-branch and archived
customer policies. Daily improvements hold non-work-order commitments fixed. Recurring visits stay
fixed unless their opaque synthetic occurrence UID is explicitly included in
`allow_occurrence_uids`. Selected work uses an aware `occurrence_start` alongside
the parent work-order UID. Apply creates or updates only that child occurrence;
other occurrences and the series root remain unchanged.


## Place selected work orders

`POST /api/schedule/route-proposals/insert` compares one work order across
explicit person/day scopes. `POST /api/schedule/route-proposals/selected` plans
only the selected work orders across those separate days. Both require `schedule.edit` and `schedule.assign` and accept:

```json
{"scopes":[{"resource_uid":"sres_a1b2c3d4e5f6a1b2","working_day":"2026-10-06"}],"work":[{"work_order_uid":"work_order_a1b2c3d4e5f6a1b2","constraints":{}}],"max_added_drive_seconds":1800}
```

Insertion returns `options`, each with an expiring proposal UID and the exact
person, start and finish. Batch returns one reviewed proposal plus `unplaced`
reasons. Existing unselected appointments stay fixed. A duration comes from the
work order or an explicitly labelled override; a service offering is optional.
Required people, skills, territory and equipment are checked before an option is
recommended. The additional-driving limit hides placements above that limit;
unknown driving never means zero. Apply and undo use the same reviewed operations
above. A selected work or location change also makes a proposal stale.

`POST /api/schedule/daily-routes/summaries` accepts a `scopes` list and returns
at most 31 complete person/day summaries. Its entire request shares one governed
provider budget and requests no geometry. `GET /api/schedule/daily-routes/technicians`
returns authorized technicians, including an `own` flag; the matching
`/technicians/{resource_uid}/days/{day}/revision` reads a business fingerprint
without buying road estimates.

The service-booking form uses `POST /api/schedule/daily-routes/booking-candidates`
with `offering_uid`, `location_uid`, `range_start` and `range_end`. It assesses the
booking engine's actual opaque assignments and exact duration against the same
daily rules. A drive-ranked save carries `route_review` with `resource_uid`,
`working_day` and `fingerprint`; the booking writer preserves that assignment and
rechecks the complete day before creating the appointment. Limits are 25 people,
31 dates, 100 candidate appointments and 120 governed road requests per preview.
Unsupported travelling assignments and missing road results remain unavailable.
The former `/travel/suggest-slots` operation and its range-based calculation have
been removed.


## Plan larger selections in the background

`POST /api/schedule/route-proposals/jobs` takes the selected-work request above
and returns HTTP202 with an opaque `rjob_...` UID. Poll
`GET /api/schedule/route-proposals/jobs/{job_uid}` for `queued`, `running`,
`succeeded`, `failed`, `cancelled` or `expired`. A successful job supplies a
`proposal_uid`, unplaced reasons and whether the calculation hit a limit.
It creates a reviewable proposal, never appointments. The worker rebuilds current
permissions and branch access within the original principal's ceiling.

Use `POST /api/schedule/route-proposals/jobs/{job_uid}/cancel` when the selection
or scope changes. Cancellation stops subsequent candidate/provider work. Jobs
expire after ten minutes and have no automatic retry. The maximums are 60 selected
work orders, 25 people, 31 dates, 100 person/day routes, 2,000 evaluated placements
and twenty seconds of candidate search. Reaching a bound is reported explicitly.

`POST /api/schedule/route-proposals/{proposal_uid}/review` obtains fresh,
request-only road comparisons for the completed proposal. Reading job status or
reading a stored proposal does not buy road estimates. Review and apply each
recheck current inputs; an old job result cannot override changed appointments.
The browser uses background planning for more than five work orders or twenty
person/day scopes. API callers can choose background planning directly.

## Refresh and offline display

Committed booking, assignment, visit, availability, location and configuration
changes publish a privacy-safe Schedule event through the existing Redis
transport, including changes made by another backend worker. Read the lightweight
revision operation to recover after disconnection without repurchasing unchanged
routes. Superseded person/day requests should be cancelled; all query caches need
organization, branch, scope, technician and working day.

Offline itineraries must be marked stale and retain only Nova business data.
Do not store provider legs, totals, expected finish or geometry in the itinerary
cache. Access refusals must never revive an offline copy. The web and Android
clients retain these business-only copies for at most 24 hours.
