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

# Get Order by ID

`GET /orders/{id}`

## Request

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

Fetch one order from your depot with all of its details. The response wraps the order in `order`. It includes:
- addresses, contacts, dates and time windows
- line items, each with its own `status`, quantities, capacities and custom fields
- order-level capacities, custom fields, skills, tags, emails and attachments
- order value fields, if order values are enabled for your depot
- `stops`: every stop the order is planned on, with its route, delivery outcome and proof of delivery (photos, signatures, questionnaire submissions)

`id` is the SmartRoutes order ID returned by `POST /orders` and `GET /orders`. It is not your order number. To find an order by its order number, use `GET /orders?order_number=...`. If the order isn't in your depot or has been deleted, you get `404 RESOURCE_NOT_FOUND`.

## Path parameters

- `id` string, required

## Response `200`

Successful response

- OrderResponse
  - `order` OrderWithValues, required: An order. Property set is the union of fields returned by list and get-by-id paths; individual endpoints may return a subset.
    - `id` integer
    - `order_number` string
    - `type` 'delivery' | 'pickup' | 'shipment': delivery, pickup or shipment. Always set — required on every create path and enforced NOT NULL at the database.
    - `status` string: Order status. SmartRoutes derives these values from line-item statuses (source: `cORDER_STATUS_TYPES` in `src/models/order-status.js`): `BOOKED`, `BOOKING_DECLINED`, `DELIVERED`, `LOADED`, `OPEN`, `PARTIALLY_BOOKED`, `PARTIALLY_BOOKING_DECLINED`, `PARTIALLY_DELIVERED`, `PARTIALLY_LOADED`, `PARTIALLY_READY_FOR_COLLECTION`, `PARTIALLY_ROUTED`, `PARTIALLY_UNDELIVERED`, `PARTIALLY_WAITING_CONFIRMATION`, `READY_FOR_COLLECTION`, `ROUTED`, `UNDELIVERED`, `WAITING_CONFIRMATION`. This is **not** a closed set. Your organisation can define its own statuses, and any of them can appear here. Call `GET /order-statuses` for the full list your organisation can use, and treat this field as a free string rather than an enum.
    - `priority` string, nullable: LOW, MEDIUM, HIGH, or null.
    - `parts` integer, nullable
    - `delivery_address` string
    - `delivery_lat` number
    - `delivery_lng` number
    - `delivery_notes` string, nullable
    - `delivery_available_days` integer[]: Days of week the delivery may be made. Empty array when the order carries no day restriction.
    - `delivery_duration` number, nullable: Service duration in minutes. Null when unset — not the same as 0.
    - `delivery_date` string, date, nullable
    - `delivery_contact_name` string, nullable
    - `delivery_contact_number` string, nullable
    - `delivery_contact_email` string, nullable
    - `pickup_address` string, nullable
    - `pickup_duration` number, nullable
    - `pickup_lat` number, nullable
    - `pickup_lng` number, nullable
    - `pickup_notes` string, nullable
    - `pickup_contact_name` string, nullable
    - `pickup_contact_number` string, nullable
    - `pickup_contact_email` string, nullable
    - `pickup_available_days` integer[]: Days of week the pickup may be made. Empty array when the order carries no day restriction.
    - `customer` Customer, nullable: Trimmed customer reference embedded in Order responses. May be `null` for orders without a customer association.
      - `id` integer, nullable
      - `account` string, nullable
    - `capacities` Capacity[]
      - `id` integer
      - `type` string
      - `capacity` number, nullable
    - `custom_fields` CustomField[]
      - `id` integer
      - `name` string
      - `value` unknown
    - `time_windows` TimeWindow[]
      - `from` string: HH:mm:ss
      - `to` string: HH:mm:ss
      - `type` string
    - `skills` string[]
    - `attachments` object[]
      - `id` integer
      - `name` string
    - `vehicle_assignment` object[], nullable
      - `vehicle_id` integer
    - `emails` object[]
      - `email` string
    - `third_party` object
      - `id` integer, nullable
    - `order_exchange` object, nullable: Present when this order is one half of an exchange (a drop + collect served together by one vehicle). The two halves share exchange_id.
      - `id` integer
      - `order_id` integer: Server-set: the id of this order. Never accepted as input.
      - `exchange_id` string: Server-minted uuid identifying the pair — identical on both halves. Never accepted as input.
      - `partner_order_id` integer: Server-set: the other half's order id. Never accepted as input.
      - `reference` string, nullable: Customer-supplied swap reference (display only). The only client-settable field — provide it on the create request (order_exchange.reference).
    - `tags` string[]
    - `stops` OrderStop[]
      - `id` integer
      - `route_id` integer
      - `address` string
      - `postcode` string
      - `phone` string, nullable
      - `lat` number
      - `lng` number
      - `planned_distance` number
      - `arrival_date` string, date
      - `departure_date` string, date
      - `estimated_arrival_time` string: HH:mm or HH:mm:ss
      - `estimated_departure_time` string: HH:mm or HH:mm:ss
      - `actual_completed_ts` string, date-time, nullable
      - `actual_completed_lat` number, nullable
      - `actual_completed_lng` number, nullable
      - `delivery_notes` string, nullable
      - `driver_notes` string, nullable
      - `actual_arrival_time` string, nullable
      - `actual_duration` number, nullable
      - `signature` string, uri, nullable
      - `signature_lat` number, nullable
      - `signature_lng` number, nullable
      - `signed_by` string, nullable
      - `photos` string[], nullable: URLs to proof-of-delivery photos for this stop. A photo appears here as soon as it is uploaded and synced, which may be before compression has finished — following the URL of a photo that is still compressing returns `202 Accepted` with a `Retry-After` header (see the photo endpoint), so be prepared to retry. `null` when there are none.
      - `tracking_link` string, uri, nullable
      - `vehicle_assignment` object, nullable
        - `vehicle_id` integer
      - `directions` object[]
        - `instruction` string
      - `driver_questionnaire_submissions` DriverQuestionnaireSubmission[]
        - `id` integer
        - `driver_questionnaire` object
          - `type` string
          - `title` string
      - `failed_reason` object, nullable
        - `reason` string
      - `tags` string[]
      - `available_days` integer[]
      - `emails` object[], nullable
        - `email` string
      - `driver_phone_calls` object[]: Calls the driver placed to this stop's contact number from the driver app, oldest first. `[]` when there are none. **These are call *attempts*, not call records.** The app hands a `tel:` URL to the device's dialler and the OS takes over, so SmartRoutes never observes the call itself. There is deliberately no status, no duration and no answered/unanswered flag — that information does not exist. Inbound calls, and calls placed outside the driver app, are not recorded at all. `initiated_at` is the driver device's own clock at the moment the call was placed; `recorded_at` is when our servers received it. Drivers work offline, so these can differ by hours, and a call can arrive with an `initiated_at` well before the `recorded_at` of calls you have already seen. Poll on `recorded_at`, not `initiated_at`, if you are looking for new activity. Absence is not proof no call was made: on iOS, a call placed with no GPS fix available is not recorded.
        - `initiated_at` string, date-time, nullable: When the driver placed the call, from the device clock. `null` if the app did not record a usable timestamp.
        - `recorded_at` string, date-time, nullable: When our servers received the record. Later than `initiated_at` — by hours if the driver was working offline.
        - `lat` number, nullable: Driver's latitude when the call was placed. `null` when no GPS fix was available.
        - `lng` number, nullable: Driver's longitude when the call was placed. `null` when no GPS fix was available.
      - `custom_fields` CustomField[]
        - `id` integer
        - `name` string
        - `value` unknown
      - `route` object
        - `id` integer
        - `date` string, date
        - `started_ts` string, date-time, nullable
    - `line_items` object[]
      - `id` integer: Server-set line item id. Ignored on input.
      - `product_name` string
      - `product_code` string
      - `product_quantity` number, float
      - `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` Capacity[]
        - `id` integer
        - `type` string
        - `capacity` number, nullable
      - `status` 'BOOKED' | 'BOOKING_DECLINED' | 'CANCEL' | 'COMPLETED' | 'DECLINED' | 'DELIVERED' | 'LOADED' | 'NOT RECEIVED' | 'ON HOLD' | 'OPEN' | 'PARTIALLY_DELIVERED' | 'READY_FOR_COLLECTION' | 'RETURNED' | 'ROUTED' | 'UNDELIVERED' | 'WAITING_CONFIRMATION', nullable: Per-line-item status. `PARTIALLY_DELIVERED` appears when a line item has been partly fulfilled — partly collected on a `pickup` order or partly delivered on a `delivery` order; how much is left to fulfil is in `quantity_outstanding`. There is no separate picked-up status: completed and partial pickups both use the `DELIVERED` family. `UNDELIVERED` covers both "attended, nothing fulfilled" (in which case `quantity_outstanding` still equals `product_quantity`) and a failed visit. The other `PARTIALLY_*` values are aggregates and apply only at the order level (see `OrderStatus`). Historical rows may still carry legacy values (`CANCEL`, `COMPLETED`, `DECLINED`, `NOT RECEIVED`, `ON HOLD`, `RETURNED`); these are listed for backward compatibility but are no longer written by current SR code.
      - `date` string, date, nullable
      - `custom_fields` CustomField[]
        - `id` integer
        - `name` string
        - `value` unknown
      - `product_unit_price` number, double: Price per unit of this line item, in your organisation's currency — **not** the line's value, which is `product_value`. Up to 4 decimal places, because `product_quantity` may be fractional. Captured as a snapshot when written and never re-resolved, so a later price change does not alter historical orders. Omitted from responses when the depot does not have order values enabled, when the line item has no value, or when its value is `MANUAL`.
      - `product_value` number, double: The line's value, whichever way it was set. On a `DERIVED` line, `product_unit_price` × `product_quantity` rounded to 2 decimal places; on a `MANUAL` line, the flat value sent as `product_value`. Like `order_value`, a derived value cannot be written back: when sending back line items from a response, remove `product_value` from any line whose `product_value_source` is `DERIVED` — it would be sent alongside `product_unit_price`, and the request returns `400`. Likewise remove `order_value` from an order whose `value_source` is `DERIVED`. The order total is the sum of these already-rounded line values, not the rounded sum of unrounded lines; the two differ at the penny.
      - `product_value_source` 'DERIVED' | 'MANUAL': How the line's value was set. `DERIVED` — from `product_unit_price` × `product_quantity`, and recalculated when the quantity changes. `MANUAL` — a flat value sent as `product_value`, which ignores quantity; `product_unit_price` is omitted. **Read-only** — ignored if sent. Omitted alongside `product_value` when the line item has no value.
    - `created` string, date-time
    - `updated` string, date-time
    - `order_value` number, double, nullable: Total monetary value of the order, to 2 decimal places. **Usually derived, and only sometimes settable.** If any line item has a value — from `product_unit_price` or `product_value` — the order value is the sum of the line values and cannot be set: a non-null `order_value` returns `400`, rather than being silently ignored, whether the line values are in the same request or already stored. It is settable only on an order with no valued line items, and giving a line a value later replaces the manual figure with a derived one. **`null` is not `0` and neither is absent.** `null` means some but not all line items have a value, so no total is known — a sum over 3 of 5 valued lines would be a wrong number that looks legitimate. `0` means a genuinely zero-value order, such as a replacement or a goodwill redelivery. The field is omitted entirely when the order has no value recorded, or when the depot does not have order values enabled. Do not treat these three states as equivalent.
    - `currency` string: ISO 4217 currency this order is valued in. **Read-only** — not a request field; it is ignored if sent. Currency belongs to your organisation, not to an order or a line item: every order the organisation owns is valued in it, and it is resolved when the order is read, so correcting a wrongly-set account currency corrects the whole order history at once. There is no per-line currency, no mixed-currency order and no FX. An order carrying an `order_value` or a valued line item is rejected with `400` if your organisation has no valid currency set; the error names the setting to fix. Omitted from responses when the order has no value recorded, when the depot does not have order values enabled, or when no valid organisation currency is 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. Omitted alongside `order_value` when no value is recorded.

## 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)
