> ## Agent Instructions
> Nash exposes two MCP servers. The docs MCP server at https://docs.usenash.com/mcp searches this documentation and needs no credentials. The Nash MCP server at https://mcp.usenash.com/mcp operates an organization's deliveries and needs a Nash API key. See https://docs.usenash.com/reference/build-with-ai.
> Use the Sandbox environment (https://api.sandbox.usenash.com/v1) for anything that creates or dispatches deliveries during development.
# Nash API
Source: https://docs.usenash.com/api-reference/api-overview
The Nash API is the gateway to the delivery orchestration platform. Find the right resource group, learn how to authenticate, pick an environment, and get started.
The Nash API is the gateway to our delivery orchestration platform. This page is the on-ramp: find the resource group you need, then learn how to authenticate, which environment to target, and the rate limits that apply.
## Available APIs
Create orders, compare quotes, and dispatch deliveries programmatically.
Choose providers and configure how jobs (deliveries) get dispatched.
Send notifications to your customers or internal systems.
Manage delivery origins and the geographic areas they serve.
Track and update a job (delivery) once it has been dispatched.
Track and update jobs (deliveries) with multiple packages.
Get the providers available in your organization.
Create, update, and dispatch ordered multi-stop routes.
Manage driver shifts.
Upsert reusable templates.
Manage products and store inventory.
Submit delivery feedback.
Manifest and dispatch orders for shipping.
The inbound surface a fleet uses to report delivery state to Nash.
Server health check.
## Conventions
The API follows RESTful conventions and uses JSON for both requests and responses. Set the `Content-Type` header to `application/json` on requests that carry a payload. All requests are strictly HTTPS.
Every Nash API response includes a request ID, whether the request succeeded or failed (see [Errors](/reference/errors)). Include it when contacting support about a specific API call — it's the fastest way for us to find your request.
## Authentication
Nash APIs use API keys. Send your key as a Bearer token in the `Authorization` header. If your key has access to more than one Nash organization, also send the `Nash-Org-Id` header to indicate which org the request is for.
```sh theme={"dark"}
curl --request GET \
--url https://api.sandbox.usenash.com/v1/jobs \
--header "Authorization: Bearer $NASH_API_KEY" \
--header "Nash-Org-Id: $NASH_ORG_ID"
```
See [Authentication](/reference/authentication) for full details, including how to create an API key and find your organization ID.
## Environments
Nash provides separate Production and Sandbox environments, each with US and AU endpoints. Sandbox is a free, fully-featured environment for testing your integration without dispatching to real drivers. For the full list of base URLs and Portal links, see [Environments](/reference/environments).
## Rate limits
Each organization is limited to 20 requests per second across all Nash API endpoints. Requests beyond the limit receive an HTTP `429` response, so build in a backoff strategy. See [Rate limits & throttling](/reference/rate-limits-throttling) for guidance.
## Using LLMs
If you're integrating with the help of an LLM, Nash publishes documentation in LLM-friendly formats and a few best practices for sharing context safely. See [Using LLMs with Nash API](/api-reference/using-llms-with-nash-api).
## Next steps
Create an API key and authenticate your requests.
Pick the right base URL for Sandbox or Production.
Start managing delivery operations with the Orders API.
Integrate faster with LLM-friendly documentation.
# Cancel Batch Job
Source: https://docs.usenash.com/api-reference/batch-job/cancel-job-by-job-id-and-task-or-job-configuration-id
post /v1/jobs/{jobId}/cancel
Cancel a batch job by job ID and task or job configuration ID.
# Create Batch Job
Source: https://docs.usenash.com/api-reference/batch-job/create-batch-job
post /v1/batch_job
Create a job with multiple packages
### What's a batch job?
A batch job is a group of packages delivered by the same courier, and so by the same provider.
A batch job holds N packages where N > 1, in contrast with single jobs (non-batch), which hold a single package.
### One pickup, many dropoffs
Every package in a batch job shares the same pickup: the same address, contact name, and business name. A request whose packages pick up from different locations is rejected with a `400` and the message `Batch jobs must share a single pickup location across all packages`. To move packages from several pickup points, create one job per pickup.
The response resembles [Create Job](/api-reference/job/create-job), with two added fields: `isBatch` and `batch`.
The `batch` field contains:
* `batch.id`: unique ID for this batch
* `batch.type`: `SINGLE_PICKUP_MULTIPLE_DROPOFFS` for every batch created through this endpoint
* `batch.objectSequence`: list of `jobConfigurations` (which carry the package information) in order
* `batch.stopTypes`: list of stops representing the action sequence, each `PICKUP` or `DROPOFF`
* `batch.quotes`, `batch.winnerQuote`, `batch.failedQuotes`: the same as `task.quotes`, `task.winnerQuote`, and `task.failedQuotes` on non-batch jobs
```json theme={"dark"}
{
"batch": {
"id": "bth_...",
"type": "SINGLE_PICKUP_MULTIPLE_DROPOFFS",
"objectSequence": [],
"stopTypes": [
"PICKUP",
"DROPOFF",
"DROPOFF"
],
"providerId": "FleetSimulatorFast",
"winnerQuote": {
"id": "qot_..",
"providerId": "FleetSimulatorFast",
"providerName": "FleetSimulator (4TestRunner)"
},
"quotes": [],
"failedQuotes": []
},
...
}
```
For this and all other endpoints, Nash processes fields from different providers — assume that any field in the response may be `null`.
# Refresh Quotes (Batch)
Source: https://docs.usenash.com/api-reference/batch-job/refresh-quotes-by-job-id-and-batch-id
post /v1/jobs/{jobId}/batch/{batchId}/refresh_quotes
Refresh the quotes for a batch job.
# Select Quote (Batch)
Source: https://docs.usenash.com/api-reference/batch-job/select-quote
post /v1/select_quote
Select the quote you would like to execute from a specific job.
# Update Batch Job
Source: https://docs.usenash.com/api-reference/batch-job/update-job-by-job-id-and-job-configuration-id
patch /v1/jobs/{id}/{jobConfigurationId}
Update fields on a Nash job. Updating a job should always work before a quote is selected; once one has been selected, Nash returns an error if the fleet rejects the change.
## Pre-dispatch updates
Updating a job should always work before a quote is selected. Some updates may result in pricing changes.
You can only update the package delivery times and the pickup or dropoff addresses before an order has been dispatched to a fleet. If any task in a job is dispatched and you attempt to update a delivery time or address, the endpoint returns an error.
If you update any of the following fields, the quotes returned by the job are refreshed and the endpoint returns a new set of quotes. Any existing quotes (and quote IDs) become invalid for dispatch. The fields that force a quote refresh:
1. `pickupAddress`
2. `dropoffAddress`
3. `packagePickupStartTime`
4. `packagePickupEndTime`
5. `packageDropoffStartTime`
6. `packageDropoffEndTime`
7. `packageDeliveryMode`
8. `packageRequirements`
9. `packageMinimumVehicleSize`
10. `packageValue`: this field can also be updated in the [post-dispatch stage](/api-reference/batch-job/update-job-by-job-id-and-job-configuration-id#post-dispatch-updates)
## Updating times
To update the times of a job, provide at least one of `packagePickupStartTime`, `packagePickupEndTime`, `packageDropoffStartTime`, and `packageDropoffEndTime`. Times you don't specify are calculated by Nash, and any previous times left out of the update request are overwritten. So to change one time field and keep the rest, send the others in the same request.
If you change `packageDeliveryMode` from `SCHEDULED` to `NOW`, you don't need to specify any other time fields.
## Post-dispatch updates
If a quote has been selected or the job was dispatched by auto-dispatch, Nash returns an error if any of the assigned fleets in the job reject the update.
In the post-dispatch stage, updating the package value of the job may result in a price change, depending on how the providers calculate their pricing.
Below is a list of fields that can be updated, with more detail on pre-dispatch and post-dispatch restrictions.
# Book a delivery window for order
Source: https://docs.usenash.com/api-reference/delivery-windows/book-a-delivery-window-for-order
post /v1/order/external-identifier/{externalId}/delivery-window/book
Reserve a delivery window for an order identified by external ID. The booking holds capacity in the window until confirmed or revoked.
# Confirm a delivery window for order
Source: https://docs.usenash.com/api-reference/delivery-windows/confirm-a-delivery-window-for-order
post /v1/order/external-identifier/{externalId}/delivery-window/confirm
Confirm a previously booked delivery window for an order. Finalizes the booking and commits the capacity reservation.
# Create a single delivery window
Source: https://docs.usenash.com/api-reference/delivery-windows/create-a-single-delivery-window
post /v1/delivery-window
Create a delivery window defining an available time slot for scheduled deliveries. Delivery windows are associated with a store location and have capacity limits.
# Get a delivery window
Source: https://docs.usenash.com/api-reference/delivery-windows/get-a-delivery-window
get /v1/delivery-window/{id}
Retrieve a single delivery window by ID, including its time slot, capacity, and status.
# Get eligible delivery windows for order
Source: https://docs.usenash.com/api-reference/delivery-windows/get-eligible-delivery-windows-for-order
get /v1/eligible_delivery_windows
Find delivery windows available for a specific order based on the order's store location and requirements. Requires either order_id or external_order_id. Optionally filter by time range and requested capacity.
# Revoke a delivery window for order
Source: https://docs.usenash.com/api-reference/delivery-windows/revoke-a-delivery-window-for-order
post /v1/order/external-identifier/{externalId}/delivery-window/revoke
Revoke a previously booked delivery window for an order. Releases the capacity reservation and disassociates the window from the order.
# Create dispatch strategy
Source: https://docs.usenash.com/api-reference/dispatch-strategies/create-dispatch-strategy
post /v1/options_group
Create a new dispatch strategy (options group) with provider selection rules, fallback logic, and quoting configuration.
# Automations
Source: https://docs.usenash.com/api-reference/dispatch-strategies/dispatch-automations
Apply dispatch strategies dynamically with rule-based automations evaluated against each incoming job.
Dispatch automation is a way to dynamically manage your dispatch strategies. An automation is composed of several rules; when a rule's conditions are met, its action applies a dispatch strategy to the job. Let's assume you have three dispatch strategies:
* Individual Order Dispatch Strategy (*DS\_1*): comprised of short-distance gig providers, lowest price strategy
* High Value Dispatch Strategy (*DS\_2*): contains a list of long-range providers that specialize in white-glove delivery
* Default Dispatch Strategy (*DS\_3*): a mix of all providers
You may want to assign different dispatch strategies based on the content of each delivery. One way to do that is to have the logic live on your server and attach the appropriate dispatch strategy for each new order.
Another way to do this is by creating an automation at the account level. An automation is a set of IF / ELIF / ELSE conditions:
```py theme={"dark"}
# pseudo code
if condition_1:
apply dispatch_strategy_1
else if condition2:
apply dispatch_strategy_2
else:
apply dispatch_strategy_3
```
Dispatch automations apply to any job that doesn't have a dispatch strategy assigned.
If a job meets the defined criteria then it will have the relevant dispatch strategy applied, otherwise,
the default dispatch strategy will be applied. The fallback default dispatch strategy can also be `None`,
if you do not want any dispatch strategy to apply (not common).
### Properties
A rule's conditions test properties of the incoming job. Text properties support equals, does not equal, in list, not in list, starts with, ends with, contains, and does not contain. Numeric properties support greater than, less than, and the other comparison operators. Weight and dimension properties let you pick the unit in the rule editor and Nash converts it for you; driving distance uses your organization's distance unit.
**Where**
| Property | What it checks |
| - | - |
| Pickup Business Name, Pickup City, Pickup State, Pickup Zip, Pickup Country | The pickup address fields. Country is a two-letter ISO code. |
| Dropoff Business Name, Dropoff City, Dropoff State, Dropoff Zip, Dropoff Country | The dropoff address fields. |
| Pickup Store Location | The store location the job picks up from. |
| Driving Distance | The driving distance from pickup to dropoff, in miles or kilometers. |
**When**
| Property | What it checks |
| - | - |
| Minutes Until Pickup, Minutes Until Dropoff | Minutes from now until the scheduled pickup or dropoff. |
| Pickup Time, Dropoff Time | The time of day of the scheduled pickup or dropoff, in that location's local time zone. Comparison operators only. |
| Day of Week | The weekday of the scheduled pickup, in the pickup location's local time. |
**What's in the package**
| Property | What it checks |
| - | - |
| Package / Order Value | The declared value of the order, entered in dollars. |
| Driver's Tip | The tip on the order, entered in dollars. |
| Items Count | The number of items in the package. |
| Number of Packages | The number of packages in the job. Always `1` when the automation evaluates an order. |
| Package Weight | The declared weight of the package, in pounds or kilograms. |
| Package Total Weight | The weights of the package's items added up, each multiplied by its quantity. Falls back to Package Weight when no item carries a weight. Comparison operators only. |
| Package Width, Package Height, Package Length | The declared package dimensions, in inches or centimeters. |
| Package Max Dimension | The largest single dimension across the package's items. Comparison operators only. |
| Package Cube | The volume of the package, width × height × length, in cubic inches or cubic centimeters. Comparison operators only. |
| Package Requirements | The requirements set on the package, such as a signature or age verification. Contains operators only. |
| Delivery Mode | The order's delivery mode: `now` or `scheduled`. |
**Your own signals**
| Property | What it checks |
| - | - |
| Tags | The tags on the order. Contains operators only. |
| Metadata | A key you set in `jobMetadata` (see below). |
| Random Grouping | A stable number from 0 to 99 derived from the order's ID, so you can send a share of jobs down a different path. "Less than 20" routes about a fifth of your jobs. |
**Matching postal codes.** Pickup Zip and Dropoff Zip compare against the postal code exactly as it's stored on the address, with no normalization. An address Nash geocodes carries the five-digit US ZIP, while an address you send as separate components keeps whatever you sent, including a ZIP+4 such as `94103-1234`. To match both forms, use **starts with** and the five-digit code rather than **equals**.
**Don't see the property you need?**
We are continuously adding to this list based on customer needs — reach out to [support@usenash.com](mailto:support@usenash.com) and we can help you add a new property.
### Using job metadata
You can query fields, such as tags, that you set in `jobMetadata` when applying dispatch automations. For example,
```js theme={"dark"}
jobMetadata: {
"tags": ["catering"]
}
```
And then when creating your Dispatch Rule you would query that the `jobMetadata` "tags" key contains the value "catering".
### Configuration
Dispatch automations can be configured from the [Nash Portal](https://portal.usenash.com/settings/dispatch-automation/active). If you plan to create several Nash Organizations to manage as part of a partnership with Nash, you can get in touch with your Nash point of contact to configure it at the Org Creation level.
# Dispatch strategies
Source: https://docs.usenash.com/api-reference/dispatch-strategies/dispatch-strategy
How dispatch strategies (options groups) control which providers can fulfill a job and how a provider is selected.
Dispatch Strategies, or Options Groups, are sets of configurations that can be attached to Nash jobs during creation.
For example, you can set up a job to dispatch to the most affordable provider. In another scenario, you might want to
dispatch the job to your internal fleet, and if they cannot fulfill it then you may want to send it to a third party provider.
Dispatch Strategies allow all of these customizations to happen.
### Allowed providers
The list of allowed providers allows you to limit the number of delivery providers available to a job. Through this list,
you can add (or remove) any provider that is currently enabled for your account. That way, when creating a job with that
dispatch strategy, only the providers you choose will show availability for that delivery.
### Selection strategy
Jobs that are assigned via auto-dispatch are assigned to a delivery provider based on one of four selection strategies:
**Price** (`lowest_price`): Choosing a price-based selection strategy will compare all of the available delivery provider's quotes, and
dispatch the job to the one with the lowest price.
**Ranked** (`preferred_order`): Choosing a selection strategy based on rank allows you to set your preferred delivery providers. Jobs will be
dispatched in order, based on which providers are actually available for each job.
**Reliability**: Choosing a reliability-based dispatch will engage with Nash's proprietary algorithm that ranks each
delivery provider's reliability. This is determined by a number of factors including completion rate, cancellation rate, and on-time rate.
**Dynamic Dispatch**: Weighs cost, reliability, and speed together for each delivery and picks the best provider for that specific job — see [Dynamic Dispatch](/reference/dispatch-strategies#dynamic-dispatch).
The [create](/api-reference/dispatch-strategies/create-dispatch-strategy) and [update](/api-reference/dispatch-strategies/update-dispatch-strategy) endpoints accept `lowest_price` and `preferred_order` as the `providerSelectionStrategy`; Reliability and Dynamic Dispatch are configured in the [Nash Portal](https://portal.usenash.com).
When auto-dispatch is enabled, the provider will be selected and job dispatched to them immediately upon job creation.
If your use case requires user confirmation before dispatch (for example, confirming the price), you can use the
[autodispatch](/api-reference/order/autodispatch-order) API endpoint to trigger the outcome of the strategy when you are ready for dispatch.
## When to enable auto-dispatch
### Possible applications where you might want to enable this option
* You want to send only one command to Nash to simultaneously create and dispatch a Job (set it and forget it).
* You offer free or fixed price delivery and don't want to show any dynamic delivery options or prices to your customers.
* You will always want the delivery to occur, you just want the lowest price option without having to write logic to
figure this out (you can set a max possible fee). This is popular with restaurants.
### Possible applications where you might **not** want to enable this option
* You want to allow a customer to choose their own delivery options like provider or price which is dynamically calculated by Nash.
* You want to know how much a particular delivery will cost before committing to the delivery, either for yourself or to show your customer. Very common in customer checkout workflows.
# Get Dispatch Strategies
Source: https://docs.usenash.com/api-reference/dispatch-strategies/get-dispatch-strategies
get /v1/options_group
List your organization's dispatch strategies.
# Update dispatch strategy
Source: https://docs.usenash.com/api-reference/dispatch-strategies/update-dispatch-strategy
patch /v1/options_group/{id}
Update an existing dispatch strategy's provider selection rules, fallback logic, or quoting configuration. Only provided fields are updated.
# Add Delivery Feedback
Source: https://docs.usenash.com/api-reference/feedback/add-delivery-feedback
post /v1/feedback/{delivery_id}
Record a customer's rating and comment against a delivery.
`score` is a number from 0 to 5. The tracking page's own feedback form sends the same range whichever scale you've chosen for it: thumbs up or down sends 5 or 1, a five-star rating sends 1 to 5, and a 0–10 NPS answer is halved, so it can arrive in half steps. The scale that produced a score isn't stored alongside it.
# Add Delivery Feedback by External Identifier
Source: https://docs.usenash.com/api-reference/feedback/add-delivery-feedback-by-external-identifier
post /v1/feedback/external_identifier/{external_identifier}
Record a customer's rating and comment against a delivery, addressed by your own identifier.
`score` is a number from 0 to 5. The tracking page's own feedback form sends the same range whichever scale you've chosen for it: thumbs up or down sends 5 or 1, a five-star rating sends 1 to 5, and a 0–10 NPS answer is halved, so it can arrive in half steps. The scale that produced a score isn't stored alongside it.
# Simulate Delivery Status Transition
Source: https://docs.usenash.com/api-reference/fleet-simulator/simulate-delivery-status-transition
post /v1/deliveries/{delivery_id}/simulate
Advance a delivery to a given status for testing in sandbox.
This endpoint is intended to simulate delivery status transitions in **sandbox only**.
It is available in dev and sandbox environments, and only for deliveries dispatched
to the Nash sandbox test provider. Use it to test how your integration reacts to
delivery status changes without needing a real courier.
## Supported statuses
Pass one of the following values as the `status` field in the request body:
| Status | Description |
| - | - |
| `assigned_driver` | A driver has been assigned to the delivery |
| `pickup_enroute` | Driver is en route to the pickup location |
| `pickup_arrived` | Driver has arrived at the pickup location |
| `pickup_complete` | Driver has picked up the order |
| `dropoff_enroute` | Driver is en route to the dropoff location |
| `dropoff_arrived` | Driver has arrived at the dropoff location |
| `dropoff_complete` | Order has been delivered to the customer |
| `return_in_progress` | Driver is returning the order |
| `return_arrived` | Driver has arrived back at the return location |
| `returned` | Order has been returned |
| `canceled_by_provider` | Provider canceled the delivery |
| `failed` | Delivery failed |
# Bulk Update Deliveries
Source: https://docs.usenash.com/api-reference/fleet/bulk-update-deliveries
patch /v1/fleet/deliveries
Apply updates to up to 100 deliveries in a single request. Each item is keyed by exactly one of `deliveryId` or `packageExternalIdentifier` (your own id set on the package at creation), plus the same field set as the single-delivery endpoint. The per-item result echoes back the key you sent under `deliveryId`. Per-item failures come back as a structured error in the response and do not affect the rest of the batch.
# Get Driver Group
Source: https://docs.usenash.com/api-reference/fleet/get-driver-group
get /v1/fleet/driver-groups/{first_version_contract_id}
Read one driver group and the drivers in it.
Returns the group and its `drivers`: each driver's `id`, your `externalIdentifier` for them, their name, and whether they are `enabled` and on shift (`isShiftActive`). The path takes the group's `firstVersionContractId`, the ID that stays the same however many times the group is edited.
# List Driver Groups
Source: https://docs.usenash.com/api-reference/fleet/list-driver-groups
get /v1/fleet/driver-groups
List the driver groups in your internal fleet, newest first.
Use this to read the driver groups your organization has, with the stable `firstVersionContractId` each one is known by. Disabled groups are left out unless you send `activeOnly=false`.
Results come back newest first, up to 500 a page. Pass the `nextCursor` from one page as `cursor` on the next call; a missing `nextCursor` means you've reached the end. To read who is in a group, call [Get Driver Group](/api-reference/fleet/get-driver-group).
# Outbound integration
Source: https://docs.usenash.com/api-reference/fleet/outbound-integration
Synchronous HTTP calls Nash makes to a fleet for quoting, dispatch, modifications, cancellations, and refresh.
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](/api-reference/fleet/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
```mermaid theme={"dark"}
sequenceDiagram
autonumber
participant Nash
participant Fleet
participant Courier
Note over Nash,Fleet: Quoting (optional)
Nash->>Fleet: POST /fleet/routes/quote
Fleet-->>Nash: 200 { priceCents, etas, expireTime }
Note over Nash: Nash ranks quotes per merchant strategy and picks a winner
Nash->>Fleet: POST /fleet/routes (event: dispatched)
Fleet-->>Nash: 200 { partnerRouteId, stops: [{ partnerStopId, deliveryId }] }
Fleet->>Fleet: route to fulfillment, assign courier
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { courier, externalDeliveryId }
Nash-->>Fleet: 200 { ...full delivery payload }
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { status: pickup_enroute, coordinates }
Courier->>Fleet: arrives at pickup
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { status: pickup_arrived }
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { status: pickup_complete, proofOfDelivery }
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { status: dropoff_enroute, coordinates }
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { status: dropoff_arrived }
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { status: dropoff_complete, proofOfDelivery }
```
## 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 ` 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
```
Updates only land on deliveries owned by the fleet's organization. Anything else returns not-found — Nash never leaks existence across organization boundaries.
## 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`. |
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:
| 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 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
```json theme={"dark"}
{
"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`:
```json theme={"dark"}
{
"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:
```json theme={"dark"}
{
"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:
```json theme={"dark"}
{ "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.
```json theme={"dark"}
{
"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.
```mermaid theme={"dark"}
sequenceDiagram
autonumber
participant Merchant
participant Nash
participant Fleet
Merchant->>Nash: PATCH /v1/order/{id} (new dropoff time)
Nash->>Nash: validate, update order
Nash->>Fleet: POST /fleet/routes/{routeId} {event: updated, data.fieldChanges: ["dropoffStartTime"], data.stops: [...]}
Fleet-->>Nash: 200 OK
Note over Fleet: Fleet re-routes courier or 4xx if uncommittable
```
## `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`.
```json theme={"dark"}
{
"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.
```mermaid theme={"dark"}
sequenceDiagram
autonumber
participant Merchant
participant Nash
participant Fleet
Merchant->>Nash: cancel order
Nash->>Nash: mark order canceled
Nash->>Fleet: POST /fleet/routes/{routeId}/cancel {event: canceled, data.deliveryIds: [...], cancelReason: ...}
Fleet-->>Nash: 200 OK
Fleet->>Nash: PATCH /v1/fleet/deliveries/{deliveryId} { status: canceled_by_provider }
Note over Nash,Fleet: Nash terminates the lifecycle; further PATCHes are no-ops
```
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`:
```json theme={"dark"}
{
"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).
```mermaid theme={"dark"}
sequenceDiagram
autonumber
participant Nash
participant Fleet
Note over Nash: periodic refresh from tracker
Nash->>Fleet: GET /fleet/routes/{routeId}
Fleet-->>Nash: 200 { stops: [{ deliveryId, status, coordinates, ... }, ...] }
Nash->>Nash: apply each stop entry via the same code path as inbound PATCH
```
## `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).
```json theme={"dark"}
{
"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:
```json theme={"dark"}
{
"externalQuoteId": "",
"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.
```mermaid theme={"dark"}
sequenceDiagram
autonumber
participant Nash
participant Fleet
participant FleetB as Backup Fleet
Nash->>Fleet: POST /fleet/routes (event: dispatched)
Fleet--xNash: 4xx { error: { code: "no_capacity" } }
Nash->>Nash: mark dispatch failed, try another fleet
Nash->>FleetB: POST /fleet/routes (event: dispatched, same routeId)
FleetB-->>Nash: 200 OK
```
## 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
| 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. |
# Update Delivery
Source: https://docs.usenash.com/api-reference/fleet/update-delivery
patch /v1/fleet/deliveries/{delivery_id}
Apply a partial update — such as status, courier location, proof of delivery, courier identity, or ETAs — to a delivery your organization owns. Only available for deliveries fulfilled by your own fleet (not by a third-party courier integration). Returns the updated delivery.
# Upsert Driver
Source: https://docs.usenash.com/api-reference/fleet/upsert-driver
put /v1/fleet/drivers/upsert
Create or update a driver in your internal fleet, keyed on your own `externalIdentifier`. There is no separate create vs. update call — sending the same `externalIdentifier` again updates the existing driver. The response's `created` field tells you which happened. To disable a driver, upsert with `isShiftActive: false` (or `enabled: false`) — there is no separate delete/disable call.
# Upsert Driver Group
Source: https://docs.usenash.com/api-reference/fleet/upsert-driver-group
put /v1/fleet/driver-groups/upsert
Create or update a driver group, keyed on your own identifier.
Your `externalIdentifier` is the upsert key. Send a new value to create a group, which needs a `displayName`; send the same value again to update that group. `created` in the response says which happened. Fields you leave out keep their current values.
`driverIds` is the group's full roster. Sending it replaces the list, `[]` removes everyone, and leaving it out changes nothing. To add or remove one driver without resending the roster, use `addFirstVersionContractIds` and `removeFirstVersionContractIds` on [Upsert Driver](/api-reference/fleet/upsert-driver).
There is no delete call. To disable a group, upsert it with `isActive: false`. A disabled group can lose members but can't gain new ones.
# Sync store inventory catalogs to SFTP
Source: https://docs.usenash.com/api-reference/inventory/sync-store-inventory-catalogs-to-sftp
post /v1/inventory/sync
Trigger a full catalog export+upload for all synced stores (`all: true`) or a specific list of store external ids. Returns the run activity id; per-store export runs asynchronously.
# Autodispatch Job
Source: https://docs.usenash.com/api-reference/job/autodispatch-job
post /v1/jobs/{id}/autodispatch
Automatically select a quote based on dispatch strategy
For a given job, you can set up automatic dispatch by creating [Dispatch Strategies](/reference/dispatch-strategies).
In a checkout experience, you often create a job first so you can show delivery quotes to your customer. Once the customer places the order, call this endpoint to dispatch the job using the Dispatch Strategy attached to it — instead of selecting a quote yourself.
To autodispatch a job, the job must have a Dispatch Strategy attached, with a provider selection strategy configured.
### How autodispatch selects a quote
When you call `/autodispatch`, Nash selects the quote tagged `autodispatch_preferred_quote`. Use this tag to know which price to display to your customer at checkout. You can read the full list of quotes at `create_job_response["jobConfigurations"][0]["advancedTask"]["quotes"]` and filter for the one with `"autodispatch_preferred_quote"` in `quote.tags`.
Make sure **Enable Autodispatch** is switched off on the Dispatch Strategy you are using to autodispatch the job.
# Autodispatch Job by External Identifier
Source: https://docs.usenash.com/api-reference/job/autodispatch-job-by-external-identifier
post /v1/jobs/external_identifier/{externalIdentifier}/autodispatch
Automatically select a quote based on dispatch strategy
For a given job, you can set up automatic dispatch by creating [Dispatch Strategies](/reference/dispatch-strategies).
In a checkout experience, you often create a job first so you can show delivery quotes to your customer. Once the customer places the order, call this endpoint to dispatch the job using the Dispatch Strategy attached to it — instead of selecting a quote yourself.
To autodispatch a job, the job must have a Dispatch Strategy attached, with a provider selection strategy configured.
### How autodispatch selects a quote
When you call `/autodispatch`, Nash selects the quote tagged `autodispatch_preferred_quote`. Use this tag to know which price to display to your customer at checkout. You can read the full list of quotes at `create_job_response["jobConfigurations"][0]["advancedTask"]["quotes"]` and filter for the one with `"autodispatch_preferred_quote"` in `quote.tags`.
Make sure **Enable Autodispatch** is switched off on the Dispatch Strategy you are using to autodispatch the job.
# Cancel Job by External Identifier
Source: https://docs.usenash.com/api-reference/job/cancel-job-by-external-identifier
post /v1/jobs/external_identifier/{externalIdentifier}/cancel
Cancels a Nash Job by the Partner's External ID if one was supplied at job creation.
# Cancel Job
Source: https://docs.usenash.com/api-reference/job/cancel-job-by-job-id-and-job-configuration-id
post /v1/jobs/{jobId}/{objectId}/cancel
Cancel a specific task or job configuration within a batch job. The objectId can be a task ID (tsk_ prefix) or job configuration ID (cfg_ prefix).
# Create Delivery Incident
Source: https://docs.usenash.com/api-reference/job/create-delivery-incident
post /v1/jobs/{jobId}/tasks/{taskId}/delivery_incident
Create a new delivery incident for a task
#### Requirements
At least one of the following fields must be provided:
* `requestAmountDeliveryFeeCents`
* `requestAmountOrderValueCents`
* `requestAmountTipCents`
All provided values must be non-negative integers (≥ 0), and at least one must be greater than zero.
# Create Job
Source: https://docs.usenash.com/api-reference/job/create-job
post /v1/jobs
Create a new job (delivery)
This endpoint is deprecated. Use the [Create Order](/api-reference/order/create-order) endpoint instead.
### Scheduled job time calculations
When creating a scheduled job, you must include at least one of the following fields:
* `packagePickupStartTime`
* `packagePickupEndTime`
* `packageDropoffStartTime`
* `packageDropoffEndTime`
Times you don't specify are calculated by Nash. "Now" or ASAP jobs don't require any of these fields.
If the specified pickup time of a scheduled job is very close to the current time (within 5 minutes in the past or 10 minutes in the future), it becomes an ASAP job automatically.
For this and all other endpoints, Nash processes fields from different providers — assume that any field in the response may be `null`.
### Package items
An array of item objects, each describing an item within the package for delivery.
Each object should include the item's name, dimensions (length, width, height), and count, giving a detailed inventory of the package's contents. Nash uses this to handle the package correctly and allocate space for transport.
Include both **pickup** and **dropoff times** wherever you can: our estimates are based on historical time data rather than real-time metrics.
The fields `selectedConfiguration` and `jobSpecification` returned in the job response are deprecated and should not be used.
**Migration guide, replace:**
`jobSpecification->package` with `data->jobConfiguration[0]->package`
and `selectedConfiguration` with `jobConfiguration[0]`
# Create or Update Job by External Identifier
Source: https://docs.usenash.com/api-reference/job/create-or-update-job-by-external-identifier
post /v1/jobs/external_identifier/{externalIdentifier}
Create a new job or update an existing one using your external identifier. If a job with the given external identifier exists, it is updated; otherwise a new job is created. Set autodispatch=true to automatically dispatch after creation.
This endpoint is deprecated. Please use the [Create or Update Order by External Identifier](/api-reference/order/create-or-update-order-by-external-identifier) endpoint instead.
# Get Job
Source: https://docs.usenash.com/api-reference/job/get-job
get /v1/jobs/{id}
Retrieve a job by its ID.
# Get Job by External Identifier
Source: https://docs.usenash.com/api-reference/job/get-job-by-external-identifier
get /v1/jobs/external_identifier/{externalIdentifier}
Retrieve a job by its external identifier.
# Get Refund Request
Source: https://docs.usenash.com/api-reference/job/get-refund-request
get /v1/refund-request/{id}
Retrieve a refund request by its ID.
# Get Refund Requests
Source: https://docs.usenash.com/api-reference/job/get-refund-requests
get /v1/refund-requests
Retrieve a list of refund requests by task ID or job ID.
# Jobs overview
Source: https://docs.usenash.com/api-reference/job/jobs
What a job (delivery) is in Nash, its data model, and the most common paths in the job object.
When one or more Orders are dispatched, they become a Job.
Jobs represent the actual delivery process. A Job holds the package(s) to be delivered, the quotes from providers willing to deliver the package(s), and, once a quote has been selected, the current delivery status.
Within each job, there's a structure that includes **Job Configurations**, **Tasks**, and **Deliveries** that manage the details and processes of delivery.
## Job data model
The figure here draws the job data model as a tree and gives the path to each part of the job object:
* **Order**: the delivery details before dispatch. When one or more orders are dispatched, they become a job.
* **Job**: the primary object for a delivery. Path: `id`.
* **Job configuration**: the dispatch strategy, special instructions, package, and tasks for this job. Path: `jobConfigurations[0]`.
* **Package**: what is carried, with its pickup and dropoff locations. Path: `jobConfigurations[0].package`.
* **Task**: one attempt at the delivery. Path: `jobConfigurations[0].tasks[0]`.
* **Quotes**: the offers for that attempt. Path: `jobConfigurations[0].tasks[0].quotes`.
* **Delivery**: status, ETA, courier details, and proof of delivery. It is `null` until a provider is assigned. Path: `jobConfigurations[0].tasks[0].delivery`.
* **A second task**: a reassigned job keeps its old task and gains a new one with its own quotes and delivery. Path to the latest: `jobConfigurations[0].advancedTask`.
* **Batch**: a batch job carries several packages in one multi-stop delivery, each with its own pickup and dropoff. A job that is not a batch has no `batch` key, and its `isBatch` is false.
Here's how the data model relates to each other:
* **Job**: This is the primary object that represents a delivery. It contains all the specifications for the package(s) to be delivered, such as the size, value, and the pickup and dropoff locations.
* **Job Configuration**: Within each Job, there is a JobConfiguration. It includes information on the dispatch strategy, special instructions, and other configurations specific to this job.
* **Task**: Under each Job Configuration, there can be a list of Tasks. A Task is where the action happens; it involves a set of Quotes and a Delivery object. When you create a new Task, you're seeking quotes for a new delivery to occur. After requesting a Task, you will receive a set of quotes from different providers, and upon selecting one, you will initiate a Delivery object that tracks the job's delivery lifecycle.
* **Delivery**: The Delivery object holds information on the actual progress of the delivery, such as the status, estimated delivery time, courier details, and proof of delivery. It is created after you choose a quote for a Task and it becomes active when a delivery is underway.
Nash provides standardized statuses ([Delivery Statuses](/reference/delivery-status)) that help you understand the delivery's progress, regardless of the provider. This simplicity lets you focus on a single integration with Nash rather than deal with the idiosyncrasies of different providers' status naming conventions.
## Putting it into practice
Here's an example scenario: You create a Job to deliver an order using your internal fleet (this would be your first Task). If you later realize that you don't have an internal driver available, you might create a new Task for the same Job to get quotes from third-party providers within Nash. Once you select a new quote, this initiates another Delivery object associated with the second Task.
## Common paths in the job object
The Job object contains various nested structures. Here are the most commonly used paths to access important information:
### Basic job information
* **Job ID**: `Job["id"]`
### Quotes and tasks
* **All quotes for the first task**: `Job["jobConfigurations"][0]["tasks"][0]["quotes"]`
* **ID of a specific quote** (replace `Index` with the quote's index): `Job["jobConfigurations"][0]["tasks"][0]["quotes"][INDEX]["id"]`
### Advanced tasks
When a job has multiple tasks (e.g. it has been reassigned to a different delivery provider), use this to get the most advanced (aka latest) task in the process:
`Job["jobConfigurations"][0]["advancedTask"]`
## Delivery status
* **Check if a provider has been assigned**: `Job["jobConfigurations"][0]["tasks"][0]["delivery"]` If this value is `null`, no provider has been assigned yet.
* **Access delivery information**: `Job["jobConfigurations"][0]["tasks"][0]["delivery"]`
This object contains useful attributes such as:
* `status`
* `dropoffEta`
* `courierName`
* `courierPhoneNumber`
* `courierLocation`
* **Fees**: the same object carries the delivery's fee fields (`priceCents`, `waitFeeCents`, `totalPriceCents`, and the rest). What each one means, what the total includes, and when each fee is final is covered in [Delivery fees and pricing](/reference/delivery-fees-and-pricing).
### Example job object
Note: All timestamps are in UTC and in this format `%Y-%m-%dT%H:%M:%S.%f`
The Job object returned for most endpoints looks like this:
```json theme={"dark"}
{
"id": "job_R7FXJrfB99bpvpzf6CriJC",
"createdAt": "2022-03-05T01:11:51.078927",
"portalUrl": "https://portal.usenash.com/active/job_R7FXJrfB99bpvpzf6CriJC",
"externalIdentifier": null,
"jobMetadata":
{
"key": "val"
},
"isActive": false,
"jobConfigurations":
[
{
"package":
{
"description": "Test package description",
"pickupEndTime": null,
"pickupStartTime": "2022-03-05T01:16:51",
"dropoffStartTime": null,
"dropoffEndTime": null,
"valueCents": 100,
"taxCents": 950,
"serviceFeeCents": null,
"itemsCount": 1,
"pickupLocation":
{
"id": "loc_dB4x4pToexhgRvGhZSTHaQ",
"address": "185 University Ave, Palo Alto, CA 94301",
"formattedAddress": "185 University Ave, Palo Alto, CA 94301, USA",
"addressNumber": "185",
"addressFormattedStreet": "University Ave",
"addressCity": "Palo Alto",
"addressCountry": "US",
"addressState": "CA",
"addressZip": "94301",
"addressCounty": "Santa Clara County",
"businessName": "Test pickup Business",
"instructions": "Test pickup Instructions",
"firstName": "Test pickup FirstName",
"lastName": "Test pickup LastName",
"phoneNumber": "+12345679012",
"email": null
},
"dropoffLocation":
{
"id": "loc_5heYem2xKGfNt9twg33kcT",
"address": "401 San Antonio Rd, Mountain View, CA 94040",
"formattedAddress": "401 San Antonio Rd, Mountain View, CA 94040, USA",
"addressNumber": "401",
"addressFormattedStreet": "San Antonio Rd",
"addressCity": "Mountain View",
"addressCountry": "US",
"addressState": "CA",
"addressZip": "94040",
"addressCounty": "Santa Clara County",
"businessName": "Test dropoff Business",
"instructions": "Test dropoff Instructions",
"firstName": "Test dropoff FirstName",
"lastName": "Test dropoff LastName",
"phoneNumber": "+12345679019",
"email": null
}
},
"tasks":
[
{
"id": "tsk_7xKGR6PW2pBtuTeRg2b7Bw",
"createdAt": "2022-03-05T01:11:51.078927",
"status": "COMPLETED",
"providerId": "FleetSimulator",
"tipAmountCents": 250,
"winnerQuote":
{
"id": "qot_nh5VSwxr4trHsuKHpQbGW6",
"providerId": "FleetSimulator",
"providerName": "FleetSimulator"
},
"quotes":
[
{
"id": "qot_DSv9NrbiNMjQw556H33LkY",
"providerId": "uber_partner",
"providerName": "Uber",
"providerLogo": "https://nash-provider-logos.s3.us-west-1.amazonaws.com/uber-logo.png",
"createdTime": "2022-03-05T01:11:52.912584",
"expireTime": "2022-03-05T01:16:52.913410",
"priceCents": 1209,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": "2022-03-05T01:29:08",
"dropoffEta": "2022-03-05T02:03:39",
"tollFeeCents": 0,
"insuranceFeeCents": 0,
"taxAmountCents": 0,
"totalPriceCents": 1209
},
{
"id": "qot_Fz8tQbiNMjQw556H33LkZ",
"providerId": "roadie",
"providerName": "Roadie",
"providerLogo": "https://nash-provider-logos.s3.us-west-1.amazonaws.com/roadie-logo.png",
"createdTime": "2022-03-05T01:11:52.912584",
"expireTime": "2022-03-05T01:16:52.913410",
"priceCents": 699,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": "2022-03-05T01:29:08",
"dropoffEta": "2022-03-05T02:03:39",
"tollFeeCents": 0,
"insuranceFeeCents": 0,
"taxAmountCents": 0,
"totalPriceCents": 699
},
{
"id": "qot_nh5VSwxr4trHsuKHpQbGW6",
"providerId": "FleetSimulator",
"providerName": "FleetSimulator",
"providerLogo": "https://nash-provider-logos.s3.amazonaws.com/FleetSimulator.png",
"createdTime": "2022-03-05T01:11:52.417462",
"expireTime": "2022-03-05T02:11:52.417463",
"priceCents": 0,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": null,
"dropoffEta": "2022-03-05T01:17:52.417416",
"tollFeeCents": 0,
"insuranceFeeCents": 0,
"totalPriceCents": 0
},
{
"id": "qot_mfqxvKhCK8jqioCiVZBnXt",
"providerId": "frayt_partner",
"providerName": "Frayt",
"providerLogo": "https://nash-provider-logos.s3.us-west-1.amazonaws.com/frayt-logo.jpeg",
"createdTime": "2022-03-05T01:11:52.870453",
"expireTime": "2022-03-05T03:11:52.870457",
"priceCents": 3239,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": null,
"dropoffEta": null,
"tollFeeCents": 0,
"insuranceFeeCents": 0,
"totalPriceCents": 3239
},
{
"id": "qot_5DyiesH2CXYXunoavBuBux",
"providerId": "skipcart_partner",
"providerName": "Skipcart",
"providerLogo": "https://nash-provider-logos.s3.us-west-1.amazonaws.com/skipcart-logo.png",
"createdTime": "2022-03-05T01:11:52.900834",
"expireTime": "2022-03-05T01:21:52",
"priceCents": 699,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": "2022-03-05T01:26:51",
"dropoffEta": "2022-03-05T02:11:51",
"tollFeeCents": 0,
"insuranceFeeCents": 0,
"totalPriceCents": 699
},
{
"id": "qot_F48QHqWtNdEeZWNGJH9MzJ",
"providerId": "senpex_partner",
"providerName": "Senpex",
"providerLogo": "https://nash-provider-logos.s3.us-west-1.amazonaws.com/senpex-logo.png",
"createdTime": "2022-03-05T01:11:53.875782",
"expireTime": "2022-03-05T02:11:53.875797",
"priceCents": 3450,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": null,
"dropoffEta": "2022-03-05T02:41:53.875791",
"tollFeeCents": 0,
"insuranceFeeCents": 0,
"totalPriceCents": 3450
}
],
"failedQuotes":
[
{
"id": "qot_n7pBYDYawaEFb83VL9C6oc",
"providerId": "Entree_OnFleet",
"providerName": "Entree",
"errorMessage": "Provider Entree_OnFleet is ineligible because of location coverage: pickup_state=CA and dropoff_state=CA. Provider location_coverage=['IL']"
},
{
"id": "qot_cQWPW48ZDkHLpcR5Bof6Y8",
"providerId": "caterwheels_partner",
"providerName": "CaterWheels",
"errorMessage": "Provider caterwheels_partner is ineligible because of location coverage: pickup_state=CA and dropoff_state=CA. Provider location_coverage=['NY']"
},
{
"id": "qot_JzcNUsiqrBpDptZpDeuNwk",
"providerId": "deluxe_delivery_partner",
"providerName": "Deluxe Delivery",
"errorMessage": "Provider deluxe_delivery_partner is ineligible because of location coverage: pickup_state=CA and dropoff_state=CA. Provider location_coverage=['NY', 'CT', 'PA', 'NJ', 'DE', 'MD', 'VA']"
},
{
"id": "qot_gkXDGdT5wJvVbafHvLRrGf",
"providerId": "senpex_batch_partner",
"providerName": "Senpex (Batch)",
"errorMessage": "Provider senpex_batch_partner is ineligible because of PackageDeliveryMode is not scheduled"
},
{
"id": "qot_FoidPBMEvuFcs6rdVXmDvJ",
"providerId": "dropcar_partner",
"providerName": "Dropcar",
"errorMessage": "Provider dropcar_partner is ineligible because of location coverage: pickup_city='Palo Alto, CA' and dropoff_city='Mountain View, CA'. Provider location_coverage=['Chicago, IL', 'New York, NY']"
},
{
"id": "qot_QYjd23Rbo7Ca452pL6ACyt",
"providerId": "snap_partner",
"providerName": "Snap",
"errorMessage": "Provider snap_partner is ineligible because of location coverage: pickup_city='Palo Alto, CA' and dropoff_city='Mountain View, CA'. Provider location_coverage=['Chicago, IL']"
}
],
"delivery":
{
"id": "dlv_MwQDy3s6ajvw5aQ7eQPt7i",
"status": "DROPOFF_COMPLETE",
"isActive": false,
"pickupEta": null,
"dropoffEta": "2022-03-05T01:17:52.417416",
"dropoffDeadline": null,
"priceCents": 0,
"nashFeeCents": 100,
"currency": "USD",
"documents": [],
"courierName": "Tjy Ifulu",
"courierPhoneNumber": "+13408480904",
"courierLocation":
{
"lat": 37.4043247,
"lng": -122.1112215
},
"proofOfDelivery":
[
{
"type": "photo_proof_of_delivery",
"url": "https://nash-proof-of-delivery-dev.s3-us-west-1.amazonaws.com/dlv_ZkHf4mLVCXy3avKDJE77jj_J2YHkGbRN8kTdjBrgu8gaQ==.jpg"
},
{
"type": "photo_proof_of_delivery",
"url": "https://nash-proof-of-delivery-dev.s3-us-west-1.amazonaws.com/dlv_ZkHf4mLVCXy3avKDJE77jj_AKapMndrIp2DDW5YUnWa9w==.jpg"
},
{
"type": "signature_proof_of_delivery",
"url": "https://nash-proof-of-delivery-dev.s3-us-west-1.amazonaws.com/dlv_ZkHf4mLVCXy3avKDJE77jj_toG5E7I-BZrvXqnIZAVyWw==.jpg"
}
],
"statusHistory":
[
{
"createdAt": "2022-03-05T01:12:00.984078",
"status": "created"
},
{
"createdAt": "2022-03-05T01:12:16.067396",
"status": "not_assigned_driver"
},
{
"createdAt": "2022-03-05T01:13:02.459762",
"status": "assigned_driver"
},
{
"createdAt": "2022-03-05T01:14:32.532602",
"status": "pickup_enroute"
},
{
"createdAt": "2022-03-05T01:15:17.533789",
"status": "pickup_complete"
},
{
"createdAt": "2022-03-05T01:16:02.598233",
"status": "dropoff_enroute"
},
{
"createdAt": "2022-03-05T01:17:32.690484",
"status": "dropoff_arrived"
},
{
"createdAt": "2022-03-05T01:18:17.945368",
"status": "dropoff_complete"
}
],
"tollFeeCents": 0,
"waitFeeCents": 0,
"waitTimeMinutes": 0,
"cancellationFeeCents": 0,
"returnFeeCents": 0,
"insuranceFeeCents": 0,
"totalPriceCents": 0
}
}
]
}
]
}
```
## Public tracking URL
Nash provides your end customers with a way to track deliveries without having to be logged into the Nash Portal. Nash will automatically send text alerts which include the tracking link for a live delivery to the end recipient, unless you would like to disable this feature for your organization. The easiest way to get the link is to read the `publicTrackingUrl` field from the job or order response; the URL takes this form:
`https://tracking.usenash.com/{jobId}`
`https://tracking.sandbox.usenash.com/{jobId}`
See [Embed the Nash live-tracking experience](/reference/nash-embedding) for embedding the tracking view in your own app.
# List/Search Jobs
Source: https://docs.usenash.com/api-reference/job/listsearch-jobs
get /v1/jobs
List jobs (deliveries) with filters, sorting, and pagination. Returns a slim payload suitable for discovery; use GET /v1/jobs/ for full details.
# Reassign a Task
Source: https://docs.usenash.com/api-reference/job/reassign-task-by-job-id-and-task-id
post /v1/jobs/{jobId}/{taskId}/reassign
Create a new task in a job based on a given job ID and task ID.
Reassign a task within an existing job. You can update task parameters along the way, including pickup and dropoff times — which is how you reschedule a task.
### Rescheduling a task
When reassigning a task, you can update the following time fields to reschedule it:
* `pickupStartTime`
* `pickupEndTime`
* `dropoffStartTime`
* `dropoffEndTime`
This lets you change the timing of an existing task without creating a new job or task from scratch.
Times you don't specify are calculated by Nash, based on the existing job configuration and current conditions.
# Refresh Quotes
Source: https://docs.usenash.com/api-reference/job/refresh-quotes-by-job-id-and-task-id
post /v1/jobs/{jobId}/tasks/{taskId}/refresh_quotes
Regenerate quotes for a given job's task.
# Select Quote
Source: https://docs.usenash.com/api-reference/job/select-quote
post /v1/select_quote
Select the quote you would like to execute from a specific job.
# Update Job
Source: https://docs.usenash.com/api-reference/job/update-job-by-id
patch /v1/jobs/{id}
Update fields on a Nash job. Updating a job should always work before a quote is selected; once one has been selected, Nash returns an error if the fleet rejects the change.
## Pre-dispatch updates
Updating a job should always work before a quote is selected. Some updates may result in pricing changes.
You can only update the package delivery times and the pickup or dropoff addresses before an order has been dispatched to a fleet. If any task in a job is dispatched and you attempt to update a delivery time or address, the endpoint returns an error.
If you update any of the following fields, the quotes returned by the job are refreshed and the endpoint returns a new set of quotes. Any existing quotes (and quote IDs) become invalid for dispatch. The fields that force a quote refresh:
1. `pickupAddress`
2. `dropoffAddress`
3. `packagePickupStartTime`
4. `packagePickupEndTime`
5. `packageDropoffStartTime`
6. `packageDropoffEndTime`
7. `packageDeliveryMode`
8. `packageRequirements`
9. `packageMinimumVehicleSize`
10. `packageValue`: this field can also be updated in the [post-dispatch stage](/api-reference/job/update-job-by-id#post-dispatch-updates)
## Updating times
To update the times of a job, provide at least one of `packagePickupStartTime`, `packagePickupEndTime`, `packageDropoffStartTime`, and `packageDropoffEndTime`. Times you don't specify are calculated by Nash, and any previous times left out of the update request are overwritten. So to change one time field and keep the rest, send the others in the same request.
If you change `packageDeliveryMode` from `SCHEDULED` to `NOW`, you don't need to specify any other time fields.
## Post-dispatch updates
If a quote has been selected or the job was dispatched by auto-dispatch, Nash returns an error if any of the assigned fleets in the job reject the update.
In the post-dispatch stage, updating the package value of the job may result in a price change, depending on how the providers calculate their pricing.
Below is a list of fields that can be updated, with more detail on pre-dispatch and post-dispatch restrictions.
# Update job by job id and job configuration id
Source: https://docs.usenash.com/api-reference/job/update-job-by-job-id-and-job-configuration-id
patch /v1/jobs/{jobId}/{jobConfigurationId}
# Server Health Check
Source: https://docs.usenash.com/api-reference/miscellaneous/server-health-check
get /v1/health-check
Check the health of the Nash API server.
# Add Contact to the Deny List
Source: https://docs.usenash.com/api-reference/notifications/add-a-contact-to-the-deny-list
post /v1/organization-management/notifications/deny-list
Adds a contact to the deny list to opt the user out of communications.
# Get Contacts in the Deny List
Source: https://docs.usenash.com/api-reference/notifications/get-contacts-in-the-deny-list
get /v1/organization-management/notifications/deny-list
Retrieves a list of contacts that have opted out of communications.
# Get Notification Trigger Catalog
Source: https://docs.usenash.com/api-reference/notifications/get-notification-trigger-catalog
get /v1/notification-triggers/catalog
The full notification-trigger catalog. Each entry describes a trigger's `type` slug, human `title`, the `value` used to subscribe automations, the `entityType` (route/delivery/task/order/activity), its `statuses` vocabulary, the JSON Schema of its create `parameters`, and the `supportedFilters` / `supportedActions`. The shared `filterCriteria` (dispatch strategies, contracts, zones, metadata conditions) and `actions` are described once at the top level. Designed for agents and humans to discover what can be automated and how.
# List Notification Trigger Templates
Source: https://docs.usenash.com/api-reference/notifications/list-notification-trigger-templates
get /v1/notification-triggers/templates
List pre-defined notification trigger templates. Each template has a `type` slug (e.g. 'tasks_attempts_exhausted') and a human-readable `title` (e.g. 'Tasks Attempts Exhausted'). Use the `type` when creating a trigger via POST /v1/notification-triggers.
# Notifications
Source: https://docs.usenash.com/api-reference/notifications/notifications
Configure notification triggers, channels, and message variables to keep customers and internal teams informed about deliveries.
Nash has a built-in, fully customizable notifications platform. This platform enables you to own the end-to-end customer experience and keep your internal teams up to speed on events and potential issues.
Each notification is broken down into two primary parts: defining the **trigger** and **message**, and identifying the **audience**. The trigger and message are defined in each Notification Trigger, and the audience is defined by the Destination. By separating the trigger and message from the audience, Nash makes it easy to send multiple notifications to the same audience.
Within the message section, you will have access to delivery-specific variables that you can use to customize the message to your end customer or internal team.
## Supported channels
Nash can send notifications across the following channels:
* SMS
* Email
* Slack
* Webhook
* WhatsApp
A single Destination can mix channels. Triggers can also hand off to Nash Agent — starting a custom agent to investigate and resolve an issue, or pushing an alert to your team.
## Default notifications
By default, your account will have 3 customer-facing SMS notifications enabled, and 2 internal notifications that are disabled. To see these notifications and edit them if necessary, go to **Settings > Notifications** in the Nash Portal.
### Customer-facing notifications
1. **Status transitions to "Pickup Complete"**
Message: `Your order from {{pickup_business_name}} has been picked up. Stay tuned for more text updates and track the delivery here {{job_tracking_link}}`
2. **Status transitions to "Dropoff Enroute"**
Message: `The driver is on their way. They should arrive by {{dropoff_eta}}.`
3. **Status transitions to "Dropoff Complete"**
Message: `Your order from {{pickup_business_name}} has been delivered.`
### Internal notifications (disabled by default)
To activate internal notifications, you need to create a Destination and add it to each notification. You can select to receive notifications via SMS, email, Slack, and/or webhook.
1. **Delivery Booked**
Message: `A new delivery has been scheduled with Nash. Track here: {{job_tracking_link}}.`
2. **Task Reassigned**
Message: `Task reassigned on job {{job_id}}. Track here: {{job_tracking_link}}.`
## Recommendations
### Setting up flags
Flags allow teams to be proactive during live delivery operations. You can enable flags for any notification, and flagged deliveries appear in the **Flagged** view in the Portal.
### Recommended triggers
We strongly recommend setting up and enabling these triggers:
1. **Autodispatch Failed**: Alerts when a delivery fails to auto-dispatch.
2. **Address Validation Failed**: Notifies when there's an issue with the customer's address.
## Trigger options
Triggers are grouped by what they watch. Enable, disable, and fine-tune any of them under **Settings > Notifications** in the Portal. For the authoritative, machine-readable list, call [List notification trigger templates](/api-reference/notifications/list-notification-trigger-templates).
| Trigger | Fires when |
| :- | :- |
| **Delivery** | |
| Delivery Status Transition | A delivery moves to any of the statuses you choose. One trigger can watch several statuses, each firing on its own. |
| Delivery Incident Report | A courier files an incident report; can filter to created or resolved reports. |
| Task Reassignment | A delivery is reassigned — every time, or after a set number of reassignments. |
| Autodispatch Failed | Automatic dispatch can't assign a provider. |
| Delivery Cancellation Failed | A cancellation attempt fails. |
| Job Creation Failed | A job can't be created. |
| Internal Driver Not Assigned | An internal-fleet delivery still has no driver after a set time. |
| Delivery Completed Too Early | A delivery is marked complete before its scheduled window. |
| Tasks Attempts Exhausted | Every delivery attempt has been used. |
| Status Triggered Too Early | A status is set earlier than expected. |
| Driver Stacking | Multiple deliveries are stacked on one driver. |
| Time Elapsed Since Status | A delivery holds a status for a set time without moving on. |
| Proof of Delivery Uploaded | A proof-of-delivery photo is uploaded. |
| Courier Updated | The assigned courier or provider changes. |
| Timing Updated | A delivery's ETA or timing changes. |
| AI Conversation Completed | A Nash AI conversation about a delivery finishes. |
| Delivery Note Added | A note is added to a delivery. |
| **Driver** | |
| Driver Geolocation | A driver is within, beyond, or stopped for a set distance or time from pickup or dropoff. |
| Zone Geolocation | A driver enters or leaves a configured zone. |
| Driver Location Inconsistent with Delivery Status | A driver's location doesn't match the reported delivery status. |
| Pickup Delayed | The pickup ETA runs too far past the scheduled pickup time. |
| Dropoff Delayed | The dropoff ETA runs too far past the scheduled dropoff time. |
| **Status** | |
| "Stuck" Status | A delivery holds a status longer than expected. |
| Statuses Changed Too Quickly | Delivery statuses change faster than they should. |
| Live Monitoring Status | A delivery's live-monitoring health changes. |
| Activity Status Transition | A scheduled activity changes status. |
| **Order** | |
| Nash Order Needs Attention | An order needs manual attention. |
| Nash Order Ready to Fulfill | An order is ready to fulfill. |
| Order Not Dispatched Close to Pickup | An order still isn't dispatched as pickup approaches. |
| Address Validation Failed | An address can't be validated. |
| Order Modified | An order's details change. |
| Delivery Window Booked Timeout | A booked delivery window times out without confirmation. |
| Optimization Unassigned Order | Optimization leaves an order unassigned. |
| Optimization Action Status | An optimization action succeeds, fails, or is skipped. |
| API Based | You trigger it directly from the API. |
| **Refund** | |
| Refund Request | A refund request moves through review — under review, needs attention, approved, denied, and more. |
## Dynamic text options
Here are variables you can use in notification messages. The message editor in the Portal autocompletes the full, current list as you type `{{`, and some triggers expose extra variables specific to their event (for example, refund or optimization details). Nash adds to the list regularly.
| Variable | Description |
| :- | :- |
| **Customer** | |
| `{{customer.first_name}}` | The customer's first name (in dropoff address) |
| `{{customer.last_name}}` | The customer's last name (in dropoff address) |
| `{{customer.phone_number}}` | The customer's phone number |
| `{{customer.email}}` | The customer's email address |
| `{{customer_feedback_link}}` | The URL for the customer to provide feedback |
| **Pickup** | |
| `{{org_name}}` | Name of your organization in Nash |
| `{{pickup_business_name}}` | The business name from where the order(s) were picked up |
| `{{pickup_business.phone_number}}` | The phone number associated with pickup location |
| `{{pickup_location.email}}` | The email address associated with the pickup location |
| `{{pickup_eta}}` | The pickup ETA |
| `{{pickup_datetime}}` | The actual pickup datetime localized to the pickup location |
| `{{pickup_address}}` | The pickup address |
| `{{pickup_instructions}}` | Instructions for the pickup |
| **Dropoff** | |
| `{{dropoff_eta}}` | The dropoff ETA |
| `{{dropoff_datetime}}` | The dropoff datetime localized to the dropoff location. |
| `{{dropoff_business_name}}` | The dropoff business name. |
| `{{dropoff_location.address_first_line}}` | The first line of the dropoff address |
| `{{proof_of_delivery_photo_url}}` | The URL link to the photo proof of delivery |
| `{{dropoff_address}}` | The dropoff address |
| `{{dropoff_instructions}}` | Instructions for the dropoff |
| `{{pod_upload_url}}` | A link to upload proof of delivery |
| **Job** | |
| `{{job_id}}` | The job ID e.g. job\_oBDDbYnKFCHAfivtjMoabt |
| `{{job_cfg_id}}` | The job configuration ID e.g. cfg\_SuC7dAU8VErdhzx9Wa345k |
| `{{job_metadata.X}}` | Pulls from the key X in job\_metadata. X can be any field |
| `{{job_tracking_link}}` | The link to track the job |
| `{{job_details_url}}` | The URL to view job details |
| `{{job_external_identifier}}` | The external ID of a job |
| `{{job_feedback_url}}` | The URL for providing job feedback |
| `{{delivery_id}}` | The Nash delivery ID |
| `{{description}}` | The package description |
| **Delivery Provider** | |
| `{{provider_delivery_id}}` | The delivery ID provided by the delivery provider |
| `{{provider_name}}` | The name of the delivery provider |
| `{{provider.slack_channel}}` | The Slack channel configured for the assigned provider |
| `{{courier.phone_number}}` | The courier's phone number |
| `{{driver.name}}` | The name of the driver/courier |
| `{{driver.phone_number}}` | The phone number of the driver/courier |
| `{{delivery_incident_report_type}}` | The type of the delivery incident |
| `{{delivery_incident_report_message}}` | The message contained in the delivery incident |
| **Nash** | |
| `{{nash_order_id}}` | The Nash order ID |
| `{{nash_order_portal_url}}` | The URL to view the order in the Nash Portal |
| `{{nash_order_validation_errors}}` | Any validation errors associated with the Nash order |
| `{{nash_order_external_id}}` | The external ID of the Nash order |
| `{{nash_order_packing_slip_url}}` | The packing slip URL (ZPL) for the Nash order |
| `{{nash_order_packing_slip_pdf_url}}` | The packing slip PDF URL for the Nash order |
| `{{nash_order_pickup_location.email}}` | The email associated with the pickup location for the Nash order |
| `{{nash_order_dropoff_location.email}}` | The email associated with the dropoff location for the Nash order |
| `{{nash_order_pickup_business_name}}` | The business name of the pickup location for the Nash order |
| `{{nash_order_dropoff_business_name}}` | The business name of the dropoff location for the Nash order |
| `{{nash_order_pickup_time}}` | The pickup time for the Nash order |
| `{{reference_id}}` | The order's reference ID |
| **Refund** | |
| `{{refund_request_id}}` | The refund request ID |
| `{{refund_request_status}}` | The refund request status |
| `{{refund_request_amount}}` | The refund request amount |
| `{{refund_request_reason}}` | The reason given for the refund request |
| **Misc** | |
| `{{request_id}}` | The ID of the request as generated by Nash's API service. Helpful for debugging purposes. |
## Opt contacts out of notifications through Nash
Nash maintains a communication "deny list" for contacts who have opted out of communications.
The deny list includes a type of communication (e.g., email or SMS) and the value for the type
(e.g., [customer@usenash.com](mailto:customer@usenash.com)). This way, your organization can have fine-grained control of who receives
notifications via the Nash platform and who doesn't.
# Remove Contact from the Deny List
Source: https://docs.usenash.com/api-reference/notifications/remove-a-contact-from-the-deny-list
delete /v1/organization-management/notifications/deny-list
Removes a contact from the deny list to opt the user back into communications.
# Create Optimization Strategy
Source: https://docs.usenash.com/api-reference/optimization-strategies/create-optimization-strategy
post /v1/optimization-strategies
Create a new optimization strategy for the authenticated organization. A strategy configures how orders are grouped into routes (service times, pickup-time windows, contracts, store locations, zones, and optional cron schedules).
# Delete Optimization Strategy
Source: https://docs.usenash.com/api-reference/optimization-strategies/delete-optimization-strategy
delete /v1/optimization-strategies/{id}
Soft-delete an optimization strategy. Fails if the strategy is referenced by an automation logic or an active automation rule.
# Get Optimization Strategy
Source: https://docs.usenash.com/api-reference/optimization-strategies/get-optimization-strategy
get /v1/optimization-strategies/{id}
Returns a single optimization strategy by id.
# List Optimization Strategies
Source: https://docs.usenash.com/api-reference/optimization-strategies/list-optimization-strategies
get /v1/optimization-strategies
Returns all optimization strategies for the authenticated organization (including soft-deleted).
# Update Optimization Strategy
Source: https://docs.usenash.com/api-reference/optimization-strategies/update-optimization-strategy
patch /v1/optimization-strategies/{id}
Update an existing optimization strategy. Only provided fields are updated. Supplying `optimization_schedules` replaces the existing schedules.
# Add Custom Event to Order
Source: https://docs.usenash.com/api-reference/order/add-custom-event-to-order
post /v1/order/{id}/events
Adds a customizable event to an existing Nash order. Custom order events will be shown in the order events page alongside other Nash-generated events.
### Limits
* **Rate limits:** Max 3 requests/second
* **Size limit:** Request body must not exceed 1kb
# Archive order
Source: https://docs.usenash.com/api-reference/order/archive-order
delete /v1/order/{id}
Archive (soft-delete) an order. Archived orders are hidden from default listings but can be restored using the unarchive endpoint.
Nash never archives an order on a timer. An order you don't dispatch keeps its status until you archive it here or in the Portal, or until a cancellation from a connected demand source such as Shopify or Toast archives it for you. Leaving it active has one practical cost: its quotes carry an `expireTime`, and dispatching with an expired quote returns `400`. [Refresh Quotes](/api-reference/order/refresh-order-quotes) before you dispatch, or let auto-dispatch fetch new quotes on its own. Archived orders can be restored with [Unarchive Order](/api-reference/order/unarchive-order).
# Autodispatch Order
Source: https://docs.usenash.com/api-reference/order/autodispatch-order
post /v1/order/{id}/autodispatch
Automatically select a quote based on the order's dispatch strategy.
This is similar to [Autodispatch Job](/api-reference/job/autodispatch-job)
Autodispatch selects a quote for you instead of you choosing one by hand. Set the selection rules up front by creating a [dispatch strategy](/api-reference/dispatch-strategies/dispatch-strategy) and attaching it to the order; Nash then picks the winning quote when you call this endpoint.
This fits a checkout flow. You create the order and show the customer their delivery options while the order sits unfulfilled. When the customer places the order, you call this endpoint instead of selecting a quote yourself, and the order goes out under the dispatch strategy you configured.
## Autodispatch preferred quote
When you call `/autodispatch`, Nash selects the quote tagged `autodispatch_preferred_quote`. Read that tag ahead of time to know which price to show the customer during checkout: take the full list at `order["quotes"]` and filter for the one with `"autodispatch_preferred_quote"` in `quote.tags`.
Keep `Enable Autodispatch` switched off, so the order isn't dispatched the moment it's created.
# Confirm order
Source: https://docs.usenash.com/api-reference/order/confirm-order
patch /v1/order/{id}/confirm
Confirm a valid order, changing its status from 'valid' to 'confirmed'.
# Create or Update Order by External Identifier
Source: https://docs.usenash.com/api-reference/order/create-or-update-order-by-external-identifier
post /v1/order/external-identifier/{externalId}
Create an order or update an existing one, keyed by your own external identifier.
Create an order or update an existing one by your own identifier. This suits checkout workflows: create the order from the customer's cart or order ID, then update it once the customer completes checkout.
If two requests arrive with the same `externalId`, Nash blocks the second until the first has been committed to the database and its response returned to the client. The second request is then processed as an update, since the first already created the order. Only one order is created per `externalId`, even when requests are made simultaneously.
Updating an archived order will also unarchive it.
If `quotes_only` is included in the `tags` field, the response will include quotes for the order.
# Create or Update Orders by External Identifier
Source: https://docs.usenash.com/api-reference/order/create-or-update-orders-by-external-identifier-v2
post /v2/orders/upsert/bulk
Create or update multiple orders by external identifier in Nash
Create or update multiple orders in one call, keyed by your own external identifiers. Use it for batch work: importing orders from an external system, or updating many orders at once by their external IDs.
Create or Update Orders by External Identifier behavior: For each order with
an `external_id`, Nash will check if an order with that external ID already
exists. If it exists, the order will be updated; if not, a new order will be
created. A hard failure on one order does not fail the batch; the rest still
commit. The response returns a `results` array (one entry per input order, in
the same order as the request) with `success` or `failed` status, plus a
`summary` of the counts.
Updating archived orders will also unarchive them.
Common uses:
* Bulk importing orders from external systems
* Synchronizing order data between systems
* Batch updating multiple orders by external identifier
# Create Order
Source: https://docs.usenash.com/api-reference/order/create-order
post /v1/order
Create an order in Nash
See [Order Address Validation](/api-reference/order/order-address-validation-geocoding) for
more info on how Nash validates addresses and how you can use your own geocoding service.
# Create Orders
Source: https://docs.usenash.com/api-reference/order/create-orders
post /v1/orders
Create multiple pre-dispatch orders in a single request. Each order is created independently with its own pickup and dropoff details.
# Bulk Order Actions
Source: https://docs.usenash.com/api-reference/order/create-update-upsert-or-delete-orders-asynchronously-in-bulk
post /v1/orders/bulk
Create, update, upsert, or delete orders asynchronously in bulk.
The **upsert** (update or insert) action will update existing orders that match the provided `externalId`, and
create new orders for those that don't.
The **delete** action will archive the order, akin to the [Archive Order endpoint](/api-reference/order/archive-order).
Status field returned is for the bulk operation, not the individual orders and can be one of the following:
* `QUEUED`: The request has been received and is waiting to be processed in the bulk operation queue
* `IN_PROGRESS`: The request is currently being worked on
* `SUCCESS`: The request was successfully processed without any errors to the overall async process (individual orders might have failed)
* `FAILED`: The request encountered an error and could not be completed
* `CANCELLED`: The request processing was manually cancelled or terminated before completion
* `SKIPPED`: The request was intentionally not processed due to validation issues or business rules
* `SCHEDULED`: The bulk action is set to be processed at a future specified time (n/a)
Bulk order actions are asynchronous operations. You can either poll the [GET endpoint](/api-reference/order/get-response-from-asynchronous-bulk-order-operation)
using the returned `id`, and/or opt-in to receive the results via webhook. The webhook event is
`order.bulk_operation.completed` and will be sent once the bulk operation is complete.
# Get order
Source: https://docs.usenash.com/api-reference/order/get-order
get /v1/order/{id}
Retrieve a single order by ID, including its pickup/dropoff details, status, and metadata.
# Get Order Documents
Source: https://docs.usenash.com/api-reference/order/get-order-documents
get /v1/order/{id}/documents
Get the documents for an order
# Create Quote
Source: https://docs.usenash.com/api-reference/order/get-order-quotes
post /v1/order/get_quotes
Create a new quote for an order
This endpoint always creates a new order from the payload you send, tags it `quotes_only`, and returns it with its provider quotes. It doesn't look up an existing order, even when the payload carries an `externalId` you've used before; if that `externalId` already belongs to another order, the request fails with `400 VALIDATION_ERRORS`. To get fresh quotes for an order that already exists, call [Refresh Quotes](/api-reference/order/refresh-order-quotes) with its `orderId` or `externalOrderId`. See [Choose an order creation flow](/api-reference/order/order#choose-an-order-creation-flow) for how the quote-first flow fits together.
# Get Bulk Order Response
Source: https://docs.usenash.com/api-reference/order/get-response-from-asynchronous-bulk-order-operation
get /v1/orders/bulk/{id}
Get the response from an asynchronous bulk order operation.
# List/Search Orders
Source: https://docs.usenash.com/api-reference/order/listsearch-orders
get /v1/orders
List orders with filters, sorting, and pagination. Returns a slim payload suitable for discovery; use GET /v1/order/ for full details.
# Orders overview
Source: https://docs.usenash.com/api-reference/order/order
What an order is, how to choose an order creation flow, and how orders relate to jobs (deliveries).
An Order represents the delivery details for package(s) in the real world. It's the most flexible way to initiate deliveries in Nash.
Orders enable you to:
* Update the delivery details before a delivery is created (contact details, addresses, times, and requirements) *e.g. E-Commerce Checkout Experience*
* Understand whether the order meets the criteria to become a delivery (validation) *e.g. Error handling as early as possible*
* Batch orders through Nash Platform (not optimized or batched on your end) *e.g. Optimizing catering routes*
When one or more Orders are dispatched, they become a Job.
## Choose an order creation flow
The order endpoints return similar response objects, but they create, validate, quote, and dispatch Orders differently.
### Recommended checkout flow
For most checkout integrations, start with [Create Quote](/api-reference/order/get-order-quotes), then update the returned Order after checkout. This separates the eligibility and price check from the final fulfillment details.
Send the details needed to evaluate delivery eligibility and price. Create Quote always starts a new Order; it does not look up an existing Order by `externalId`. If order-level validation fails, Nash returns `400 VALIDATION_ERRORS` and does not save the new Order. A successful response includes the saved Order, its provider quotes, and the `quotes_only` tag.
Call [Update Order](/api-reference/order/update-order) with the returned Order ID. If the Create Quote request included `externalId`, you can use that identifier in the same update path instead. Send the final fulfillment details and the complete `tags` list without `quotes_only`. Nash runs full order validation. If the Order is valid, it can auto-dispatch when its dispatch strategy has auto-dispatch enabled.
Using `externalId` in the Update Order path does not turn it into an upsert. The Order must already exist. Update Order refreshes an existing quote set after the update. [Create or Update by External Identifier](/api-reference/order/create-or-update-order-by-external-identifier) creates the Order when no match exists and requests fresh quotes on each call while `quotes_only` remains.
Use a different creation flow when you do not need quotes before saving an Order or need one request that creates or updates by your own identifier.
| Flow | Use when | Behavior |
| - | - | - |
| [Create Order](/api-reference/order/create-order) | You need a new Order without quotes in the same call. | Runs order-creation workflows and can auto-dispatch a valid Order. |
| [Create Quote](/api-reference/order/get-order-quotes) | You need to check delivery eligibility and price before checkout. | Always attempts a new Order; it does not look up `externalId`. On success, it saves the Order, requests quotes, and adds `quotes_only`. |
| [Create or Update by External Identifier](/api-reference/order/create-or-update-order-by-external-identifier) | You need one request to create the Order when your identifier is new and update it when it already exists. | Creates once, then updates. With `quotes_only`, each request replaces the previous quotes. |
`quotes_only` is persistent Order state, not a response option. When an Order created by external identifier remains tagged `quotes_only`, each later create-or-update request asks providers for fresh quotes. Omitting `tags` preserves the existing tag. To return the Order to its normal dispatch flow, send the complete tags list without `quotes_only`.
### Validation behavior
Create Order and Create Quote use normal order-creation validation when `quotes_only` is absent from the request. Create Quote adds the tag only after it validates the Order and requests quotes. If `externalId` already belongs to another Order, Create Quote returns `400 VALIDATION_ERRORS` and does not save the new attempt. Later updates use quote-specific validation while the tag remains, so Nash can price an Order that has enough information for quoting but is not ready for fulfillment.
| Flow | Response | Order state |
| - | - | - |
| Create Order | `200` with `validationErrors` | Saved as `needs_attention`. |
| Create Quote | `400 VALIDATION_ERRORS` | Not saved. |
| Create or Update by External Identifier | Without `quotes_only`: `200` with saved errors. With it: quote generation can return `400`. | The Order may already exist after a `400`, so retrieve it by external identifier before retrying. |
The outcomes in the table describe order-level validation, which runs after Nash accepts the request body. Request-shape errors, such as a field with the wrong type, return `422 VALIDATION_ERRORS` before the Order is created or updated.
When moving from checkout to dispatch, update the Order with the remaining fulfillment fields and remove `quotes_only` before relying on normal validation or auto-dispatch.
## Order data model
The figure here shows where an order goes. When it is dispatched, an order becomes a job. The job has a job configuration, which holds the package and one or more tasks. Each task is one attempt at the delivery, with its own quotes and, once a quote is selected, its own delivery. See [Jobs overview](/api-reference/job/jobs) for the path to each part.
**Order status**
* `needs_attention`: The order requires attention due to some data issues (e.g. invalid address)
* `valid`: The order is valid and is ready to be dispatched to a Nash Job & Delivery.
* `dispatched`: The order has been dispatched and a job (delivery) created for it. Open the job from the **view dispatched job** action in the Portal.
* `archived`: The order has been archived and is moved to the archived tab in the Nash Portal.
Note: All timestamps are in UTC and in this format `%Y-%m-%dT%H:%M:%S.%f`
```json theme={"dark"}
{
"id": "ord_PewiSWzxmCFPzW62Ry3A6V",
"createdAt": "2024-01-25T21:10:43.722214",
"externalId": null,
"externalGroupId": null,
"referenceId": null,
"portalUrl": "https://portal.sandbox.usenash.com/orders/ord_PewiSWzxmCFPzW62Ry3A6V",
"publicTrackingUrl": "https://tracking.sandbox.usenash.com/ord_PewiSWzxmCFPzW62Ry3A6V",
"status": "valid",
"tags": null,
"orderMetadata": null,
"pickupAddress": null,
"pickupPhoneNumber": "+15005550005",
"pickupFirstName": "pickupFName",
"pickupLastName": "pickupLName",
"pickupBusinessName": "pickupBusinessName",
"pickupInstructions": "Ask for the manager at the counter",
"pickupBarcodes": null,
"dropoffAddress": "401 San Antonio Rd, Mountain View, CA 94040",
"dropoffPhoneNumber": "+15005550006",
"dropoffFirstName": "dropoffFName",
"dropoffLastName": "dropoffLName",
"dropoffBusinessName": "dropoffBusinessName",
"dropoffInstructions": "Leave the package at the front desk",
"dropoffBarcodes": null,
"deliveryMode": "now",
"pickupStartTime": null,
"pickupEndTime": null,
"dropoffStartTime": null,
"dropoffEndTime": null,
"description": "Test package description",
"itemsCount": null,
"requirements": ["photo_proof_of_delivery"],
"minimumVehicleSize": null,
"currency": null,
"documents": [
{
"id": "doc_ELre5GCELQ6w3UHKAgbm6F",
"type": "shipping_label",
"url": "https://nash-documents.s3-us-west-1.amazonaws.com/dlv_bR7NWwhWSQRohyjs7UZd7D_fEavuZda787M70sJEhtCAA==.png",
"data": null,
"contentType": "image/png"
},
{
"id": "doc_nuKEL5W8mMgnXuRrFqwwsc",
"type": "pickup_label",
"url": "https://nash-documents.s3-us-west-1.amazonaws.com/dlv_bR7NWwhWSQRohyjs7UZd7D_QPiARbKwfJRkC94KVQrT8w==.png",
"data": null,
"contentType": "image/png"
}
],
"valueCents": "5000",
"tipAmountCents": "100",
"width": null,
"depth": null,
"height": null,
"volume": null,
"weight": null,
"dispatchStrategyId": null,
"validationErrors": {}
}
```
# Order address validation (geocoding)
Source: https://docs.usenash.com/api-reference/order/order-address-validation-geocoding
How Nash validates addresses, and how to bring your own address components instead.
By default, Nash validates an address on every request when passed a pickup or dropoff address or Google Place ID. When provided with a raw address, Nash uses several geocoding services and selects the most appropriate result based on the region of the address. Once geocoded, Nash will populate the componentized pickup/dropoff address in the corresponding component fields, which will be returned in the API response, providing you with structured and standardized address data.
### Bypassing Nash's address validation
If you prefer to use your own geocoding service, you can bypass Nash's address validation by passing the componentized address fields directly. To do this, provide the following fields **instead of** the `Address` or `PlaceId` when creating or updating an order.
**Pickup**
* `pickupAddressNumber`
* `pickupAddressSecondarynumber`
* `pickupAddressFormattedStreet`\*
* `pickupAddressCity`\*
* `pickupAddressCounty`
* `pickupAddressState`
* `pickupAddressZip`\*
* `pickupAddressCountry`\*
* `pickupLat`\*
* `pickupLng`\*
**Dropoff**
* `dropoffAddressNumber`
* `dropoffAddressSecondarynumber`
* `dropoffAddressFormattedStreet`\*
* `dropoffAddressCity`\*
* `dropoffAddressCounty`
* `dropoffAddressState`
* `dropoffAddressZip`\*
* `dropoffAddressCountry`\*
* `dropoffLat`\*
* `dropoffLng`\*
*\* = Required components of the address if using address components*
When using componentized address fields, do not pass `Address` or `PlaceId` in the request to create or update an order.
### Using a store location as a pickup or dropoff point
As an alternative to providing an address, address components, or a Google Place ID, you can specify a store location as the pickup or dropoff point for an order. To do this, pass the Store Location ID (starting with `stl_`) or the Store Location External ID in the request when creating or updating an order.
When a Store Location ID or External ID is provided without any accompanying address information, Nash will automatically populate the pickup or dropoff address fields using the address associated with the specified store location. This eliminates the need to manually input the address details for frequently used store locations.
If a Store Location ID or External ID is provided along with address information, the address information (including name, phone number, email, instructions) will take precedence over the store location's address.
# Order requirements
Source: https://docs.usenash.com/api-reference/order/order-requirements
Requirements you can attach to a Nash order, such as proof of delivery, age verification, and barcode scans.
When creating a Nash order, Nash allows specifying different requirements to suit your order/package delivery.
Available options are:
| Requirement | Description |
| - | - |
| `photo_proof_of_delivery` | Require a picture of the order after drop off |
| `signature_proof_of_delivery` | Require the customer's signature after drop off |
| `photo_proof_of_pickup` | Require a picture of the order after picking it up |
| `signature_proof_of_pickup` | Require the customer's signature after picking up the order |
| `age_verification_on_delivery` | Requires driver to verify recipient's age from a government-issued ID.
minimum\_age depends on country: US: 21 AU: 18 NZ: 18 GB: 18 CA: 18 |
| `alcohol` | Lets the provider know there's alcohol in the order and requires driver to verify recipient's age from a government-issued ID.
minimum\_age depends on country: US: 21 AU: 18 NZ: 18 GB: 18 CA: 18 |
| `id_verification_on_delivery` | Requires driver to verify the recipient's ID when dropping off the order (for pharmaceutical delivery) |
| `barcode_scan_on_pickup` | Requires driver to scan a barcode on pickup. Barcode values are provided in `pickupBarcodes` |
| `barcode_scan_on_dropoff` | Requires driver to scan a barcode on dropoff. Barcode values are provided in `dropoffBarcodes` |
| `barcode_scan_on_return` | Requires driver to scan a barcode when returning an order |
| `display_barcode_on_pickup` | Requires driver to display a barcode at pickup (for example, for store staff to scan). Barcode values are provided in `pickupBarcodes` |
| `meet_on_delivery` | Instructs driver to meet customer on delivery and hand off order. Note: Supported by select providers only, and best effort rather than guaranteed. |
| `shipping_label` | Requires driver to check or scan the shipping label at the pickup and drop-off location.
Note: The shipping label sometimes should be printed and stamped to the order depending on the provider. |
| `two_person_team` | Requires a two-person team to assist in the delivery |
| `pincode_verification` | Requires a pincode entry at dropoff. The pincode will be shared with the dropoff customer through SMS.
Note: The pincode will also be added to the proofOfDelivery object and will trigger a webhook event of type `delivery.pincode_verification`. example: `"proofOfDelivery": [{"type": "pincode_verification","pincode": "1578"}]` |
| `parking_check_in` | Requires courier to check in upon arrival while waiting in their vehicle. The courier will provide the parking location information.
Note: The parking location can be found in the `delivery.courier_vehicle.parking_location` field. |
| `schedule_ii_controlled_substances` | Require the order to be handled by a provider that is certified to handle Schedule II Controlled Substances (US) |
| `tobacco` | Lets the provider know there's tobacco in the order and requires driver to verify recipient's age from a government-issued ID.
minimum\_age depends on country: US: 21 AU: 18 NZ: 18 GB: 18 CA: 18 |
| `vapes` | Lets the provider know there are vape products in the order |
| `pick_and_pack` | Enables item-level fulfillment with store picking before delivery. See [Pick and Pack](/api-reference/store-catalog/pick-and-pack). |
| `custom:{requirement_name}` | Custom requirement that will filter any provider/contract without this specific requirement. The requirement must be added to the wanted contract before being used. Example: "custom:refrigerated" will only match providers that explicitly support refrigerated transport. |
### Notes
**Multiple requirements are allowed.**
Example:
`requirements: ["photo_proof_of_delivery", "signature_proof_of_delivery"]`
**Not all providers support all requirements.** Providers that don't support provided order/package requirements will be ineligible and will show in the failedQuotes section. A quote rejected for that reason carries a machine-readable `errorCode` and an `errorDetails` object naming the requirements the provider wouldn't take, so you can branch on the cause instead of reading the message. When *every* eligible provider rejects a package on its requirements, the delivery carries a `quoteFailure` summary and a normalized `failureCode`, and dispatching it returns a `422` rather than a generic failure.
By default, `requirements: ["photo_proof_of_delivery"]`
`requirements=[]` means no requirements are expected.
# Order validations
Source: https://docs.usenash.com/api-reference/order/order-validations
The validations Nash applies when you create an order, covering addresses, required fields, data formats, enums, and datetimes.
When you create a Nash order, these validations run before the order is accepted.
### Address & location validation
* **Address input exclusivity**:
* An error is raised if you provide both `pickupAddress` and `pickupPlaceId` (or the dropoff equivalents). You must use one or the other.
* An error is raised if you provide address components (e.g., `pickupAddressFormattedStreet`) along with `pickupAddress` or `pickupPlaceId`.
* **Address input requirement**: If no store location ID is provided, Nash requires one of the following for both pickup and dropoff:
1. A single-line address string (`pickupAddress` / `dropoffAddress`).
2. A Google Place ID (`pickupPlaceId` / `dropoffPlaceId`).
3. A set of address components (street, city, zip, etc.).
* If none of these are present, a "required field" error is added for the address.
* **Store location logic**:
* If a `pickupStoreLocationId` or `pickupExternalStoreLocationId` is provided, Nash looks up the corresponding store location.
* **Existence check**: Fails if the store location ID doesn't exist for that organization, or if the store has been deleted.
* **Automatic store creation**: If an external store location ID is given but not found, Nash checks an organization preference for automatically creating store locations. If enabled, it creates a new store location from the order's pickup details.
* **Default field population**: If a valid pickup store location is found, Nash populates the order's pickup fields (name, phone, email, instructions) from the store, unless those fields were already provided in the request.
* **Geocoding and parsing**:
* If a raw `pickupAddress` or `dropoffAddress` string is provided, Nash parses and geocodes it to get coordinates (latitude/longitude). Parsing failure raises an error (for example, "Address not found").
* Nash performs the same parsing and geocoding for `pickupPlaceId` and `dropoffPlaceId`.
* **Identical location check**:
* Nash raises an error if `pickupPlaceId` and `dropoffPlaceId` are the same.
* After parsing, Nash compares the final parsed pickup and dropoff address components and raises an error if they're identical.
* **Cross-country check**: Fails if the parsed `pickupAddressCountry` and `dropoffAddressCountry` are not the same.
* **Distance check**:
* Nash calculates the straight-line distance between the pickup and dropoff coordinates.
* That distance is checked against an organization-specific maximum delivery distance preference, and fails if it exceeds the maximum.
### Required & conditional fields
* **Core required fields**: Checks that a value is present for: `valueCents`, `pickupPhoneNumber`, `dropoffPhoneNumber`, `deliveryMode`.
* **Contact name requirement**:
* Requires either `pickupFirstName` or `pickupBusinessName` to be present.
* Requires either `dropoffFirstName` or `dropoffBusinessName` to be present.
### Data type & format validation
* **Integer fields**: Verifies that `valueCents`, `tipAmountCents`, and `itemsCount` are valid integers.
* **Float/decimal fields**: Verifies that dimensional fields (`weight`, `height`, `width`, `depth`, `volume`) are valid decimal numbers and are greater than 0.
* **Units**: Nash stores weight in kilograms and dimensions in centimeters. Send those units and you don't need to say so. To send imperial values instead, declare the unit and Nash converts on ingest:
* `weightUnit` accepts `lb` or `lbs` (case-insensitive) and converts `weight` to kilograms. Omitting it, or sending `kg`, stores the value as given. Any other value is rejected with `ORDER_INVALID_WEIGHT_UNIT`.
* `lengthUnit` accepts `in`, `inch`, or `inches` (case-insensitive) and converts `height`, `width`, and `depth` to centimeters. `volume` is read as cubic inches and converted to cubic centimeters, since one unit covers all four fields. Omitting it, or sending `cm`, stores the values as given. Any other value is rejected with `ORDER_INVALID_LENGTH_UNIT`.
* Declaring a unit without the matching value is harmless — `weightUnit` with no `weight` converts nothing.
* **Order value**: Checks that `valueCents` is greater than 0.
* **Item count**: Checks that `itemsCount`, if provided, is at least 1.
* **Name fields**: Checks that all name fields (`pickupFirstName`, etc.) are strings and do not exceed a maximum length (80 characters).
* **Phone numbers**:
* Checks that `pickupPhoneNumber` and `dropoffPhoneNumber` are valid phone numbers for the corresponding country.
* If the number is invalid but the organization has a default backup phone number configured, Nash uses the backup number instead of failing.
* **Items**: If the `items` field is provided, Nash validates that each entry in the list is a valid item structure.
* **Order metadata**:
* Nash validates that `orderMetadata` has no more than 15 key-value pairs, and that all values are simple scalar types (string, int, float, bool).
### Enum & ID validation
* **Delivery mode**:
* Ensures `deliveryMode` is one of the allowed values (`now` or `scheduled`).
* If mode is `now`, Nash nullifies all datetime fields.
* If mode is `scheduled`, Nash requires at least one of the datetime fields (`pickupStartTime`, etc.) to be set.
* **Currency**:
* If a `currency` is provided, Nash checks it against the list of supported currencies.
* If no currency is provided, Nash infers it from the `pickupAddressCountry`.
* **Requirements**: Ensures all provided `requirements` are supported (see [Order requirements](/api-reference/order/order-requirements)), unless it's a `custom:` requirement.
* **Vehicle size**: Ensures `minimumVehicleSize` is one of the supported vehicle sizes.
* **Dispatch strategy**: If `dispatchStrategyId` is provided, Nash confirms it exists, belongs to the organization, and isn't deleted.
* **Delivery window**: If `deliveryWindowId` is provided, Nash checks that it's a valid, active window.
* **External ID uniqueness**: If an `externalId` is given, Nash ensures no other order in the same organization already uses that ID.
### Datetime validation
* **Parsing**: Parses all provided datetime strings (`pickupStartTime`, `pickupEndTime`, `dropoffStartTime`, `dropoffEndTime`) into datetime objects, handling both UTC and timezone-localized inputs. Fails if a string is not a valid date format.
* **Chronological order**:
* `pickupStartTime` must be earlier than `pickupEndTime`.
* `dropoffStartTime` must be earlier than `dropoffEndTime`.
* `pickupStartTime` must be earlier than `dropoffEndTime`.
* `pickupEndTime` must be earlier than `dropoffEndTime`.
# Refresh order quotes
Source: https://docs.usenash.com/api-reference/order/refresh-order-quotes
post /v1/order/refresh_quotes
Request fresh price quotes from delivery providers for an existing order. Replaces any existing quotes with updated pricing from available providers.
# Select Quote
Source: https://docs.usenash.com/api-reference/order/select-quote
post /v1/select_quote
Select the quote you would like to execute from a specific order.
# Unarchive order
Source: https://docs.usenash.com/api-reference/order/unarchive-order
patch /v1/order/{id}/unarchive
Restore a previously archived order, making it visible in default listings again.
Unarchives a specific order, changing its status to either `valid` or `needs_attention` based on validation results.
An order that has been dispatched to a job cannot be unarchived.
# Undispatch an order
Source: https://docs.usenash.com/api-reference/order/undispatch-an-order
post /v1/orders/external-identifier/{external_id}/undispatch
Un-dispatch a route-based order: cancel its active task and provider delivery, remove it from its route (re-syncing the linked batch), and revert it to the `valid` status. Returns the updated order. Errors if the order is not dispatched, or is not associated with a route.
# Undispatch an order by id
Source: https://docs.usenash.com/api-reference/order/undispatch-an-order-by-id
post /v1/orders/{order_id}/undispatch
Un-dispatch a route-based order by id: cancel its active task and provider delivery, remove it from its route (re-syncing the linked batch), and revert it to the `valid` status. Returns the updated order. Errors if the order is not dispatched, or is not associated with a route.
# Update order
Source: https://docs.usenash.com/api-reference/order/update-order
patch /v1/order/{id}
Update an existing pre-dispatch order's details such as addresses, times, or metadata. Only provided fields are updated.
Updating an archived order will also unarchive it.
# Upsert order by external identifier with delivery window
Source: https://docs.usenash.com/api-reference/order/upsert-order-by-external-identifier-with-delivery-window
post /v1/order/external-identifier/{externalId}/delivery-window
Create or update an order by external identifier and associate it with a delivery window. The order is linked to the specified delivery window for scheduled delivery.
# Get Providers
Source: https://docs.usenash.com/api-reference/provider/get-providers
get /v1/providers
List all delivery providers configured for your organization, including their status and capabilities.
# Assign or Reassign a Route
Source: https://docs.usenash.com/api-reference/route/assign-route
post /v1/routes/{id}/assign
Assign a route to a provider, driver group, courier, or vehicle — before or after dispatch.
Use this endpoint to set (or change) who fulfills a route. You can target any of these:
* **Provider** (`providerId`) — an external delivery provider (e.g. `uber`). Selects the route's successful quote for that provider.
* **Driver group** (`driverGroupId`) — a contract. Selects the route's successful quote for that contract.
* **Courier** (`courierId`) — an individual driver. Must belong to the route's driver group.
* **Vehicle** (`vehicleId`) — a vehicle. Must be assigned to the route's driver group (contract).
`driverGroupId`, `courierId`, and `vehicleId` accept either an internal Nash id or your own external id. Provide at least one target; you may combine a courier and a vehicle.
## Pre-dispatch vs. post-dispatch
The endpoint behaves differently depending on whether the route has already been dispatched:
* **Pre-dispatch** — the route's assignment is updated in place.
* **Post-dispatch** — the active assignment is cancelled, live deliveries are cancelled, tasks are recreated and re-quoted, a fresh route assignment is created, and the requested target is applied.
In both cases the updated route is returned.
## Requirements
* Provide at least one target (`providerId`, `driverGroupId`, `courierId`, or `vehicleId`).
* Assigning by **provider** or **driver group** selects that target's successful quote on the route — a successful quote for it must already exist.
* A **courier** must belong to the route's driver group.
* A **vehicle** must be assigned to the route's driver group (contract).
## Errors
Failed assignments return `400 Bad Request` with a body of shape `{ "error": { "code", "message", "details": { "reason" } }, "response_status", "RequestID" }`. Common reasons:
| `reason` | Meaning |
| - | - |
| `courier_not_assigned_to_driver_group` | The courier isn't a member of the route's driver group. Move the route to a group the courier belongs to, or add the courier to that group. |
| `vehicle_not_assigned_to_contract` | The vehicle isn't assigned to the route's driver group (contract). |
A request with no target fails validation with `422 Unprocessable Content`.
To assign a route using your own identifier instead of the Nash route id, use [Assign or Reassign a Route by external id](/api-reference/route/assign-route-by-external-identifier).
# Calculate Route Timing
Source: https://docs.usenash.com/api-reference/route/calculate-route-timing
post /v1/routes/{id}/calculate-timing
Calculate per-stop ETAs and polyline for a route using HERE truck routing. Updates arrival/departure times for each stop and returns the route polyline.
# Create or Update Route
Source: https://docs.usenash.com/api-reference/route/create-or-update-route
post /v1/routes
Create or update a route in Nash
A `Route` object represents a planned sequence of stops for a vehicle or delivery agent.
The core of the `Route` is the `stops` array, which lists the specific locations to visit in order. Each stop within this array details:
* The actions to be performed (like `stopType`).
* Associated items (`objectIds`).
* Timing estimates (`arrivalTime`, `departTime`, `serviceTime`).
* Travel details from the prior stop (`distanceFromPrevious`, `durationFromPrevious`).
* The physical coordinates (`location`).
Create a new route or update an existing one by providing the route configuration and its stop details.
# Dispatch Optimized Orders
Source: https://docs.usenash.com/api-reference/route/dispatch-optimized-orders
post /v1/activities/{id}/dispatch
Dispatch orders from an optimization activity to delivery providers. Creates jobs and assigns them to providers based on the optimized routes.
# Dispatch Routes
Source: https://docs.usenash.com/api-reference/route/dispatch-routes
post /v1/routes/dispatch
Dispatch one or more routes to delivery providers. Creates jobs for each route and assigns them based on the organization's dispatch strategy.
# Find Nearby Couriers
Source: https://docs.usenash.com/api-reference/route/find-nearby-couriers
get /v1/routes/nearby-couriers
Find the drivers on active routes nearest to a point.
Use this when a route needs help: a van has broken down, or a driver is running out of hours, and you need to know who else is close. Send the point as `lat` and `lng`, and Nash returns the nearest drivers on routes that are assigned, started, or in progress, nearest first, each with their route and their distance from the point in meters.
Distance is a straight line from each driver's latest GPS position, not a driving distance. A driver with no recorded position is left out, and so is one whose position is older than `max_location_age_minutes` (30 by default). Narrow the search to one depot with `store_location_id`, and leave out the route you're rescuing, and its drivers, with `exclude_route_ids`.
Unlike most of the API, this endpoint's query parameters and response fields are snake\_case.
# Get Route
Source: https://docs.usenash.com/api-reference/route/get-route
get /v1/routes/{id}
Retrieve a route by ID, including its stops, travel metrics, and associated orders.
A `Route` object represents a planned sequence of stops for a vehicle or delivery agent.
The core of the `Route` is the `stops` array, which lists the specific locations to visit in order. Each stop within this array details:
* The actions to be performed (like `stopType`).
* Associated items (`objectIds`).
* Timing estimates (`arrivalTime`, `departTime`, `serviceTime`).
* Travel details from the prior stop (`distanceFromPrevious`, `durationFromPrevious`).
* The physical coordinates (`location`).
**Planned times, not live telemetry.** `arrivalTime`, `departTime`, `serviceTime`, `distanceFromPrevious`, and `durationFromPrevious` are the values stored with the route. Nash fills them in when it plans the route for you or when you call [Calculate Route Timing](/api-reference/route/calculate-route-timing); a route you build by hand carries whatever you supplied at creation. This endpoint reads them back and doesn't recompute them. To follow what the driver actually did, read each stop's `status` and `statusHistory`, which record every transition (`ENROUTE`, `ARRIVED`, `IN_PROGRESS`, `COMPLETED`, and the failure states) with a timestamp.
# List Routes
Source: https://docs.usenash.com/api-reference/route/list-routes
get /v1/routes
List and search routes with filters and pagination.
Use this to find routes — by date, status, driver, or free-text search — when you have no route ID to start from. The payload is deliberately slim: enough to identify a route and show it in a list, not the whole plan. Read [Get Route](/api-reference/route/get-route) once you know which one you want.
`startDate` defaults to 30 days ago, so a call with no date range returns the recent past rather than everything you have ever run.
# Save Optimization Routes
Source: https://docs.usenash.com/api-reference/route/save-optimization-routes
post /v1/activities/{id}/save-routes
Save the optimized routes from an optimization activity. Creates route records with stops and travel metrics based on the optimization results.
# Create a shift
Source: https://docs.usenash.com/api-reference/shifts/create-a-shift
post /v1/shift
Create a courier shift with start/end times and an assigned store location.
# Get a shift
Source: https://docs.usenash.com/api-reference/shifts/get-a-shift
get /v1/shift/{id}
Retrieve a single courier shift by ID, including its times, assigned courier, and store location.
# Get all shifts
Source: https://docs.usenash.com/api-reference/shifts/get-all-shifts
get /v1/shifts
List courier shifts with optional filtering by time range and store location. Filter by start_time, end_time, store_location_id, or external_store_location_id.
# Upsert shifts
Source: https://docs.usenash.com/api-reference/shifts/upsert-shifts
post /v1/shifts/upsert
Create or update multiple courier shifts in a single request. Existing shifts are matched by ID for updates; new shifts are created.
# Dispatch and Manifest Orders
Source: https://docs.usenash.com/api-reference/shipping/dispatch-and-manifest-orders
post /v1/dispatch-and-manifest
Dispatch a batch of orders and manifest them with the carrier in one call, with per-shipment failures reported rather than the batch refused.
When a carrier rejects a batch as a whole, Nash doesn't leave every order in it stranded. Each delivery is noted as resubmitted individually and sent again one shipment at a time, so a single shipment the carrier won't take — a bad receiver contact, an instruction it can't parse — fails alone. Each such failure is its own entry in `errors[]`, with the order's `identifier` and `orderId`, `stage: "manifest"`, and the carrier's own message, while `manifests[].delivery_ids` lists only the shipments that were accepted.
A shipment that fails stays dispatched and untagged, so a later call retries it once you've fixed the order. If nothing in the batch gets through, the original batch error is returned. The single-purpose [Manifest Orders](/api-reference/shipping/manifest-orders) call keeps its all-or-nothing behavior.
# Manifest Orders
Source: https://docs.usenash.com/api-reference/shipping/manifest-orders
post /v1/manifest
Create manifests for dispatched shipping orders. Groups orders by store location and provider, then generates manifest documents (e.g. AusPost order summaries).
# Create or update inventory
Source: https://docs.usenash.com/api-reference/store-catalog/create-or-update-inventory
post /v1/inventory
Create or update inventory levels for products at specific store locations. Rows referencing an unknown product or store location are skipped and returned in the same array with `errorCode`/`errorMessage` set; the rest of the batch still commits.
# Create or update products
Source: https://docs.usenash.com/api-reference/store-catalog/create-or-update-products
post /v1/products
Create new products or update existing ones in the store catalog. Products are matched by external identifier for upsert logic.
# Get inventory
Source: https://docs.usenash.com/api-reference/store-catalog/get-inventory
get /v1/inventory
List inventory levels with pagination. Filter by external store location ID or external product ID.
# Get products
Source: https://docs.usenash.com/api-reference/store-catalog/get-products
get /v1/products
List products in the store catalog with pagination and optional filtering. Filter by IDs, SKUs, text search, or semantic search.
# Pick and Pack overview
Source: https://docs.usenash.com/api-reference/store-catalog/pick-and-pack
How Pick and Pack works across orders, the product catalog, and store inventory.
Pick and Pack enables item-level fulfillment by coordinating products, store-level inventory, and delivery providers.
## Quick start
Define your master product data with names, images, categories, and identifiers
Track per-store availability, pricing, and aisle locations for efficient picking
Handle substitutions, track picking status, and manage item-level fulfillment
## Product catalog
Your product catalog serves as the master data source for all items across your stores. It defines the core product information that drives inventory management, order processing, and picking operations.
Products are managed in bulk and referenced throughout your fulfillment workflow by inventory and orders.
### Key fields
* **Required fields**: `name`, `categories[]`, `weight`, `dimensions{depth,height,width}`, `identifiers[{type,value}]`.
* **Optional fields**: `imageUrls[]`, `externalIdentifier`, `sku`, `description`, `attributes[]` (e.g., `WEIGHTED`), and `details` (`sizeSpecification`, `packSizeSpecification`, `weightedItemInfo`).
### Rate limits
* **10 requests per second** per organization
* **1,000 products** per request
### API endpoints
* **[Create/Update Products](/api-reference/store-catalog/create-or-update-products)**: `POST /v1/products` - Bulk create or update products
* **[List Products](/api-reference/store-catalog/get-products)**: `GET /v1/products` - Retrieve products with pagination
Ready-to-use sample payloads for common product types
## Store inventory
Store inventory tracks the real-time availability, pricing, and location of products within each of your store locations. This data powers accurate order fulfillment by ensuring pickers know exactly what's available and where to find it.
Each inventory entry links a product to a specific store location using your external identifiers, enabling precise per-store inventory management.
### Key fields
* **Required field**: `available` (boolean).
* **Common fields**: `externalProductId`, `externalStoreLocationId`, `quantity`, `valueCents`, `currency`, `location{aisle,bay,shelf}`.
### Weighted items
For variable-weight products (produce, deli items, etc.):
1. Set the product attribute to `WEIGHTED` in your product catalog
2. Set per-unit pricing on the store inventory entry's `details.weightedItemInfo.valueCentsPerMeasurementUnit` (e.g., price per pound)
### Rate limits
* **10 requests per second** per organization
* **10,000 inventory items** per request
### API endpoints
* **[Create/Update Inventory](/api-reference/store-catalog/create-or-update-inventory)**: `POST /v1/inventory` - Bulk inventory updates
* **[Get Inventory](/api-reference/store-catalog/get-inventory)**: `GET /v1/inventory` - Query inventory with filters:
* `externalStoreLocationId`: Filter by store location
* `externalProductId`: Filter by product
* Pagination: `pageIndex`, `numResultsPerPage`
## Pick and Pack orders
Orders with Pick and Pack enabled use your product catalog and store inventory to enable precise item-level fulfillment. The system tracks exactly what was requested, what was picked, and any substitutions made during the process.
To enable Pick and Pack functionality, include `pick_and_pack` in the `order.requirements` array when creating orders. See the complete list in [Order Requirements](/api-reference/order/order-requirements).
### Order lifecycle
1. **Order Creation**: Customer places order with specific items with substitution preferences
2. **Picking**: 3rd party courier picks items, handling substitutions as needed
3. **Status Updates**: Real-time updates on picking progress
4. **Delivery**: Order handed off to delivery provider once picking is complete
### Sub-item fields
* **`item.subItems[].sku`**: optional SKU for the specific sub-item being picked.
* **`item.subItems[].substitution`**: object describing substitution preferences and chosen replacements.
* **`preference`**: what to do when the item can't be picked — `refund`, `substitute`, or `contact`. With `contact`, the shopper reaches the customer to agree a replacement instead of deciding for them, so the order needs a contactable dropoff. Support for `contact` depends on the pick-and-pack provider.
* **`source`**: who made the decision (e.g., merchant, customer).
* **`substituteItems[]`**: list of `{ id, sku, quantity }` representing the chosen replacements.
### Status
* **`items_pick_complete`**: emitted when item picking for the order has finished and it is ready for handoff to delivery. Track this status via your order retrieval endpoints and/or subscribe to status change events in [Webhooks](/reference/webhooks).
### Picked items
Each delivery carries a `pickedItems` array describing what the picker actually put in the bag. The REST response always emits the same fields per item; values are nullable, but fields are never omitted.
| Field | Type | Notes |
| - | - | - |
| `id` | string \| null | Order line ID this pick fulfills. `null` when the SKU wasn't a pre-existing line (substitution). |
| `sku` | string | SKU of what was actually picked. |
| `name` | string | Display name of the picked item. |
| `status` | string | Per-item fulfillment outcome at pick time. One of `picked`, `partially_picked`, `not_picked`, `substituted`. |
| `quantity` | int | Units picked. |
| `requestedQuantity` | int \| null | Units originally requested. |
| `weight` | number \| null | Net weight in kilograms for random-weight goods. |
| `priceCents` | int | Line price in cents (already reflects `weight × unit price` for weighted items). |
| `requestedId` | string | ID of the original order line. |
| `requestedSku` | string | Originally requested SKU. Equals `sku` on straight picks. |
| `scannedBarcode` | string \| null | Raw scan at pick time. `null` for loose produce; 8/12/13 digits for EAN/UPC; ≥24 for GS1 in-store labels (GTIN + weight + price + use-by). |
| `scans` | array \| null | Per-scan detail for multi-scan items (e.g. a variable-weight item scanned multiple times). Each entry has its own parsed `barcodes[]` payload. |
A substitution is identifiable by `sku != requestedSku` (or equivalently, `id == null`).
#### Example — straight pick, packaged item
```json theme={"dark"}
{
"id": "11111111-1111-1111-1111-111111111111",
"sku": "100001",
"name": "Dried Fruit Snack 130g",
"status": "picked",
"quantity": 2,
"requestedQuantity": 2,
"weight": null,
"priceCents": 880,
"requestedId": "11111111-1111-1111-1111-111111111111",
"requestedSku": "100001",
"scannedBarcode": "1234567890128",
"scans": null
}
```
#### Example — straight pick, weighted item (single scan)
`weight` is populated; `scannedBarcode` is a GS1 in-store label encoding GTIN + weight + price. `scans` is `null` because the item was captured in a single scan.
```json theme={"dark"}
{
"id": "22222222-2222-2222-2222-222222222222",
"sku": "200002",
"name": "Free Range Chicken Tenderloins 450g-650g",
"status": "picked",
"quantity": 1,
"requestedQuantity": 1,
"weight": 0.608,
"priceCents": 1307,
"requestedId": "22222222-2222-2222-2222-222222222222",
"requestedSku": "200002",
"scannedBarcode": "0112345678901234152601013103000608392200130700",
"scans": null
}
```
#### Example — substitution
```json theme={"dark"}
{
"id": null,
"sku": "300099",
"name": "Immune Support Shot 60ml",
"status": "substituted",
"quantity": 1,
"requestedQuantity": 1,
"weight": null,
"priceCents": 330,
"requestedId": "33333333-3333-3333-3333-333333333333",
"requestedSku": "300003",
"scannedBarcode": "2345678901235",
"scans": null
}
```
#### Example — loose produce (no scan)
Items sold "each" have no physical barcode. Identify by `sku`.
```json theme={"dark"}
{
"id": "66666666-6666-6666-6666-666666666666",
"sku": "600006",
"name": "Fresh Bananas Each",
"status": "picked",
"quantity": 2,
"requestedQuantity": 2,
"weight": null,
"priceCents": 186,
"requestedId": "66666666-6666-6666-6666-666666666666",
"requestedSku": "600006",
"scannedBarcode": null,
"scans": null
}
```
#### Example — weighted item with multiple scans
Variable-weight items picked across multiple physical units produce one `scans[]` entry per scan, each with a parsed GS1 payload. For richer parsed output, read `scans[].barcodes[]` instead of re-parsing `scannedBarcode` yourself.
```json theme={"dark"}
{
"id": "77777777-7777-7777-7777-777777777777",
"sku": "700007",
"name": "Bananas Loose",
"status": "picked",
"quantity": 1,
"requestedQuantity": 1,
"weight": 1.45,
"priceCents": 450,
"requestedId": "77777777-7777-7777-7777-777777777777",
"requestedSku": "700007",
"scannedBarcode": "0112345678901234310300072039220000225",
"scans": [
{
"substitutionType": "no_substitution",
"barcodes": [
{
"barcode": "0112345678901234310300072039220000225",
"format": "gs1_databar",
"isVariableWeight": true,
"weight": 0.72,
"weightUnit": "kg",
"priceCents": 225,
"productCode": "12345678901234",
"expirationDate": null
}
]
},
{
"substitutionType": "no_substitution",
"barcodes": [
{
"barcode": "0112345678901234310300073039220000225",
"format": "gs1_databar",
"isVariableWeight": true,
"weight": 0.73,
"weightUnit": "kg",
"priceCents": 225,
"productCode": "12345678901234",
"expirationDate": null
}
]
}
]
}
```
# Product catalog examples
Source: https://docs.usenash.com/api-reference/store-catalog/product-catalog-examples
Sample product payloads for common product types to help you structure your catalog data correctly.
This guide provides ready-to-use sample payloads for the [Create or Update Products](/api-reference/store-catalog/create-or-update-products) endpoint (`POST /v1/products`). Each example covers a common product type you'll encounter when building your catalog.
Before sending products, review the required and optional fields in the [Pick and Pack overview](/api-reference/store-catalog/pick-and-pack#product-catalog).
## Required fields checklist
Every product **must** include the following fields:
| Field | Type | Description |
| - | - | - |
| `name` | string | Display name of the product |
| `categories` | string\[] | Category hierarchy (e.g., `["Dairy & Eggs", "Milk"]`) |
| `weight` | number | Total weight in kilograms |
| `dimensions` | object | `depth`, `width`, `height` in centimeters |
| `identifiers` | array | At least one identifier (e.g., UPC barcode) |
**Commonly used optional fields:**
| Field | Type | Description |
| - | - | - |
| `imageUrls` | string\[] | Product image URLs, shown to pickers during fulfillment |
| `sku` | string | Your internal SKU |
| `externalIdentifier` | string | Your system's unique product ID |
| `description` | string | Product description |
| `details.sizeSpecification` | object | Size value and unit (e.g., `200.0` / `"G"`) |
| `details.packSizeSpecification` | object | Pack description (e.g., `"140g x 4 Pack"`) |
| `attributes` | string\[] | Special attributes like `"WEIGHTED"` for variable-weight items |
## Fixed-weight packaged goods
Standard shelf products sold at a fixed weight — canned goods, boxed items, packaged proteins, etc.
**What to look for:**
* `weight` reflects the total packaged weight (not just the net contents)
* `details.sizeSpecification` captures the labeled net weight (e.g., 200g)
* A single UPC identifier is typically sufficient
```json theme={"dark"}
{
"products": [
{
"name": "Skinless & Boneless Salmon 200g",
"description": "Skinless & Boneless Salmon 200g",
"sku": "3001012",
"externalIdentifier": "3001012",
"imageUrls": [
"https://cdn.example.com/products/3001012/main.jpg"
],
"categories": [
"Pantry",
"Canned & Prepared Meals",
"Canned Fish"
],
"weight": 0.225,
"dimensions": {
"depth": 3.5,
"width": 10.2,
"height": 7.8
},
"identifiers": [
{
"type": "UPC",
"value": "9300657100810"
}
],
"details": {
"sizeSpecification": {
"value": 200.0,
"description": "G"
}
}
}
]
}
```
For fixed-weight items, `weight` (in kg) should include packaging. The `sizeSpecification` value represents the labeled net contents.
## Count / piece items (EA)
Products sold by count or number of pieces — tea bags, tablets, individual items, etc.
**What to look for:**
* `details.sizeSpecification` uses `"EA"` (each) as the unit
* `details.packSizeSpecification` describes the pack (e.g., `"25 Pack"`)
* Multiple `imageUrls` can be provided for different product views
```json theme={"dark"}
{
"products": [
{
"name": "English Breakfast Tea Bags 25 Pack",
"description": "English Breakfast Tea Bags 25 Pack",
"sku": "3002023",
"externalIdentifier": "3002023",
"imageUrls": [
"https://cdn.example.com/products/3002023/main.jpg",
"https://cdn.example.com/products/3002023/side.jpg"
],
"categories": [
"Pantry",
"Tea & Coffee",
"Tea"
],
"weight": 0.095,
"dimensions": {
"depth": 7.0,
"width": 8.5,
"height": 13.0
},
"identifiers": [
{
"type": "UPC",
"value": "70177020095"
}
],
"details": {
"sizeSpecification": {
"value": 25.0,
"description": "EA"
},
"packSizeSpecification": {
"value": "25 Pack",
"description": "pack size"
}
}
}
]
}
```
## Multi-pack products
Bundled products containing multiple individual units — yoghurt packs, drink multipacks, snack boxes, etc.
**What to look for:**
* `details.sizeSpecification` reflects the **total** combined weight/volume (e.g., 4 x 140g = 560g)
* `details.packSizeSpecification` describes the bundle (e.g., `"140g x 4 Pack"`)
* Multiple UPC `identifiers` may be needed — one for the outer packaging and one for the individual units
* `weight` is the total weight of the entire multi-pack including packaging
```json theme={"dark"}
{
"products": [
{
"name": "Greek Yoghurt Strawberry 140g x 4 Pack",
"description": "Greek Yoghurt Strawberry 140g x 4 Pack",
"sku": "3003034",
"externalIdentifier": "3003034",
"imageUrls": [
"https://cdn.example.com/products/3003034/main.jpg",
"https://cdn.example.com/products/3003034/back.jpg",
"https://cdn.example.com/products/3003034/nutrition.jpg"
],
"categories": [
"Dairy & Eggs",
"Yoghurt",
"Multipacks"
],
"weight": 0.612,
"dimensions": {
"depth": 7.5,
"width": 24.0,
"height": 9.0
},
"identifiers": [
{
"type": "UPC",
"value": "9343587000824"
},
{
"type": "UPC",
"value": "9343587000831"
}
],
"details": {
"sizeSpecification": {
"value": 560.0,
"description": "G"
},
"packSizeSpecification": {
"value": "140g x 4 Pack",
"description": "pack size"
}
}
}
]
}
```
For multi-packs, always include the outer package barcode as an identifier. If the individual units have a different barcode, include that as a second identifier — this helps pickers scan either code during fulfillment.
## Liquid / volume-based products
Products where the primary measurement is volume — milk, juice, cleaning products, etc.
**What to look for:**
* `details.sizeSpecification` uses volume units (`"ML"` or `"L"`) instead of weight
* `weight` still reflects the physical weight in kg (important for delivery logistics)
* For liquids, weight will typically be close to the volume (1L of water \~ 1kg) plus packaging
```json theme={"dark"}
{
"products": [
{
"name": "Full Cream Milk 2L",
"description": "Full Cream Milk 2L",
"sku": "3005034",
"externalIdentifier": "3005034",
"imageUrls": [
"https://cdn.example.com/products/3005034/main.jpg"
],
"categories": [
"Dairy & Eggs",
"Milk",
"Full Cream"
],
"weight": 2.06,
"dimensions": {
"depth": 9.5,
"width": 9.5,
"height": 27.0
},
"identifiers": [
{
"type": "UPC",
"value": "9310073000206"
}
],
"details": {
"sizeSpecification": {
"value": 2.0,
"description": "L"
}
}
}
]
}
```
## Size specification units reference
Use the correct `description` value in `sizeSpecification` for your product type:
| Unit | Description Value | Use For |
| - | - | - |
| Grams | `"G"` | Solid food, packaged goods |
| Kilograms | `"KG"` | Bulk or heavy items |
| Millilitres | `"ML"` | Small liquid products |
| Litres | `"L"` | Large liquid products |
| Each | `"EA"` | Count-based items (bags, tablets, pieces) |
## Common mistakes to avoid
`weight` must be in **kilograms** and reflect the total packaged weight. A 200g product should have `"weight": 0.2`, not `"weight": 200`. This field is critical for delivery logistics and bag packing optimization.
The `dimensions` object is required. Provide all three fields (`depth`, `width`, `height`) in **centimeters**. These are used for delivery capacity planning.
Include at least one image URL. Images are shown to pickers during fulfillment — without them, pickers cannot visually verify they have the correct product.
Categories should form a hierarchy from broad to specific (e.g., `["Dairy & Eggs", "Yoghurt", "Multipacks"]`). This powers search and browse functionality in the storefront.
`sizeSpecification` is the **labeled product size** customers see (e.g., 2L, 500g, 25 pack). `weight` is the **physical weight in kg** for logistics. These serve different purposes.
## Next steps
Send your product catalog via the API
Set up per-store availability and pricing
# Create or update store location
Source: https://docs.usenash.com/api-reference/store-locations/create-or-update-store-location
post /v1/store_locations/upsert
Create a new store location or update an existing one. Matches on external identifier or store location ID for upsert logic.
# Create or update store location by external identifier
Source: https://docs.usenash.com/api-reference/store-locations/create-or-update-store-location-by-external-identifier
post /v1/store_locations/external-identifier/{externalIdentifier}
Create a new store location or update an existing one using your external identifier. If a store location with the given external identifier exists, it is updated.
# Create store location
Source: https://docs.usenash.com/api-reference/store-locations/create-store-location
post /v1/store_locations
Create a new store location (pickup point) with address, hours, and capacity settings.
# Delete store location
Source: https://docs.usenash.com/api-reference/store-locations/delete-store-location
delete /v1/store_locations/{id}
Delete a store location by ID. Orders referencing this store location will no longer have a valid pickup point.
# Get Store Location
Source: https://docs.usenash.com/api-reference/store-locations/get-store-location
get /v1/store_locations/{id}
Get a store location by ID.
# Get store location by external identifier
Source: https://docs.usenash.com/api-reference/store-locations/get-store-location-by-external-identifier
get /v1/store_locations/external_identifier/{externalIdentifier}
# Get Store Locations
Source: https://docs.usenash.com/api-reference/store-locations/get-store-locations
get /v1/store_locations
Get a list of store locations.
# Get store locations that cover specified location (latitude & longitude, city_zipcode)
Source: https://docs.usenash.com/api-reference/store-locations/get-store-locations-that-cover-specified-latitude-and-longitude
get /v1/store_locations/coverage
Find store locations whose delivery zones cover a given geographic point. Provide latitude/longitude, city_zipcode, or zipcode to find matching store locations.
# Update Store Location
Source: https://docs.usenash.com/api-reference/store-locations/update-store-location
patch /v1/store_locations/{id}
Update a store location by ID.
# Upsert multiple store locations
Source: https://docs.usenash.com/api-reference/store-locations/upsert-multiple-store-locations
post /v1/store_locations/upsert/bulk
Create or update multiple store locations in a single request. Each store location is matched by external identifier for upsert logic.
# Upsert multiple templates
Source: https://docs.usenash.com/api-reference/templates/upsert-multiple-templates
post /v1/templates/upsert
Create or update multiple delivery window or shift templates in a single request. Templates generate their instances on a recurring schedule. On update, a shift template's payload is merged: a field you omit keeps its stored value, and clearing one requires sending it explicitly as null. The external references are the exception, because they are resolved to internal ids only when supplied: sending externalStoreLocationId or externalZoneIds as null leaves the stored store location or zones unchanged.
# Using LLMs with Nash API
Source: https://docs.usenash.com/api-reference/using-llms-with-nash-api
Point an LLM at the Nash API reference and let it speed up your integration
An LLM — Claude, ChatGPT, Cursor, or your own agent — can speed up your Nash integration considerably: generating request code, explaining endpoints, and debugging responses. This page covers the formats we publish for exactly that.
If your assistant supports MCP, connect the [docs MCP server](/reference/docs-mcp-server) at `https://docs.usenash.com/mcp` instead of pasting links — it searches and reads the current docs on its own. [Build with AI](/reference/build-with-ai) covers every option.
## Nash API documentation for LLMs
Give the LLM the API documentation in a machine-readable format:
* **Complete API Documentation**: [https://docs.usenash.com/llms-full.txt](https://docs.usenash.com/llms-full.txt)
* **Condensed Documentation**: [https://docs.usenash.com/llms.txt](https://docs.usenash.com/llms.txt)
* **Any single page as Markdown**: append `.md` to the page URL — for example [https://docs.usenash.com/reference/webhooks.md](https://docs.usenash.com/reference/webhooks.md)
* **OpenAPI Specification**: [https://docs.usenash.com/api-reference/openapi.json](https://docs.usenash.com/api-reference/openapi.json)
* **Docs MCP Server**: let an assistant search these docs itself — see [Docs MCP server](/reference/docs-mcp-server)
* **Nash MCP Server**: connect an agent directly to Nash — see [Nash MCP server](/reference/mcp-integration)
These specially formatted text files contain the Nash API endpoints, schemas, and implementation details in a format that's optimized for LLMs to process. They're generated from the public Nash API specification, so they describe only the endpoints available to you — no internal surfaces.
## Example prompt
```
I'm working on integrating with Nash's delivery API. Here's the relevant documentation:
https://docs.usenash.com/llms-full.txt
I need to implement [SPECIFIC FUNCTIONALITY]. Can you help me understand how to structure this request?
```
## Best practices for using LLMs with the Nash API
1. **Provide clear context**: Share the relevant Nash API documentation links with the LLM
2. **Be specific**: Clearly describe your use case and requirements
3. **Include complete information**: When troubleshooting, share error messages and API responses
## Key integration areas where LLMs can help
* **Understanding Nash Concepts**: Orders, Jobs, Batch Jobs, Dispatch Strategies
* **API Request/Response Formatting**: Generate code samples for API calls
* **Webhook Implementation**: Set up secure webhook handlers for delivery updates
* **Error Handling**: Understand and resolve API error responses
## Keep sensitive data out of the prompt
The documentation links above are safe to share — they're public. Your operational data is not. Treat anything you paste into a third-party LLM as if it could be retained or used for training, and keep these out of the prompt:
* **Credentials** — API keys, tokens, `Authorization` headers, webhook signing secrets.
* **Customer and recipient PII** — names, dropoff addresses, phone numbers, email addresses.
* **Identifiers that map back to your operation** — organization IDs, `Nash-Org-Id`, order/job IDs, request IDs.
* **Raw API responses and webhook payloads** — these bundle several of the above together. Redact before sharing.
When you need an LLM to debug a real request or response, **replace the sensitive values with placeholders** (``, ``, `123 Main St`) before pasting. The structure is what the model needs; the real values are not.
Always review and test LLM-generated code before deploying it to production — verify endpoints, fields, and error handling against the [API reference](/api-reference/api-overview).
For comprehensive integration guidance, refer to our [Plan Your Integration](/reference/plan-your-integration) documentation.
# Count Workflow Executions
Source: https://docs.usenash.com/api-reference/workflow-execution/count-workflow-executions
get /v1/workflows/{id}/executions/count
Returns the total count of workflow executions matching the given filters. Useful for pagination or dashboards without fetching full execution data.
# Get Workflow Execution Detail
Source: https://docs.usenash.com/api-reference/workflow-execution/get-workflow-execution-detail
get /v1/workflow-executions/{id}
Returns a single workflow execution with all its execution steps, including step status, node references, and node variables.
# List Workflow Executions
Source: https://docs.usenash.com/api-reference/workflow-execution/list-workflow-executions
get /v1/workflows/{id}/executions
Returns paginated workflow execution history with optional filtering by status, date range, and search. Supports sorting by created_at, completed_at, or status.
# Create Workflow
Source: https://docs.usenash.com/api-reference/workflow/create-workflow
post /v1/workflows
Create a new workflow with nodes, edges, and a trigger. The workflow is created in draft status by default. Nodes define the actions and filters, edges define the execution flow between nodes, and the trigger defines when the workflow is activated. First fetch GET /workflows/schema for current action config schemas and branch rules. Bounded AI decisions use nodeType=action, nodeAction=ai_decision; a workflow may contain multiple decision nodes, each with its own config and named decision edges.
# Get Workflow
Source: https://docs.usenash.com/api-reference/workflow/get-workflow
get /v1/workflows/{id}
Returns a single workflow by ID, including its latest version with nodes, edges, and trigger.
# Get Workflow Schema
Source: https://docs.usenash.com/api-reference/workflow/get-workflow-schema
get /v1/workflows/schema
Returns self-describing workflow metadata including triggers, action config schemas, filter operators/fields, published node outputs, and supported workflow node, edge, and status types. Fetch before authoring a workflow. Action-specific edge_rules override the ordinary node-type rules. ai_decision includes all three decision modes, template syntax, availability, and test semantics.
# List Workflows
Source: https://docs.usenash.com/api-reference/workflow/list-workflows
get /v1/workflows
Returns all non-archived workflows for the authenticated organization.
# Set Workflow Status
Source: https://docs.usenash.com/api-reference/workflow/set-workflow-status
post /v1/workflows/{id}/status
Change the status of a workflow (draft, active, inactive, archived, template). Archiving a workflow disables its notification trigger.
# Test Workflow
Source: https://docs.usenash.com/api-reference/workflow/test-workflow
post /v1/workflows/{id}/test
Run a workflow against a real entity without activating it. Requires jobId or orderId — the entity the run is evaluated against. By default this is a dry run: filters are evaluated against the entity and each action returns a preview of what it would do, without performing it. With live=true, messaging actions (send_slack, send_email, send_sms) are sent with a [Test] prefix and ai_decision calls the model; every other action stays preview-only and its preview carries a note saying so, so a live test can never apply a state change such as cancel_order or add_tag. Returns per-node status, so a caller can confirm which filters passed before activating the workflow.
# Trigger Workflow
Source: https://docs.usenash.com/api-reference/workflow/trigger-workflow
post /v1/workflows/{id}/trigger
Trigger an active or inactive workflow with optional entity references and input data. Supports sync and async execution modes.
# Update Workflow
Source: https://docs.usenash.com/api-reference/workflow/update-workflow
patch /v1/workflows/{id}
Update an existing workflow. Only provided fields are updated. If nodes, edges, and trigger are all provided, a new workflow version is created. These three structure fields must be provided together or not at all.
# Create a zone
Source: https://docs.usenash.com/api-reference/zones/create-a-zone
post /v1/zones
Create a geographic zone defined by a polygon boundary. Zones are used to define delivery areas for store locations and apply zone-based restrictions.
# Delete a zone
Source: https://docs.usenash.com/api-reference/zones/delete-a-zone
delete /v1/zones/{id}
Delete a zone by ID. Removes the zone and its store location associations.
# Get zone
Source: https://docs.usenash.com/api-reference/zones/get-zone
get /v1/zones/{id}
Retrieve a single zone by ID, including its polygon boundary and associated store locations.
# Get zone by external identifier
Source: https://docs.usenash.com/api-reference/zones/get-zone-by-external-identifier
get /v1/zones/external_identifier/{externalIdentifier}
Retrieve a zone using your external identifier instead of the Nash zone ID.
# Get zones
Source: https://docs.usenash.com/api-reference/zones/get-zones
get /v1/zones
List zones for the organization with pagination. Use size and offset parameters to paginate. Optionally include deleted zones with is_deleted.
# Get zones that cover specified location (latitude & longitude, city_zipcode)
Source: https://docs.usenash.com/api-reference/zones/get-zones-that-cover-specified-location-latitude-and-longitude-city_zipcode
get /v1/zones/coverage
Find zones whose polygon boundaries contain a given geographic point. Provide latitude/longitude, city_zipcode, or zipcode to find matching zones.
# Query zones
Source: https://docs.usenash.com/api-reference/zones/query-zones
post /v1/zones/query
Return full zone details for many zones in a single call. Filter by `ids` and/or `externalIds` (combined with OR); omit both to page through all of the organization's zones. Results come back under `data` with pagination under `meta`; each `data` item matches the shape of `GET /v1/zones/`.
# Update a zone
Source: https://docs.usenash.com/api-reference/zones/update-a-zone
patch /v1/zones/{id}
Update an existing zone's name, polygon boundary, or associated store locations. Only provided fields are updated.
# Overview
Source: https://docs.usenash.com/fleet/overview
Manage drivers and fulfill Nash deliveries with your own fleet: how driver setup, dispatch, delivery-state updates, and the Fleet API fit together.
The Fleet API is the inbound surface a fleet uses to manage its drivers and report state on deliveries Nash has dispatched to it: courier location, status transitions, proof of delivery, courier identity, ETAs, and notes.
Use it whether you're a merchant operating your own couriers or a third-party fleet receiving Nash demand. The contract is the same in both cases: Nash dispatches a job (delivery) to your organization, you fulfill it, and you `PATCH` updates back as the delivery progresses.
## Manage your drivers
Use [Upsert Driver](/api-reference/fleet/upsert-driver) (`PUT /v1/fleet/drivers/upsert`) to create or update a driver in your internal fleet. Your `externalIdentifier` is the upsert key: send the same value again to update that driver, and inspect `created` in the response to tell whether Nash created a new record.
Every request must include `phoneNumber` as an international number with a leading country code. Nash stores it in E.164 form, links the driver to a Nash user with your organization's driver role, and provisions SMS sign-in for the Driver App. The linked driver also appears in your internal-driver list in the Portal. You can include `email` to add email sign-in too. Creating a driver through this endpoint does not send a welcome message.
Keep these identity rules in mind:
* A driver's stored phone number cannot be changed through this endpoint.
* A phone number already linked to another driver in your organization cannot be reused with a different `externalIdentifier`, even when that driver is disabled.
* A phone number belonging to an active user outside your organization is rejected.
* Omitting, clearing, or sending a blank `email` leaves the existing email unchanged.
There is no separate delete call. To make a driver unavailable, upsert the driver with `isShiftActive: false`; to disable the driver, use `enabled: false`.
## Manage your driver groups
A driver group is a named set of your drivers that dispatch can target. If your roster already lives in a workforce or HR system, three endpoints keep the groups in Nash matched to it, so a change of depot or team doesn't wait for someone to edit a list in the Portal.
| Endpoint | Use it to |
| - | - |
| [List Driver Groups](/api-reference/fleet/list-driver-groups) (`GET /v1/fleet/driver-groups`) | Read your groups, newest first. Disabled groups are left out unless you send `activeOnly=false`. |
| [Get Driver Group](/api-reference/fleet/get-driver-group) (`GET /v1/fleet/driver-groups/{first_version_contract_id}`) | Read one group and the drivers in it. |
| [Upsert Driver Group](/api-reference/fleet/upsert-driver-group) (`PUT /v1/fleet/driver-groups/upsert`) | Create or update a group, keyed on your own `externalIdentifier`. |
A group has two identifiers. `externalIdentifier` is yours and is the upsert key. `firstVersionContractId` is Nash's, stays the same however often the group is edited, and is what the read endpoints and Upsert Driver take.
There are two ways to change who is in a group, and they suit different sources of truth:
* **You hold the whole roster.** Send `driverIds` on Upsert Driver Group. It replaces the group's members with the list you send; `[]` removes everyone, and leaving the field out changes nothing.
* **You hear about one driver at a time.** Send `addFirstVersionContractIds` or `removeFirstVersionContractIds` on [Upsert Driver](/api-reference/fleet/upsert-driver). The driver's other memberships are kept. These two can't be combined with `firstVersionContractIds`, which replaces all of a driver's memberships.
A new group needs a `displayName` and starts active. There is no delete call: upsert with `isActive: false` to disable a group. A disabled group can lose members but can't gain new ones.
```bash theme={"dark"}
curl -X PUT https://api.sandbox.usenash.com/v1/fleet/driver-groups/upsert \
-H "Authorization: Bearer $NASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalIdentifier": "depot-north-am",
"displayName": "North depot, morning"
}'
```
The response is the group with its `drivers` and a `created` flag that says whether the call created it. Send the same `externalIdentifier` again with `driverIds`, your drivers' Nash IDs, to set its roster.
## Report delivery state
## When updates start
Each dispatch event Nash sends carries a `deliveryId`. That's the identifier you use on every update for that delivery's lifetime. Updates only land on deliveries owned by your organization; anything else returns not-found.
## Two endpoints for delivery state
| Endpoint | Use when |
| - | - |
| `PATCH /v1/fleet/deliveries/{delivery_id}` | A single delivery changed state. |
| `PATCH /v1/fleet/deliveries` | You want to flush updates for up to 100 deliveries at once. Per-item failures don't affect the rest of the batch. |
Both accept partial bodies — only the fields you want to change need to be present.
## Lifecycle
A typical happy path moves through:
```
pickup_enroute → pickup_arrived → pickup_complete →
dropoff_enroute → dropoff_arrived → dropoff_complete
```
Terminal failures use `failed` or `canceled_by_provider`, paired with a structured `failure.code` (e.g. `customer_unavailable`, `address_not_found`, `no_capacity`). Returns flow through `return_in_progress → return_arrived → returned_to_store`.
Sending the same status twice is a no-op, so it's safe to retry.
## What you can include in a single call
A single update can carry any combination of:
* **`status`** — lifecycle transition.
* **`coordinates`** — `{ latitude, longitude }`. Append-only; safe to send on every tick.
* **`courier`** — name, phone, vehicle, profile image. Send on first assignment, or any subset on a mid-flight swap.
* **`proofOfDelivery`** — image artifacts (photo / signature at pickup or dropoff) or barcode scans.
* **`failure`** — structured `{ code, reason }` for `failed` / `canceled_by_provider`.
* **`pickupEta` / `dropoffEta`** — provider-supplied ETAs.
* **`pickupNote` / `dropoffNote`** — free text the courier captured.
* **`parkingLocation` / `returnParkingLocation`** — where the courier parked at the stop or when returning the package.
* **`externalDeliveryId`** — your own identifier for this delivery, for cross-system reconciliation.
## Validation
* At least one mutating field is required per update — empty bodies return `422`.
* `coordinates.latitude` must be in `[-90, 90]`, `longitude` in `[-180, 180]`.
* Wire format is camelCase; snake\_case is also accepted.
## Response shape
Both endpoints return the same delivery object you'd see nested under a job's task in `GET /v1/jobs/{id}`. The bulk response wraps each item with a per-item `success` flag and either the updated delivery or a structured error.
## Next steps
Walk through reporting status, location, and proof of delivery.
Create or update a driver and provision their sign-in identity.
Create a group or replace its roster from your own system.
PATCH a single delivery as it progresses.
Flush updates for up to 100 deliveries at once.
# Report delivery state
Source: https://docs.usenash.com/fleet/report-delivery-state
How a fleet reports status, location, proof of delivery, and ETAs back to Nash as a job (delivery) progresses — for single and bulk updates, and how to test transitions with Fleet simulators.
When Nash dispatches a job (delivery) to your organization, the Fleet API is how you report state back as the courier works through it — status transitions, courier location, proof of delivery, courier identity, ETAs, and notes. This guide walks through reporting state for a single delivery, flushing many updates at once, testing transitions in sandbox, and the lifecycle and validation rules that apply.
Every dispatch event Nash sends carries a `deliveryId`. That identifier is what you use on every update for that delivery's lifetime. Updates only land on deliveries owned by your organization — anything else returns not-found.
## Reporting state for a single delivery
Use [Update Delivery](/api-reference/fleet/update-delivery) (`PATCH /v1/fleet/deliveries/{delivery_id}`) to report a change on one delivery. The body is partial — include only the fields that changed.
When Nash dispatches the job (delivery) to you, store the `deliveryId` it carries. You'll send it on every subsequent update.
On assignment, send the `courier` object (name, phone, vehicle, and profile image as available) and a status such as `assigned_driver`. You can include `coordinates` and an ETA in the same call.
As the courier advances, `PATCH` each transition — `pickup_enroute`, `pickup_arrived`, `pickup_complete`, and so on. `coordinates` are append-only and safe to send on every location tick, alongside `pickupEta` / `dropoffEta` and any `pickupNote` / `dropoffNote`.
At pickup or dropoff, include `proofOfDelivery` — image artifacts (photo or signature) or barcode scans — discriminated by type.
Send `dropoff_complete` on success. For a terminal failure, send `failed` or `canceled_by_provider` together with a structured `failure` object. Returns flow through `return_in_progress → return_arrived → returned_to_store`.
A single update can carry any combination of the following fields:
| Field | What it carries |
| - | - |
| `status` | A lifecycle transition (see [Lifecycle & validation](#lifecycle--validation)). |
| `coordinates` | `{ latitude, longitude }` for the courier's current position. Append-only; safe to resend. |
| `courier` | Courier identity and vehicle — name, phone, vehicle, profile image. |
| `proofOfDelivery` | Image (photo or signature) or barcode artifacts captured at a stop. |
| `failure` | Structured `{ code, reason }` when the delivery enters `failed` or `canceled_by_provider`. |
| `pickupEta` / `dropoffEta` | Provider-supplied ETAs, ISO 8601. |
| `pickupNote` / `dropoffNote` | Free-text notes the courier captured at a stop. |
| `parkingLocation` / `returnParkingLocation` | Where the courier parked at the stop or when returning the package. |
| `externalDeliveryId` | Your own identifier for the delivery, for cross-system reconciliation. |
The endpoint returns the updated delivery object — the same shape you'd see nested under a job's task in [Get Job](/api-reference/job/get-job).
## Bulk updates
When you have updates for many deliveries to flush at once, use [Bulk Update Deliveries](/api-reference/fleet/bulk-update-deliveries) (`PATCH /v1/fleet/deliveries`). Send a `deliveries` array of up to 100 items, where each item is the same partial update you'd send to the single endpoint, keyed by its `deliveryId`.
The response returns a `results` array with one entry per request item, in order. Each result carries:
* `deliveryId` — the Nash delivery id.
* `success` — whether that item's update was applied.
* `delivery` — the updated delivery, on success.
* `errorCode` / `errorMessage` — failure detail, when `success` is `false`.
Per-item failures don't affect the rest of the batch, so a single bad item won't roll back the others. Inspect each result's `success` flag rather than relying on the overall HTTP status.
## Testing with Fleet simulators
In sandbox you can exercise your integration without a real courier. Two complementary tools:
* **fleet simulators** — mock fleets that emulate real-world scenarios. `FleetSimulator` cycles a job through statuses from driver-not-assigned up to dropoff-complete, `FleetSimulatorFast` does the same on a shorter interval, and `FailingFleetSimulator` sends a failed status. Simulators start the job based on its scheduled pickup time. See [Environments](/reference/environments) for the full list.
* **Simulate Delivery Status Transition** — [this endpoint](/api-reference/fleet-simulator/simulate-delivery-status-transition) (`POST /v1/deliveries/{delivery_id}/simulate`) advances a delivery to a specific status on demand, so you can drive your handlers through each state deliberately.
The simulate endpoint is available in dev and sandbox only, and only for deliveries dispatched to the Nash sandbox test provider. The behavior of fleets and fleet simulators is not an indication of how real fleets behave in production.
## Lifecycle & validation
A typical happy path moves through:
```
pickup_enroute → pickup_arrived → pickup_complete →
dropoff_enroute → dropoff_arrived → dropoff_complete
```
The full set of status values you can send is:
`not_assigned_driver`, `assigned_driver`, `pickup_enroute`, `pickup_arrived`, `pickup_complete`, `dropoff_enroute`, `dropoff_arrived`, `dropoff_complete`, `failed`, `canceled_by_provider`, `return_in_progress`, `return_arrived`, `returned_to_store`.
Terminal failures use `failed` or `canceled_by_provider`, paired with a structured `failure.code`. Valid codes are `customer_unavailable`, `address_not_found`, `out_of_service_area`, `damage_in_transit`, `item_unavailable`, `age_verification_failed`, `no_capacity`, and `other`, with an optional free-text `reason`.
Validation rules to keep in mind:
* **At least one mutating field is required** per update — empty bodies return `422`.
* `coordinates.latitude` must be in `[-90, 90]` and `coordinates.longitude` in `[-180, 180]`.
* Wire format is camelCase; snake\_case is also accepted.
* Sending the same status twice is a no-op, so updates are safe to retry.
* `proofOfDelivery` artifacts must be reachable URLs — a `file://` reference is rejected. A delivery holds at most 50 photos and signatures and, counted apart from those, at most 200 barcode records across pickup, dropoff, and return; the same barcode recorded at pickup and again at dropoff is two records. Any of those problems fails the whole update rather than dropping the artifact quietly, so a rejected proof is something you find out about.
## Next steps
How the inbound Fleet API surface fits together.
PATCH a single delivery's state.
Flush updates for up to 100 deliveries at once.
Drive a sandbox delivery to a given status.
# API tutorial: an example checkout workflow with Nom Nom Goods
Source: https://docs.usenash.com/guides/api-tutorial-workflow-nom-nom-foods
A basic Nash integration through an example app checkout workflow.
This guide demonstrates how to integrate Nash into the checkout and delivery process. It uses our fictional company Nom Nom Goods as an example, following our customer Sammy through the process.
## Introduction
Imagine you built a business and app called Nom Nom Goods. Nom Nom Goods manages hundreds of stores across the country and uses Nash to orchestrate its deliveries.
## The typical customer experience
Here's what Sammy's experience looks like in the Nom Nom app:
1. Sammy adds items to his cart and selects a nearby store location.
2. At checkout, he chooses to have his order delivered immediately.
3. Nash, integrated into the checkout flow, provides delivery quotes and manages the delivery.
4. Sammy receives his order quickly and reliably, along with status updates about his delivery.
## Integration with Nash on checkout
To integrate Nash into your Nom Nom Goods checkout flow, you'll follow these steps:
1. **Build the dispatch strategy**: Configure how Nash selects the best delivery provider for each order.
2. **Set up automations**: Automatically apply the appropriate dispatch strategy based on predefined rules.
3. **Create an order**: When Sammy completes checkout, create a new order in Nash for delivery coverage and pricing.
4. **Autodispatch the order**: After Sammy places his order, let Nash dispatch it to the best provider automatically.
5. **Track the delivery**: Use live updates from Nash to keep Sammy informed about his order status.
The examples below use the sandbox host. You'll need a sandbox API key, and the `Nash-Org-Id` header if your key has access to multiple organizations — see [API Key and Org ID](/reference/generating-api-key-and-org-id).
Here's a diagram of the process we'll build:
The figure here is a sequence diagram between Sammy, the Nom Nom app, Nash, and the delivery providers:
1. **Checkout.** Sammy fills a cart and picks delivery. The app creates an order (`POST /v1/order`). Nash collects quotes from the allowed providers, the automation applies `NomNomCheckoutStrategy`, and the response returns the quotes with one tagged `autodispatch_preferred_quote`. The app shows Sammy the delivery price.
2. **Order placed.** Sammy places the order. The app refreshes the quotes if they have expired (`POST /v1/order/refresh_quotes`), then autodispatches (`POST /v1/order/{id}/autodispatch`). Nash books the winning quote, and the order becomes a job (delivery).
3. **On the way.** The provider reports status changes, Nash sends them to the app as webhooks, and the app shows Sammy live order status.
## 1. Building the dispatch strategy
Dispatch strategies are configurations that determine how Nash selects delivery providers for your orders. You may prefer selecting the lowest-cost provider or have a ranked list of providers.
To create a dispatch strategy, you can use either the Nash API or the Nash Portal. Below is an example of using the API to create a dispatch strategy with the following configuration:
* **Allowed providers**: `NomNomDrive`, `HotRodWheels`, `FastFleet`
* **Provider selection strategy**: `lowest_price` (Nash chooses the provider with the lowest delivery cost.)
* **Reassign on failure**: `true` (Nash seeks an alternative provider if the first choice cannot fulfill the order.)
* **Auto dispatch**: `false` (We manage the autodispatch of orders ourselves.)
For more details, refer to [Dispatch strategies](/api-reference/dispatch-strategies/dispatch-strategy).
Here's a sample curl command to create the dispatch strategy:
```shell create dispatch strategy theme={"dark"}
curl --request POST \
--url https://api.sandbox.usenash.com/v1/options_group \
--header "Authorization: Bearer $NASH_API_KEY" \
--header "Nash-Org-Id: $NASH_ORG_ID" \
--header 'Content-Type: application/json' \
--data '{
"label": "NomNomCheckoutStrategy",
"allowedProviders": [
"NomNomDrive", "HotRodWheels", "FastFleet"
],
"enableAutoDispatch": false,
"providerSelectionStrategy": "lowest_price",
"reassignOnFailure": true
}'
```
## 2. Setting up automations
Automations apply dispatch strategies to your orders based on predefined rules, such as package value or delivery location. The right dispatch strategy applies without manual intervention.
You can configure automations in the Nash Portal. For example, create a rule that applies the "NomNomCheckoutStrategy" dispatch strategy to any order with a package value over \$50, so higher-value deliveries get the right treatment.
## 3. Creating an order
When customer Sammy completes checkout in your Nom Nom Goods app, you'll create a new order in Nash using the [`/v1/order`](/api-reference/order/create-order) endpoint. The order payload should include details such as:
* Pickup address
* Dropoff address
* Package description
* Package value
* Any special instructions
Below is an example of an order creation payload:
```json payload for creating an order theme={"dark"}
{
"pickupAddress": "185 University Ave, Palo Alto, CA 94301",
"pickupBusinessName": "Nom Nom Goods - Palo Alto",
"pickupPhoneNumber": "+17201234567",
"pickupInstructions": "Pickup 10 boxes of glazed for Sammy. Order Number 1234",
"dropoffAddress": "401 San Antonio Rd, Mountain View, CA 94040",
"dropoffBusinessName": "Sammy's House",
"dropoffFirstName": "Sammy",
"dropoffLastName": "Camchaw",
"dropoffPhoneNumber": "+12345679019",
"dropoffInstructions": "Leave at the doorstep, plz!",
"deliveryMode": "now",
"description": "10 boxes of donuts",
"valueCents": 8990,
"tipAmountCents": 1000,
"currency": "USD",
"itemsCount": 10
}
```
Notice the payload doesn't set a dispatch strategy: the automation from step 2 applies `NomNomCheckoutStrategy` on its own, because the order's \$89.90 value clears the \$50 rule. (You *can* set `dispatchStrategyId` directly on an order, but letting automations decide keeps that logic in Nash.)
After creating the order, Nash will return a list of quotes from available delivery providers, including the price, estimated delivery time, and a tag indicating the preferred quote for autodispatch.
```json quote list in create order response theme={"dark"}
"quotes": [
{
"id": "qot_UPVrXwaAt4tQraeY2mDgH2",
"providerId": "NomNomDrive",
"providerName": "NomNomDrive",
"providerLogo": "https://nash-provider-logos.s3.amazonaws.com/FleetSimulator.png",
"createdTime": "2022-09-21T22:28:47.533139",
"expireTime": "2022-09-21T23:28:47.533139",
"priceCents": 1800,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": null,
"dropoffEta": "2022-09-21T22:31:47.533124",
"tags": null,
"costSplitCustomerCents": null,
"costSplitBusinessCents": null
},
{
"id": "qot_coBkLwCrgvtbAewhUGehf3",
"providerId": "FastFleet",
"providerName": "FastFleet",
"providerLogo": "https://nash-provider-logos.s3.amazonaws.com/FleetSimulator.png",
"createdTime": "2022-09-21T22:28:47.533681",
"expireTime": "2022-09-21T23:28:47.533682",
"priceCents": 650,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": null,
"dropoffEta": "2022-09-21T22:37:47.533667",
"tags": [
"autodispatch_preferred_quote"
],
"costSplitCustomerCents": null,
"costSplitBusinessCents": null
},
{
"id": "qot_dvwfGGNGSc8yUrQxrp6cgp",
"providerId": "HotRodWheels",
"providerName": "HotRodWheels",
"providerLogo": "https://nash-provider-logos.s3.amazonaws.com/FleetSimulator.png",
"createdTime": "2022-09-21T22:28:47.534574",
"expireTime": "2022-09-21T23:28:47.534574",
"priceCents": 2000,
"nashFeeCents": 100,
"currency": "USD",
"pickupWindow": null,
"dropoffEta": "2022-09-21T22:34:47.534567",
"tags": null,
"costSplitCustomerCents": null,
"costSplitBusinessCents": null
}
]
```
Let's say an hour passes while Sammy lingers on checkout, deciding whether to add an 11th box of donuts. The quotes have expired, so we [refresh them](/api-reference/order/refresh-order-quotes) for the order:
```shell refresh order quotes theme={"dark"}
curl --request POST \
--url https://api.sandbox.usenash.com/v1/order/refresh_quotes \
--header "Authorization: Bearer $NASH_API_KEY" \
--header "Nash-Org-Id: $NASH_ORG_ID" \
--header 'Content-Type: application/json' \
--data '{
"orderId": "ord_1234567890"
}'
```
Finally, Sammy clicks the Place Order button in the Nom Nom app. Since Nom Nom already created the order with the appropriate details in Nash, the only thing left is to autodispatch.
## 4. Autodispatching the order
Once you have reviewed the quotes, you can use Nash's autodispatch feature to assign the delivery automatically to the best provider based on your dispatch strategy. To initiate the autodispatch process, you will call the [`/v1/order/{id}/autodispatch`](/api-reference/order/autodispatch-order) endpoint.
Here's an example of how to autodispatch an order:
```shell autodispatch order theme={"dark"}
curl --request POST \
--url https://api.sandbox.usenash.com/v1/order/ord_1234567890/autodispatch \
--header "Authorization: Bearer $NASH_API_KEY" \
--header "Nash-Org-Id: $NASH_ORG_ID"
```
When an order is dispatched, it becomes a job (the fulfillment attempt). The response contains the job, and within its job configurations you'll find two objects worth noting.
The `winnerQuote` object on the job's task has the selected quote's ID.
```json autodispatch response - winner quote theme={"dark"}
"winnerQuote": {
"id": "qot_coBkLwCrgvtbAewhUGehf3",
"priceCents": 650
}
```
The `delivery` object indicates the delivery is active.
```json autodispatch response - delivery object theme={"dark"}
"delivery": {
"id": "dlv_01234567890",
"status": "created"
}
```
## 5. Tracking the delivery
After dispatching the order, Nash provides live updates on the delivery status via [webhooks](/reference/webhooks). Use these webhooks to keep customers like Sammy informed about their order's progress, such as when the driver is en route or when the delivery is complete.
## Conclusion
That's the whole loop: a dispatch strategy, an automation, an order, an autodispatch, and webhooks. Sammy gets his donuts; you get a delivery pipeline that runs the same way on every order.
# Sample checkout workflow
Source: https://docs.usenash.com/guides/checkout-workflow
A step-by-step workflow for embedding Nash delivery quotes, dispatch, and tracking into your checkout.
This page outlines a sample workflow for embedding Nash into a custom checkout. It defines a few terms and walks through the Nash endpoints we recommend for fulfilling delivery from your checkout experience.
Building for a restaurant? The [fast casual restaurant integration](/guides/fast-casual-restaurant-integration) guide walks through this pattern for ASAP food orders, including kitchen prep time, guest-facing statuses, and live tracking.
## Relevant concepts
* [Authentication](/reference/authentication)
* [Nash Order](/api-reference/order/order)
* [Dispatch automations](/api-reference/dispatch-strategies/dispatch-automations)
* [Dispatch strategy](/api-reference/dispatch-strategies/dispatch-strategy)
## Step 1: Fetching order quotes
If you want to validate that Nash has a delivery option or if you want to pull the delivery price quotes from Nash for
your customer's cart, you should use the [Get Order Quotes](/api-reference/order/get-order-quotes) endpoint to fetch all
the valid quotes for the order.
Some fields you want to include in this request are:
* Pickup location: you should determine the location you would like to fulfill from. Alternatively, Nash can help
you determine the closest location to the customer that has inventory of the item, if you use our
[Store Locations](/api-reference/store-locations/get-store-location).
* Customer Address: the dropoff location for the order. You'll need a full, valid address to obtain delivery quotes.
* Estimated Pickup + Dropoff times: pass the time your customer would like to have the order delivered. If only the dropoff
time is sent, Nash will compute the pickup time based on the driving distance between the pickup and dropoff.
* Order / cart ID (if available): associating a unique identifier to the Nash Order will allow you to recognize it in the Nash Portal.
* `dispatchStrategyId` (optional): we recommend using [Dispatch automations](/api-reference/dispatch-strategies/dispatch-automations)
to determine the dispatch strategy for an order, but if you want to specify the dispatch strategy at the quote creation step, then you can set up [Dispatch strategies](/api-reference/dispatch-strategies/dispatch-strategy) and pass the ID.
### Processing the quotes response
How you process the quotes from Nash depends on the type of integration you are building. For instance, do you plan to show your customers an accurate delivery price in the checkout or will you just charge them a fixed fee for delivery, regardless of the Nash price? Here are a few fields that may be relevant:
* **`id`**: This is the order ID that you'll use in the following steps of the checkout.
* **`quotes`**: this is a list of quotes that you can process. If using dispatch strategies, look for the quote with `autodispatch_preferred_quote` in `quote.tags`.
* **`failedQuotes`**: a list of quotes from providers that can't fulfill the order, along with the failure reason.
If your customer abandons the cart, you don't need to cancel the order that was created to obtain quotes.
## Step 2: Updating the order
If your customer decides to place the order for local delivery, then you should initially use the [Update Order](/api-reference/order/update-order)
endpoint to add any fields that were missing when you first obtained your quotes. For this endpoint, you will use the Order ID returned in
Step 1, and you should consider adding any new information that's relevant for the order. For example, if an order is
created in your system, then you should add it in the `externalId` field. Similarly, you can update the `description`,
`itemsCount`, `pickupInstructions`, and `dropoffInstructions` fields to give the driver what they need to complete
the delivery.
You can also call Update Order anytime your customer updates their cart and tries to fetch a new delivery quote.
To avoid excessive calls, you should only call update order when your customer is ready to check out and wants
the latest delivery price.
## Step 3: Dispatching orders for delivery
Once your customer has placed the order, you should indicate to Nash that the order should be dispatched to the provider.
Nash will forward the delivery information about the order (addresses, pickup + dropoff times, order details, etc.) to
the chosen provider.
There are two ways you can dispatch the order, and it depends on whether you are using dispatch strategies:
1. With dispatch strategies: use [Autodispatch Order](/api-reference/order/autodispatch-order). Autodispatching an order
selects a quote based on the dispatch strategy.
2. Without dispatch strategies: use [Select Order Quote](/api-reference/order/select-quote) to choose the quote your customer
has accepted in the checkout page.
## Step 4: Tracking your deliveries
Once the customer's order is placed and booked with the provider, you might want to track
the status of the delivery in your app. To do that, you can either use the
[GET Order](/api-reference/order/get-order) endpoint or [set up webhook](/reference/webhooks) updates.
## FAQ
### (Optional) Zip / postal code validation
Some merchants want their customers to validate that delivery is available for them before they reach the checkout page.
A common question is "What if I want to check for local delivery availability in the pre-checkout step before a customer inserts their full address?"
If you want to add a delivery widget option, we recommend adding a step in your cart page or a banner in your website to
ask your customers to check if they are eligible for local delivery. If your system already tracks stores, you can store
a list of zip / postal codes that are reachable by local delivery for each store, or you can rely on the Nash
[Store Locations](/api-reference/store-locations/get-store-location) to check which areas are serviceable from your stores.
### Refreshing quotes
Quotes have an `expireTime`. You can use the [Refresh Quotes](/api-reference/order/refresh-order-quotes)
endpoint to refresh the quotes. The pricing shouldn't change assuming no other information about the order has changed.
# Fast casual restaurant integration
Source: https://docs.usenash.com/guides/fast-casual-restaurant-integration
Quote delivery during checkout, dispatch when payment succeeds, and track every delivery through webhooks.
A fast casual delivery integration comes down to three touchpoints. You quote while the guest is in checkout, dispatch that quote when payment succeeds, and listen to webhooks until the food is at the door. Nash picks the provider (a third-party courier network, a local fleet, or your own drivers) and sends every status back in one normalized format.
This guide is for a restaurant brand, or its ordering platform, that runs its own web or app checkout and wants on-demand (ASAP) delivery. For the same pattern outside restaurants, see the [sample checkout workflow](/guides/checkout-workflow).
## When to use this
* Guests order in your own app or website and choose delivery at checkout.
* You want to show a delivery fee and arrival time before the guest pays.
* Your kitchen needs couriers to arrive when food is ready, not before.
* You want delivery status in your app, your POS, or your kitchen display.
If you batch many orders into planned routes for your own drivers, start with [route optimization](/guides/route-optimization) instead.
## How it works
| Step | When it happens | Nash call | What you get back |
| - | - | - | - |
| 1. Quote | Guest enters a delivery address in checkout | [`POST /v1/order/get_quotes`](/api-reference/order/get-order-quotes) | A Nash order `id`, a list of `quotes` (price, dropoff ETA, expiry), and `failedQuotes` |
| 2. Dispatch | Payment succeeds and the POS order exists | [`PATCH /v1/order/{id}`](/api-reference/order/update-order), then [`POST /v1/order/{id}/autodispatch`](/api-reference/order/autodispatch-order) | A job (delivery), booked with the provider |
| 3. Track | From dispatch until a terminal status | [Webhooks](/reference/webhooks) to your endpoint (type `delivery`) | One event per status change, carrying the full job |
The figure here is a sequence diagram in three phases. Your app calls Nash at two moments, once to quote and once to dispatch, and everything after that comes back as webhooks.
1. **Checkout: quote.** The guest enters a delivery address. Your app calls `POST /v1/order/get_quotes`. Nash requests quotes from providers and returns an order `id`, `quotes`, and `failedQuotes`. Your app shows the fee and ETA of the quote tagged `autodispatch_preferred_quote`.
2. **Order placed: dispatch.** The guest pays. Your app updates the order with `PATCH /v1/order/{id}`, then calls `POST /v1/order/{id}/autodispatch`. Nash books the quote your dispatch strategy prefers and returns the job.
3. **Delivery: track.** The provider reports status changes. Nash sends each one to your app as a `delivery` webhook, and your app shows live status to the guest.
## Before you start
Build and test in Sandbox first. Sandbox has its own API keys and Portal, and pairs [simulated fleets](/reference/environments) with your test orders, so a test order never sends a real courier.
Every request sends `Authorization: Bearer $NASH_API_KEY` and the `Nash-Org-Id` header. See [Authentication](/reference/authentication).
Add each restaurant as a [store location](/reference/store-locations), with your own store number as its external ID. A quote then only needs `pickupExternalStoreLocationId`; Nash uses the store's saved address and contact details.
A [dispatch strategy](/reference/dispatch-strategies) holds your rules for choosing a provider: cost, speed, a preferred fleet first, a fee cap, auto-reassignment. Apply it with a [dispatch automation](/api-reference/dispatch-strategies/dispatch-automations) so you don't send `dispatchStrategyId` on every call. Keep **Enable Autodispatch** off, so an order isn't booked the moment you quote it.
In the Portal, under **Settings > Webhook Management**, add an HTTPS endpoint subscribed to `delivery` events. Copy its signing secret for [verification](/reference/webhooks#verifying-webhooks).
## Step 1: Quote during checkout
Call [Create Quote](/api-reference/order/get-order-quotes) once the guest has entered a full delivery address and you know which store will make the food. The response gives the delivery fee and arrival time to show, and a Nash order `id` to keep with the cart.
Call it after the address is confirmed and the cart is close to final, not on every keystroke or cart edit. If the guest changes the address or store, quote again. If they change only the cart, update the order (Step 2).
```bash theme={"dark"}
curl -X POST https://api.sandbox.usenash.com/v1/order/get_quotes \
-H "Authorization: Bearer $NASH_API_KEY" \
-H "Nash-Org-Id: $NASH_ORG_ID" \
-H "Content-Type: application/json" \
-d '{
"externalId": "cart_8812",
"pickupExternalStoreLocationId": "store-0142",
"dropoffAddress": "350 5th Ave, New York, NY 10118",
"dropoffFirstName": "Jordan",
"dropoffLastName": "Lee",
"dropoffPhoneNumber": "+12125550123",
"deliveryMode": "now",
"pickupStartTime": "2026-09-30T17:15:00Z",
"valueCents": 3450,
"itemsCount": 3,
"description": "2 bowls, 1 drink"
}'
```
| Field | Why it matters for fast casual |
| - | - |
| `pickupExternalStoreLocationId` | Your store number. Nash looks up the pickup address from the store location. |
| `dropoffAddress` (or address components), name, and phone | A full, valid address is required for a quote; see [Send the address](#send-the-address-single-line-or-components). The phone lets the courier reach the guest. |
| `deliveryMode` | `now` on the first quote. Switch it to `scheduled` if the guest picks a later delivery time. |
| `pickupStartTime` | The earliest the food can be ready: now plus your kitchen prep time, plus any buffer, in UTC. On the first quote, this is the only time you send; see [Choose which time to send](#choose-which-time-to-send). |
| `dropoffStartTime` / `dropoffEndTime` | Leave these out of the first quote. Send them only when the guest picks a later delivery time; Nash then works out the pickup time from drive time. |
| `valueCents`, `itemsCount`, `description` | Order value and size, passed on to the provider so the courier knows what to expect. |
| `externalId` | Your cart or order ID, so you can find the order in the Portal. You can replace it with the POS order number in Step 2. |
| `maxDeliveryFeeCents` | Optional. A cap on the delivery fee; it overrides the dispatch strategy's cap. |
### Choose which time to send
Quote with the earliest pickup time, and let the provider's ETA set the first delivery time the guest sees.
1. **First quote: send the pickup time only.** Set `pickupStartTime` to the earliest the food can be ready: now plus the restaurant's prep time, plus any buffer you want to give the kitchen. Don't send dropoff times.
2. **Show the ETA as the first available time.** Each quote comes back with a `dropoffEta`: the provider's expected arrival for that pickup time. Show the preferred quote's `dropoffEta` to the guest as the earliest delivery time they can choose.
3. **Update the order to match what the guest chose.** Before you dispatch (Step 2), send the time that matches the guest's choice:
| The guest chooses | What to send before dispatch | What Nash does |
| - | - | - |
| The first available time | Nothing changes. Keep `pickupStartTime` as you quoted it. | Books the delivery for that pickup time. |
| A later time | Set `deliveryMode` to `scheduled`, set `pickupStartTime` to `null`, and send the guest's chosen time as `dropoffStartTime` (and `dropoffEndTime` for a window). | Works out when the courier needs to pick up, from drive time, and sends back that pickup time so the restaurant knows when to have the food ready. |
Don't send both a pickup time and a guest-chosen dropoff time for the same order. One anchors the delivery; Nash calculates the other.
### Send the address: single line or components
Nash needs a deliverable dropoff address to quote. You can send it two ways, and you pick one per request; mixing them returns a validation error.
| | Single line | Address components (preferred) |
| - | - | - |
| What you send | `dropoffAddress`: the whole address as one string, such as `"350 5th Ave, Apt 4B, New York, NY 10118"` | Each part in its own field, plus coordinates: `dropoffAddressNumber`, `dropoffAddressSecondarynumber`, `dropoffAddressFormattedStreet`, `dropoffAddressCity`, `dropoffAddressState`, `dropoffAddressZip`, `dropoffAddressCountry`, `dropoffLat`, `dropoffLng` |
| What Nash does | Parses and geocodes the string, then returns the parsed components in the response | Uses your components and coordinates as sent, without geocoding again |
| If it goes wrong | An address Nash can't parse fails validation (for example, "Address not found") | You own the accuracy of what you send |
| Use it when | Your checkout only has a free-text address | Your checkout already has a structured address, such as from an address autocomplete or a saved guest profile |
Send components if you have them. The address the guest confirmed at checkout is the one the courier gets, with the unit number in its own field and the pin where your autocomplete put it, rather than a second geocode of a typed string. Required components are `dropoffAddressFormattedStreet`, `dropoffAddressCity`, `dropoffAddressZip`, `dropoffAddressCountry`, `dropoffLat`, and `dropoffLng`.
```json theme={"dark"}
{
"pickupExternalStoreLocationId": "store-0142",
"dropoffAddressNumber": "350",
"dropoffAddressSecondarynumber": "4B",
"dropoffAddressFormattedStreet": "5th Ave",
"dropoffAddressCity": "New York",
"dropoffAddressState": "NY",
"dropoffAddressZip": "10118",
"dropoffAddressCountry": "US",
"dropoffLat": 40.7484,
"dropoffLng": -73.9857
}
```
Don't send `dropoffAddress` or `dropoffPlaceId` alongside components. A Google Place ID (`dropoffPlaceId`) works like the single line: Nash looks it up and geocodes it. The pickup side follows the same rules, but with `pickupExternalStoreLocationId` Nash takes the pickup address from the store location. See [Order address validation](/api-reference/order/order-address-validation-geocoding) and [Order validations](/api-reference/order/order-validations).
### Read the response
* `id` is the Nash order ID. Store it with the cart; Steps 2 and 3 use it.
* `quotes` lists the options, each with `totalPriceCents`, `dropoffEta`, `expireTime`, and `providerName`. Show the guest the quote whose `tags` include `autodispatch_preferred_quote`; that's the one autodispatch will book.
* `failedQuotes` lists providers that can't take the order. If `quotes` is empty, delivery isn't available to this address, so offer pickup instead.
You don't have to charge the guest the Nash price. You can charge a flat delivery fee and use the quote only to confirm delivery is available and to show an ETA.
If the guest abandons the cart, do nothing. An order that was quoted but never dispatched doesn't need to be canceled.
## Step 2: Dispatch after checkout completes
Once payment succeeds and the order exists in your POS, update the Nash order with the final details, then dispatch it. Dispatching books the delivery with the provider and returns a job (delivery), which you track in Step 3.
### Update the order
Use [Update Order](/api-reference/order/update-order) with the Nash order `id` from Step 1. Swap in the POS order number and add what the courier needs at the counter and at the door:
```bash theme={"dark"}
curl -X PATCH https://api.sandbox.usenash.com/v1/order/$NASH_ORDER_ID \
-H "Authorization: Bearer $NASH_API_KEY" \
-H "Nash-Org-Id: $NASH_ORG_ID" \
-H "Content-Type: application/json" \
-d '{
"externalId": "POS-20260930-0417",
"referenceId": "417",
"pickupInstructions": "Order #417 on the pickup shelf by the door",
"dropoffInstructions": "Apt 4B, buzz 4B",
"tipAmountCents": 500
}'
```
`referenceId` is shown to the courier where the provider supports it, so use the short number your staff call out at the counter.
If the guest chose a later delivery time instead of the first available one, send the time change in the same update: switch `deliveryMode` to `scheduled`, clear the pickup time, and send their chosen dropoff time (see [Choose which time to send](#choose-which-time-to-send)).
```json theme={"dark"}
{
"deliveryMode": "scheduled",
"pickupStartTime": null,
"dropoffStartTime": "2026-09-30T19:00:00Z"
}
```
### Dispatch the order
Call [Autodispatch Order](/api-reference/order/autodispatch-order). Nash books the quote your dispatch strategy prefers, the one tagged `autodispatch_preferred_quote`, which is the quote you showed the guest in Step 1. Your provider rules live in the strategy, so you can change them in the Portal without a code change.
[Select Quote](/api-reference/order/select-quote) (`POST /v1/select_quote` with a `quoteId`) books one specific quote. It's available, but use it only when Nash's built-in selection strategies can't express what you need. It hard-codes the provider choice in your code, and you lose strategy features like auto-reassignment and fee caps. If you think you need it, talk to Nash first; a strategy change usually covers it.
Autodispatch returns `{ "job": { ... } }`. Save `job.id` on your POS order; webhooks reference it.
* **Check the quote hasn't expired.** Every quote has an `expireTime`. If the guest spent a long time in checkout, call [Refresh Quotes](/api-reference/order/refresh-order-quotes) with the `orderId` first. The price shouldn't change unless the order did.
* **Dispatch once per order.** Mark the POS order as dispatched when the call succeeds. If the call times out, don't retry blindly: read the order with [Get Order](/api-reference/order/get-order) and dispatch again only if it has no job.
* **Plan for a failed dispatch.** If the call returns an error or no provider can take the order, alert the store, then retry after refreshing quotes, switch the order to pickup, or refund the delivery fee.
## Step 3: Receive status updates through webhooks
After dispatch, Nash sends a webhook every time the delivery changes status, so you don't need to poll. Every payload has three fields: `type` (`delivery` for status changes), `event` (the new status), and `data` (the full job, the same shape as the dispatch response).
```json theme={"dark"}
{
"type": "delivery",
"event": "dropoff_enroute",
"data": {
"id": "job_R7FXJrfB99bpvpzf6CriJC",
"isBatch": false,
"portalUrl": "https://portal.usenash.com/active/job_R7FXJrfB99bpvpzf6CriJC",
"jobConfigurations": [ { "tasks": [ { "status": "dropoff_enroute" } ] } ]
}
}
```
### Map statuses to what the guest sees
Nash normalizes every provider's statuses into [one list](/reference/delivery-status). A fast casual app usually needs only a few guest-facing states:
| Nash `event` | Guest-facing message | Store action |
| - | - | - |
| `created`, `not_assigned_driver` | "Finding a courier" | None |
| `assigned_driver` | "Courier assigned", with name and ETA | Get the bag ready |
| `pickup_enroute`, `pickup_arrived` | "Courier heading to the restaurant" / "Courier at the restaurant" | Hand off the bag |
| `pickup_complete`, `dropoff_enroute` | "On the way", with live tracking | None |
| `dropoff_arrived` | "Courier has arrived" | None |
| `dropoff_complete` | "Delivered", with proof of delivery if present | Close the order |
| `canceled_by_provider`, `canceled_by_nash`, `failed`, `expired` | "We're sorting out your delivery" | Alert the store (see [Edge cases](#edge-cases)) |
| `canceled_by_customer` | "Delivery canceled" | Refund per your policy |
Every completed delivery passes the seven statuses below in order; Nash infers a status if a provider skipped it. Other statuses can arrive in between, and not every provider sends every status.
The figure here shows the seven statuses as stops on a line: `created`, `assigned_driver`, `pickup_enroute`, `pickup_arrived`, `dropoff_enroute`, `dropoff_arrived`, `dropoff_complete`. From any stop before the last, a delivery can end early as `canceled_by_provider`, `canceled_by_nash`, `canceled_by_customer`, `failed`, or `expired`. With auto-reassignment on, a provider cancel isn't the end: Nash books another provider on the same job, and a new task starts again at `created`.
### Build your webhook handler
1. **Verify the signature.** Nash sends webhooks through Svix. Check the `svix-id`, `svix-timestamp`, and `svix-signature` headers against your endpoint's signing secret, ideally with a Svix SDK. See [Verifying webhooks](/reference/webhooks#verifying-webhooks).
2. **Respond with a `2xx` quickly.** Queue the work and acknowledge right away. Failed deliveries are retried on a backoff; see the [retry policy](/reference/webhooks#retry-policy).
3. **Deduplicate.** Retries and multiple tasks can send the same event twice. Key on `svix-id`, and never move an order backward: ignore `dropoff_enroute` once you've seen `dropoff_complete`.
4. **Handle reassignments.** When Nash reassigns a delivery, the old task ends and a new task starts on the same job. A cancel on the old task doesn't mean the delivery is over; read `jobConfigurations[].advancedTask` for the attempt that's live. See [Reassignments and courier changes](/reference/webhooks#reassignments-and-courier-changes).
5. **Match on the job ID.** Look up your order by `data.id`, the `job.id` you saved in Step 2.
### Real-time tracking for the guest
You can show the guest a live map two ways: build it yourself from location webhooks, or let Nash host it. The hosted page is the faster path, since it needs no map or backend work on your side.
| | Build it yourself | Nash-hosted tracking |
| - | - | - |
| How it works | Subscribe to [`courier_location`](/reference/webhooks#courier-location-events) webhooks. Nash sends the courier's position every 1 to 2 minutes while the provider reports it. Draw it on your own map. | The job's `publicTrackingUrl` opens a Nash-hosted page with the courier on a live map and real-time status. Link to it, or [embed it](/reference/nash-embedding) in your app or site with an `