---
title: "Update Order by ID"
method: PUT
path: "/orders/{id}"
tags: ["Orders"]
---

# Update Order by ID

`PUT /orders/{id}`

## Request

- Base URL: `https://api.smartroutes.io/v2`
- URL: `https://api.smartroutes.io/v2/orders/{id}`
- Auth: none declared

Change some of the details of an existing order in your depot. Only the fields you send are changed. Everything else keeps its stored value. The response is `{order, errors}`, where `order` is the updated order in the same shape as `GET /orders/{id}`.

List fields replace the stored list instead of merging with it:
- Sending `line_items`, `custom_fields`, `capacities`, `tags`, `skills` or `emails` replaces the order's whole list. An empty array clears it.
- Line items are matched to existing ones by `product_code`. Stored line items that are missing from the list are removed.
- Sending any time-window field (`delivery_time_windows`, `pickup_time_windows` or `time_windows`) replaces all of the order's time windows, both delivery and pickup.

If you change the delivery or pickup address without sending new coordinates, the new address is geocoded. A geocoding failure doesn't fail the update. The change is saved, and the failure is returned in `errors` with name `GEOCODING_FAILED`.

You get `400 INVALID_INPUT` if:
- the new `order_number` already belongs to another order in your depot
- a skill, vehicle or third party doesn't exist
- a `type` change would leave the order without the address its new type needs

If the order isn't in your depot, you get `404 RESOURCE_NOT_FOUND`. The order's customer can't be changed here. `order_exchange` can only change the reference of an existing exchange pair. Pairs are created with `POST /orders`.

## Path parameters

- `id` string, required

## Response `200`

Successful response

- object
  - `order` object: Details of an order including delivery, pickup, or shipment information.
    - `order_number` string: Order number.
    - `priority` string: Priority of the order.
    - `type` 'delivery' | 'pickup' | 'shipment': Type of the order (delivery, pickup, or shipment).
    - `delivery_contact_name` string: Name of the contact person.
    - `delivery_contact_number` string: Contact number of the person.
    - `delivery_contact_email` string: Contact email of the person.
    - `delivery_address` string: Delivery address.
    - `delivery_postcode` string: Postcode for delivery address.
    - `delivery_lat` number: Latitude of the delivery location.
    - `delivery_lng` number: Longitude of the delivery location.
    - `delivery_duration` number: Duration for order delivery in minutes.
    - `delivery_notes` string: Notes for delivery instructions.
    - `delivery_date` string, date: Date for order delivery.
    - `delivery_available_days` string[]: List of available delivery days for the order.
    - `pickup_address` string: Address for order pickup.
    - `pickup_postcode` string: Postcode for pickup address.
    - `pickup_duration` number: Duration for order pickup in minutes.
    - `pickup_lat` number: Latitude of the pickup location.
    - `pickup_lng` number: Longitude of the pickup location.
    - `pickup_notes` string: Notes for pickup instructions.
    - `pickup_contact_name` string: Name of the contact person for pickup.
    - `pickup_contact_number` string: Contact number of the person for pickup.
    - `pickup_contact_email` string, email: Email of the contact person for pickup.
    - `pickup_available_days` string[]: List of available pickup days for the order.
    - `parts` number: Number of parts in the order.
    - `vehicle_assignment` object
      - `vehicle_id` integer: ID of the assigned vehicle
    - `emails` object[]
      - `email` string, email: Additional emails
    - `third_party` object: Third party object
      - `id` integer: ID of the third party for the order
    - `tags` string[]: List of tag names
    - `order_value` number, double, nullable: Total monetary value of the order, to 2 decimal places. `DERIVED` — the sum of the line values when any line item has a value, and `null` when only some do. `MANUAL` — set on the order when no line item has a value. `0` means a genuinely zero-value order. Omitted when the order has no value recorded, when the depot does not have order values enabled, or when no valid organisation currency is set. Remove it before sending back an order whose `value_source` is `DERIVED`, or the request returns `400`.
    - `currency` string: ISO 4217 currency this order is valued in, taken from your organisation. **Read-only** — not a request field. There is no per-order or per-line currency, and no FX. An order carrying an `order_value` or a valued line item is rejected if your organisation has no valid currency set.
    - `value_source` 'DERIVED' | 'MANUAL': How `order_value` was arrived at. `DERIVED` — summed from the line item values. `MANUAL` — set at order level because no line item has a value. Read-only; ignored if sent.
    - `line_items` object[]: List of line items in the order.
      - `product_code` string, required: Code of the product.
      - `product_name` string, required: Name of the product.
      - `product_quantity` number, float, required: Quantity of the product. May be fractional.
      - `product_unit_price` number, double, nullable: Price per unit, in your organisation's currency — not the line's value, which is `product_value`. Up to 4 decimal places. Present only on a `DERIVED` line: omitted when the line item has no value, when its value is `MANUAL`, or when the depot does not have order values enabled.
      - `product_value` number, double, nullable: The line's value, whichever way it was set: `product_unit_price` × `product_quantity` rounded to 2 decimal places on a `DERIVED` line, the flat value sent on a `MANUAL` line. Omitted when the line item has no value or the depot does not have order values enabled. Remove it before sending back a `DERIVED` line, or the request returns `400`.
      - `product_value_source` 'DERIVED' | 'MANUAL': How the line's value was set. `DERIVED` — from `product_unit_price` × `product_quantity`. `MANUAL` — a flat `product_value`; `product_unit_price` is omitted. Read-only; ignored if sent.
      - `quantity_outstanding` number, float, nullable: Quantity still to be fulfilled on this line item — left to deliver on a `delivery` order, left to collect on a `pickup` order. Equal to `product_quantity` before anything is fulfilled, a value in between after a partial fulfillment, `0` when exactly the ordered quantity has been fulfilled, and **negative** when more than the ordered quantity was fulfilled. Subtract it from `product_quantity` for the amount the driver has fulfilled. **Read-only:** drivers record fulfillment in the SmartRoutes mobile app; it cannot be set through this API. Notes: `null` — not `0` — when the line item is not tracked, so treat null as unknown rather than as fully fulfilled; an over-fulfillment is reported as recorded rather than clamped; stays at `product_quantity` on `shipment` orders, which are dual-leg and not yet tracked. A value still equal to `product_quantity` does not distinguish an untouched line item from one a driver attended and fulfilled nothing on — read `status` for that (`UNDELIVERED` means attended).
      - `capacities` object[]: List of capacities for the line item.
        - `id` integer, required: Id of capacity (e.g., 1, 2).
        - `capacity` number, float, required: Capacity value for the specified type.
      - `status` string: Status of the line item.
      - `date` string, date: Date for the line item.
      - `custom_fields` object[]: List custom fields for the line item
        - `id` integer: Id of custom field (e.g., 1, 2).
        - `value` string: New custom field value
        - `name` string: Name of the custom field.
    - `time_windows` object[]: List of time windows for order delivery or pickup.
      - `from` string, time, required: Start time of the time window.
      - `to` string, time, required: End time of the time window.
      - `type` string, string: The type of the time window, pickup or delivery.
    - `skills` string[]: List of required skills for the order.
    - `custom_fields` object[]: List of custom fields for the order.
      - `id` number: ID of the custom field.
      - `name` string: Name of the custom field.
      - `value` string, required: Value of the custom field.
    - `capacities` object[]: List of capacities for the order. Either id or type is required.
      - `id` integer: Id of capacity (e.g., 1, 2).
      - `type` string: Type of the capacity
      - `capacity` number, float, required: Capacity value for the specified type.
  - `errors` unknown[]
    - unknown

## Other responses

- `400`: Invalid input. The request body failed validation.
- `403`: Forbidden. Returned for rate-limit exhaustion (`name = FORBIDDEN`, with `details.retry_after_seconds` and `Retry-After` header) and permission gate failures (`name = PERMISSION`). Inspect `name` to disambiguate. Send `x-sr-api-features: rate-limit-429` to receive `429 RATE_LIMIT_EXCEEDED` for the rate-limit case instead, leaving `403` to mean permission failures only — see [API Features](#section/Getting-started/API-Features).
- `404`: The requested resource was not found.
- `429`: Rate limit exceeded (`name = RATE_LIMIT_EXCEEDED`). **Only returned to callers that opt in** with the `x-sr-api-features: rate-limit-429` request header; without it the same condition returns `403` with `name = FORBIDDEN` and an otherwise identical body. See [API Features](#section/Getting-started/API-Features).
- `500`: An unexpected server error occurred. The body carries only `name` and `description`: internal details are never returned.

---

[API](https://skmtc.dev/smartroutes/apis/smartroutes-api.md) · [All operations](https://skmtc.dev/smartroutes/apis/smartroutes-api/llms.txt) · [OpenAPI document](https://skmtc.dev/smartroutes/apis/smartroutes-api/revisions/a7ea507afac7?raw)
