Skip to main content
When a fleet is integrated with Nash, the runtime contract has two halves: Nash makes synchronous HTTP calls to the fleet for everything that happens to a route, and the fleet reports state back through the Update Delivery endpoint. This page covers the outbound half — the 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.
Outbound auth (Nash → fleet). Nash sends a bearer token in the 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:
Authorization: Bearer <org_api_key>
Updates only land on deliveries owned by the fleet’s organization. Anything else returns not-found — Nash never leaks existence across organization boundaries.

Events

EventMethod + pathWhen
fleet.route.quotePOST /fleet/routes/quoteNash 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.dispatchedPOST /fleet/routesNash 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.updatedPOST /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.canceledPOST /fleet/routes/{routeId}/cancelThe 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.
The minimum set required to participate is 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:
FieldMeaning
typeAlways "fleet.route".
eventOne of quote, dispatched, updated, canceled.
routeIdNash’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.
occurredAtISO 8601 timestamp of when the event happened in Nash.
dataThe route snapshot: { id, stops: [...] }. Update calls add fieldChanges[]; cancel calls replace stops with { deliveryIds, cancelReason }.

Acknowledgement and retries

Nash expects 200 OK. Other responses are handled as follows:
  • 4xx — treated as a permanent rejection. For fleet.route.dispatched, Nash will route to a different fleet.
  • 5xx or timeout — retried with exponential backoff.
  • 404 on 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

{
  "type": "fleet.route",
  "event": "dispatched",
  "routeId": "rte_abc123",
  "occurredAt": "2026-05-09T14:00:00Z",
  "data": {
    "id": "rte_abc123",
    "externalId": "merchant-route-7",
    "stops": [
      {
        "stopType": "pickup",
        "sequence": 0,
        "location": { "lat": 37.7749, "lng": -122.4194 },
        "arrivalTime": "2026-05-09T15:00:00Z",
        "endTime": "2026-05-09T15:15:00Z",
        "serviceTime": 300,
        "orders": [
          {
            "id": "ord_1",
            "deliveryId": "dlv_1",
            "externalId": "merchant-order-42",
            "referenceId": "ACM-42",
            "deliveryMode": "scheduled",

            "pickupAddress": "123 Main St, San Francisco, CA 94102, US",
            "pickupBusinessName": "Acme Bakery",
            "pickupFirstName": "Manager",
            "pickupPhoneNumber": "+14155551212",
            "pickupInstructions": "Use the side door",
            "pickupStartTime": "2026-05-09T15:00:00Z",
            "pickupEndTime": "2026-05-09T15:15:00Z",
            "pickupBarcodes": ["ACME-PKG-001"],

            "dropoffAddress": "456 Market St, San Francisco, CA 94103, US",
            "dropoffFirstName": "Jane",
            "dropoffLastName": "Customer",
            "dropoffPhoneNumber": "+14155553434",
            "dropoffInstructions": "Leave at door",
            "dropoffStartTime": "2026-05-09T15:45:00Z",
            "dropoffEndTime": "2026-05-09T16:15:00Z",

            "items": [
              { "name": "Sourdough Loaf", "quantity": 2 },
              { "name": "Croissant", "quantity": 6 }
            ],
            "valueCents": 4200,
            "tipAmountCents": 500,
            "currency": "USD",
            "weight": 3.2,
            "requirements": ["age_verification_on_delivery"],
            "tags": ["catering"]
          }
        ]
      },
      {
        "stopType": "dropoff",
        "sequence": 1,
        "location": { "lat": 37.7895, "lng": -122.4001 },
        "arrivalTime": "2026-05-09T15:45:00Z",
        "endTime": "2026-05-09T16:15:00Z",
        "orders": [
          { "id": "ord_1", "deliveryId": "dlv_1", "...": "same per-order snapshot as above" }
        ]
      }
    ]
  }
}
Key shape decisions:
  • 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).
  • stopType is pickup, 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-side Route (single-order dispatch), the delivery id is used as the wire routeId:
{
  "type": "fleet.route",
  "event": "dispatched",
  "routeId": "dlv_zyx987",
  "occurredAt": "2026-05-09T14:00:00Z",
  "data": {
    "id": "dlv_zyx987",
    "stops": [
      {
        "stopType": "pickup",
        "sequence": 0,
        "orders": [{ "id": "ord_1", "deliveryId": "dlv_zyx987", "...": "..." }]
      },
      {
        "stopType": "dropoff",
        "sequence": 1,
        "orders": [{ "id": "ord_1", "deliveryId": "dlv_zyx987", "...": "..." }]
      }
    ]
  }
}
The fleet doesn’t have to special-case single vs. batch — both are routes.

Dispatch response

On accept:
{
  "partnerRouteId": "fp-route-99",
  "stops": [
    { "sequence": 0, "deliveryId": "dlv_1", "partnerStopId": "fp-s-1" },
    { "sequence": 1, "deliveryId": "dlv_1", "partnerStopId": "fp-s-2" }
  ]
}
Nash stores 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:
{ "error": { "code": "no_capacity", "message": "Out of vehicles in this zone" } }
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.
{
  "type": "fleet.route",
  "event": "updated",
  "routeId": "rte_abc123",
  "occurredAt": "2026-05-09T14:30:00Z",
  "data": {
    "id": "rte_abc123",
    "fieldChanges": ["dropoffStartTime", "dropoffEndTime"],
    "stops": [ "...full stops array..." ]
  }
}
Sending the full updated route on every change means the fleet can diff against what they had — no need to track add / remove deltas themselves.

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.
{
  "type": "fleet.route",
  "event": "canceled",
  "routeId": "rte_abc123",
  "occurredAt": "2026-05-09T14:45:00Z",
  "data": {
    "id": "rte_abc123",
    "deliveryIds": ["dlv_1"],
    "cancelReason": "canceled_by_merchant"
  }
}
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:
GET /fleet/routes/{routeId}
Fleet returns per-stop state in the shape of the inbound PATCH /v1/fleet/deliveries/{id} body, keyed by deliveryId:
{
  "stops": [
    {
      "deliveryId": "dlv_1",
      "status": "pickup_complete",
      "coordinates": { "latitude": 37.78, "longitude": -122.41 },
      "courier": { "name": "Sam", "phoneNumber": "+14155550000" }
    },
    {
      "deliveryId": "dlv_2",
      "status": "pickup_enroute"
    }
  ]
}
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).
Nash applies the entry whose 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

  1. Nash receives an order (or multi-stop batch) from the merchant and identifies the fleets eligible to fulfill it.
  2. For each eligible fleet that participates in quoting, Nash POSTs POST /fleet/routes/quote with the route snapshot.
  3. 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.
  4. Nash filters quotes against the merchant’s constraints (max fee, max ETA) and the merchant picks a winner.
  5. 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 other fleet.route.* calls; event: "quote". Same route-shaped data as dispatch, but each order’s deliveryId is null (the delivery isn’t committed yet).
{
  "type": "fleet.route",
  "event": "quote",
  "routeId": "ord_abc123",
  "occurredAt": "2026-05-09T14:00:00Z",
  "data": {
    "stops": [
      {
        "stopType": "pickup",
        "sequence": 0,
        "location": { "lat": 37.7749, "lng": -122.4194 },
        "orders": [{
          "id": "ord_abc123",
          "deliveryId": null,
          "pickupBusinessName": "Acme Bakery",
          "pickupPhoneNumber": "+14155551212",
          "pickupStartTime": "2026-05-08T15:00:00Z",
          "pickupEndTime": "2026-05-08T15:15:00Z",
          "...": "..."
        }]
      },
      {
        "stopType": "dropoff",
        "sequence": 1,
        "location": { "lat": 37.7895, "lng": -122.4001 },
        "orders": [{
          "id": "ord_abc123",
          "deliveryId": null,
          "dropoffFirstName": "Jane",
          "dropoffPhoneNumber": "+14155553434",
          "dropoffStartTime": "2026-05-08T15:45:00Z",
          "dropoffEndTime": "2026-05-08T16:15:00Z",
          "...": "..."
        }]
      }
    ]
  }
}
For batch quoting, the route has N orders with sequenced pickup stops followed by sequenced dropoff stops.

Quote response

Respond with 200 OK and a JSON body, or 4xx to decline:
{
  "externalQuoteId": "<fleet-quote-id>",
  "priceCents": 1500,
  "currency": "USD",
  "pickupEta": "2026-05-08T15:55:00Z",
  "dropoffEta": "2026-05-08T16:20:00Z",
  "expireTime": "2026-05-08T15:30:00Z"
}
Decline responses (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 4xx to POST /fleet/routes (dispatched). Nash treats the dispatch as failed and tries a different fleet.
  • Reject after acceptance. Ack 200, then PATCH /v1/fleet/deliveries/{deliveryId} with status: failed and failure.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’s orders[] is flat camelCase, with ISO 8601 timestamps and monetary values in minor units.

Required

FieldMeaning
idNash order id. Stable across modifications.
deliveryIdIdentifier the fleet uses on subsequent updates via PATCH /v1/fleet/deliveries/{deliveryId}. Present on dispatch / update / cancel; null on quote.
externalIdMerchant’s order identifier — useful for fleet-side reconciliation.
pickupAddressFull address string.
pickupBusinessNameStore / restaurant name.
pickupPhoneNumberE.164 — for courier-to-store contact.
pickupStartTime / pickupEndTimeWhen the order will be ready (window).
dropoffAddressCustomer address.
dropoffFirstNameRecipient first name.
dropoffPhoneNumberE.164 — for SMS tracking and courier contact.
dropoffStartTime / dropoffEndTimePromised delivery window.
itemsList of { name, quantity, ... }.
valueCentsSubtotal in minor units.
currencyISO 4217.
deliveryModenow or scheduled.
  • 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

FieldMeaning
stopTypepickup, dropoff, driver_break, etc.
sequence0-indexed position in the route.
location{ lat, lng }.
arrivalTime / endTimeWindow for this stop.
serviceTimeSeconds the courier is expected to spend at the stop.
orders[]Per-order snapshot — see “Order field reference” above.