fleet.route.* calls Nash POSTs (or GETs) on the fleet’s API.
These are synchronous HTTP requests, not async webhook fanout. Nash blocks on the response and acts on it:
200 = accept, 4xx = permanent reject (Nash tries another fleet), 5xx/timeout = retryable failure.Wire model: every dispatch is a route
A single delivery is a degenerate route of one pickup stop + one dropoff stop; a multi-order job carries N stops. Same envelope, same endpoint (/fleet/routes), same response shape — the fleet implements one schema and Nash handles the single-vs-multi collapse internally.
Integration shape
Onboarding & authentication
At onboarding, Nash provisions an organization for the fleet and records:- The API base URL Nash sends route calls to.
- The outbound auth token Nash includes on every call.
- An org API key the fleet uses to authenticate inbound calls into Nash.
Authorization: Bearer <token> header on every outbound request. The token is issued at onboarding. The fleet validates the token before processing the body.
Inbound auth (fleet → Nash). The fleet sends every inbound request with the org API key as a bearer token:
Events
| Event | Method + path | When |
|---|---|---|
fleet.route.quote | POST /fleet/routes/quote | Nash is requesting a price/ETA on a route. The fleet responds with a quote in the response body. Only sent if the fleet participates in quoting. |
fleet.route.dispatched | POST /fleet/routes | Nash committed the route to this fleet for fulfillment. The body carries the full route snapshot — stops with embedded order data and per-order deliveryId. |
fleet.route.updated | POST /fleet/routes/{routeId} | A field on an in-flight order changed, or a stop was added mid-flight. The body carries the full updated route; an optional fieldChanges[] list names the paths that changed. |
fleet.route.canceled | POST /fleet/routes/{routeId}/cancel | The merchant or Nash canceled the route (or one delivery on it). Body carries cancelReason and deliveryIds[]. |
| (refresh) | GET /fleet/routes/{routeId} | Nash polls for current state. Fleet returns per-stop updates keyed by deliveryId. |
dispatched, updated, canceled. quote is required only for fleets participating in quoting; refresh is required only when the fleet doesn’t push state inbound on its own.
Wire envelope
Every event has the same top-level shape:| Field | Meaning |
|---|---|
type | Always "fleet.route". |
event | One of quote, dispatched, updated, canceled. |
routeId | Nash’s stable handle for this route. Use it on GET /fleet/routes/{routeId}, POST /fleet/routes/{routeId}, POST /fleet/routes/{routeId}/cancel. For a single-delivery dispatch with no Nash-side route, the delivery id is used as the routeId. |
occurredAt | ISO 8601 timestamp of when the event happened in Nash. |
data | The route snapshot: { id, stops: [...] }. Update calls add fieldChanges[]; cancel calls replace stops with { deliveryIds, cancelReason }. |
Acknowledgement and retries
Nash expects200 OK. Other responses are handled as follows:
4xx— treated as a permanent rejection. Forfleet.route.dispatched, Nash will route to a different fleet.5xxor timeout — retried with exponential backoff.404on refresh — soft no-op. Fleet doesn’t know about this route (possibly pruned after completion).
fleet.route.dispatched
Sent when Nash commits the route to the fleet. The body carries the full route snapshot. Each stop has an orders[] array with full per-order snapshots embedded — pickup-side AND dropoff-side fields are included on every embedding, so the fleet has everything it needs without follow-up lookups.
Multi-order route
- Stops are flat and sequenced (0..N-1), mirroring Nash’s
Route.stops. - Each stop carries
orders[]— full per-order snapshots embedded right there. No follow-up lookups. - A stop with multiple
orders[]means a co-pickup or co-dropoff (multiple orders touched at the same location). stopTypeispickup,dropoff, or other (e.g.driver_break) — driver-break stops pass through verbatim from Nash’s optimizer.
Single delivery (degenerate route of one)
A dispatch for one merchant order ships as a route with exactly two stops. When the delivery has no Nash-sideRoute (single-order dispatch), the delivery id is used as the wire routeId:
Dispatch response
On accept:partnerStopId (per-delivery) as the fleet-side delivery identifier for cross-referencing in logs. Falls back to the Nash delivery id if the fleet doesn’t return one.
On reject:
4xx with an optional structured body. Nash treats the route as failed and tries another fleet (or alerts the merchant if no fallback).
fleet.route.updated
Sent when a field on an in-flight order changes (pickup or dropoff window, address, instructions, items), or when a stop is added mid-flight. The body is a full re-send of the route snapshot plus a fieldChanges[] list naming the paths that changed.
fleet.route.canceled
Sent when the merchant or Nash cancels the route, or one delivery on it. The body carries the route id, the affected deliveryIds[], and a cancelReason.
deliveryIds[] lists the specific deliveries being canceled. For a whole-route cancellation, it’s every delivery on the route; for a single-delivery cancellation on a multi-delivery route, only that one delivery.
After a cancellation, any further PATCH /v1/fleet/deliveries/{deliveryId} calls for that delivery are no-ops.
Refresh
Nash periodically polls the fleet for the current state of an in-flight route:PATCH /v1/fleet/deliveries/{id} body, keyed by deliveryId:
Refresh and inbound PATCH share the exact same field set. The fleet can implement one
DeliveryUpdate JSON shape and reuse it for both pushing state to Nash (inbound) and returning state to Nash (the refresh GET response).deliveryId matches the delivery being refreshed and ignores the rest — they’re siblings on the same route refreshed in their own poll. 404 is a soft no-op (fleet doesn’t know about this route).
fleet.route.quote
Quoting lets the fleet compete on price and ETA against other providers configured by the merchant. It’s opt-in at onboarding — fleets that don’t participate are still eligible to receive dispatches directly when the merchant picks them; they just don’t compete on price.
Flow
- Nash receives an order (or multi-stop batch) from the merchant and identifies the fleets eligible to fulfill it.
- For each eligible fleet that participates in quoting, Nash POSTs
POST /fleet/routes/quotewith the route snapshot. - The fleet responds with a quote within the agreed latency budget. Quotes that arrive too late or fail are skipped — they don’t block other fleets’ quotes.
- Nash filters quotes against the merchant’s constraints (max fee, max ETA) and the merchant picks a winner.
- The winning fleet receives
POST /fleet/routes(dispatch). Losing fleets receive nothing — they don’t need to track the outcome.
Quote request
Same wire envelope as the otherfleet.route.* calls; event: "quote". Same route-shaped data as dispatch, but each order’s deliveryId is null (the delivery isn’t committed yet).
Quote response
Respond with200 OK and a JSON body, or 4xx to decline:
4xx) are interpreted as ineligible for this route. Nash captures the decline reason from the response body if present ({ "reason": "out_of_service_area" }) but doesn’t retry.
Latency budget
The fleet’s quote endpoint should respond within the budget configured at onboarding (typically 1–3 seconds). Quotes that exceed the budget are dropped for that route. Consistent timeouts move the fleet to a degraded state and Nash will route around it until quote latency recovers.Rejecting a dispatch
If the fleet can’t accept the route (no capacity, ineligible), it can reject in two ways:- Reject in the response. Respond
4xxtoPOST /fleet/routes(dispatched). Nash treats the dispatch as failed and tries a different fleet. - Reject after acceptance. Ack
200, thenPATCH /v1/fleet/deliveries/{deliveryId}withstatus: failedandfailure.code: "no_capacity". Nash treats it as a runtime failure and tries a different fleet.
Order field reference
The per-order snapshot embedded under each stop’sorders[] is flat camelCase, with ISO 8601 timestamps and monetary values in minor units.
Required
| Field | Meaning |
|---|---|
id | Nash order id. Stable across modifications. |
deliveryId | Identifier the fleet uses on subsequent updates via PATCH /v1/fleet/deliveries/{deliveryId}. Present on dispatch / update / cancel; null on quote. |
externalId | Merchant’s order identifier — useful for fleet-side reconciliation. |
pickupAddress | Full address string. |
pickupBusinessName | Store / restaurant name. |
pickupPhoneNumber | E.164 — for courier-to-store contact. |
pickupStartTime / pickupEndTime | When the order will be ready (window). |
dropoffAddress | Customer address. |
dropoffFirstName | Recipient first name. |
dropoffPhoneNumber | E.164 — for SMS tracking and courier contact. |
dropoffStartTime / dropoffEndTime | Promised delivery window. |
items | List of { name, quantity, ... }. |
valueCents | Subtotal in minor units. |
currency | ISO 4217. |
deliveryMode | now or scheduled. |
Recommended
pickupInstructions/dropoffInstructions— gate codes, “leave at door,” etc.requirements— alcohol, age verification, signature, photo POD, barcode scans.weight/height/width/depth— improves vehicle fit.tipAmountCents— passed through to the courier where the fleet supports it.referenceId— short identifier shown to the courier in the fleet’s app.tags— partner-specific routing flags.
Stop-level fields
| Field | Meaning |
|---|---|
stopType | pickup, dropoff, driver_break, etc. |
sequence | 0-indexed position in the route. |
location | { lat, lng }. |
arrivalTime / endTime | Window for this stop. |
serviceTime | Seconds the courier is expected to spend at the stop. |
orders[] | Per-order snapshot — see “Order field reference” above. |