---
title: "Optimise Orders For IDs"
method: POST
path: "/plans/optimise/orders-for-ids"
tags: ["Plans"]
---

# Optimise Orders For IDs

`POST /plans/optimise/orders-for-ids`

## Request

- Base URL: `https://api.smartroutes.io/v2`
- URL: `https://api.smartroutes.io/v2/plans/optimise/orders-for-ids`
- Auth: none declared

Async optimisation for a specific set of order IDs. Same lifecycle and polling semantics as `POST /plans/optimise/orders` — see that endpoint's description for completion detection, polling cadence, and the failure-signal gap. Supports the `idempotency-key` request header for safe retries.

## Headers

- `idempotency-key` string

## Request body

- object
  - `orders` object[], required
    - `id` number: ID of the order
  - `plan` object
    - `id` integer: You can provide a plan id to re-use for generating your route
    - `name` string: Provide a name for the plan. This will override the existing plan name if an id is provided.
    - `date` string, date: The date of your plan. Format YYYY-MM-DD
  - `settings` object, required
    - `active_vehicles` boolean, required: Flag to include only active vehicles.
    - `auto_dispatch` boolean, required: Flag for auto_dispatch.
    - `maintain_upload_sequence` boolean: Flag to route the orders in the order given, without optimising.
  - `vehicles` object[], nullable
    - `id` integer, required: ID of the vehicle.
    - `name` string: Name of the vehicle.
    - `availability` 'shift' | 'full_day' | 'always', required: Availability status of the vehicle.
    - `shift_start` string, time, nullable: Start time of the vehicle's shift (if availability is 'shift').
    - `shift_end` string, time, nullable: End time of the vehicle's shift (if availability is 'shift').
    - `start_location` 'depot' | 'other' | 'app-location', required: Location type where the vehicle starts.
    - `start_address` string, nullable: Address where the vehicle starts (if location is 'other').
    - `start_lng` number, nullable: Longitude of the starting location (if location is 'other').
    - `start_lat` number, nullable: Latitude of the starting location (if location is 'other').
    - `end_location` 'depot' | 'other' | 'none', required: Location type where the vehicle ends.
    - `end_address` string, nullable: Address where the vehicle ends (if location is 'other').
    - `end_lng` number, nullable: Longitude of the ending location (if location is 'other').
    - `end_lat` number, nullable: Latitude of the ending location (if location is 'other').
    - `break` boolean: Flag indicating whether the vehicle has a break.
    - `break_start` string, time, nullable: Start time of the vehicle's break (if it has a break).
    - `break_end` string, time, nullable: End time of the vehicle's break (if it has a break).
    - `break_duration` number, nullable: Duration of the vehicle's break in minutes (if it has a break).
    - `active` boolean: Flag indicating whether the vehicle is active.
    - `skills` string[]: Array of skills associated with the vehicle.
    - `capacities` object[]: Array of capacities associated with the vehicle.
      - `id` integer, required: Id of capacity (e.g., 1, 2).
      - `capacity` number, float, required: Capacity value for the specified type.
  - `zone_group_id` integer: The ID of the zone group for optimization.

## Response `200`

Optimisation accepted and Plan created. The optimisation runs asynchronously; poll `GET /plans/{id}` for completion. With `settings.maintain_upload_sequence` the body contains only `plan`.

- union
  - PlanOptimisationResponse
    - `optimisation` object, required
      - `id` string, uuid, required
      - `in_progress` boolean, required
    - `plan` object, required
      - `id` integer, required
      - `dispatched` boolean, required
    - `errors` object[]: Orders left out of the optimisation, each with the reason. May be absent.
      - `name` string, required: Reason code (e.g. `IN_ANOTHER_PLAN`).
      - `description` string, required: Human-readable explanation.
      - `details` object, required
        - `id` integer, required: Order id.
        - `order_number` string, nullable, required: Order number.
        - `plan_ids` integer[]: Plans the order already belongs to.
  - PlanSequenceResponse: Returned by POST /plans/optimise/orders-for-ids when `settings.maintain_upload_sequence` is true. The route is built synchronously, so there is no optimisation job: `optimisation` is absent (not null) and there is nothing to poll. Read the routes with GET /plans/{id}.
    - `plan` object, required
      - `id` integer, required
      - `dispatched` boolean, required

## 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.
- `409`: A request with this idempotency key is already in progress. Retry after the period given in the `Retry-After` header.
- `422`: The same idempotency key was used with a different request body for this endpoint.
- `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)
