---
title: "Cancels a sales order (the app's Cancel page): delivered goods come back, transfers are cancelled."
method: POST
path: "/api/v1/sales-orders/{id}/cancel"
tags: ["SalesOrders"]
---

# Cancels a sales order (the app's Cancel page): delivered goods come back, transfers are cancelled.

`POST /api/v1/sales-orders/{id}/cancel`

## Request

- Base URL: `https://app.flexonthejob.com`
- URL: `https://app.flexonthejob.com/api/v1/sales-orders/{id}/cancel`
- Auth: HTTP bearer

Requires `ManageSalesOrders` and an `Idempotency-Key` header. Body optional:
`{ "reason": "Customer changed their mind" }`. Same code path as the app's Cancel
page: units already delivered by completed transfers are returned to inventory, live
transfers are cancelled and their reserved stock released, the order's lines are removed,
and the order and its payment status become `Cancelled` with the history entry
"Order cancelled by … (was …) (via API key '…')". `Cancelled` is terminal;
`Complete` and `Cancelled` orders are refused.

## Path parameters

- `id` integer, required

## Request body

- CancelSalesOrderApiRequest: Body of `POST /api/v1/sales-orders/{id}/cancel`; optional. Example: `{ "reason": "Customer changed their mind" }`.
  - `reason` string, nullable: Why the order is cancelled, up to 500 characters; omitted or blank = `No reason provided` (as the app's Cancel page). Example: `Customer changed their mind`.

## Response `200`

Cancelled; `itemsReturned` and `transfersCancelled` say what was undone.

- SalesOrderStepResponse: Response of the sales-order fulfilment steps (`/pick`, `/complete-pick`, `/ship`, `/deliver`, `/complete`, `/cancel`).
  - `salesOrderId` integer: The sales order's id. Example: `318`.
  - `status` string: Order status after the step: `Pending`, `Picking`, `Packed`, `Shipped`, `Delivered`, `Complete` or `Cancelled`. Recalculated from the order's transfers, so always read it rather than assuming the next status. Example: `Packed`.
  - `fulfillmentStatus` string, nullable: Aggregate status of the order's transfers (`None`, `Pending`, `InTransit`, `AtWaypoint`, `Completed`, `Cancelled`…), as `fulfillmentStatus` on the order. Example: `InTransit`.
  - `transferId` integer, nullable: The transfer the step acted on (resolved when the request did not name one); null for `/complete` and `/cancel`. Example: `904`.
  - `message` string, nullable: Human-readable confirmation, as the app shows it. Example: `Transfer status updated to In Transit.`
  - `unitsPicked` integer: `/pick`: units marked picked by this call (0 for other steps). Example: `10`.
  - `itemsReturned` integer: `/cancel`: delivered units returned to inventory (0 for other steps). Example: `0`.
  - `transfersCancelled` integer: `/cancel`: transfers cancelled (0 for other steps). Example: `1`.

## Other responses

- `400`: Missing `Idempotency-Key` or a reason over 500 characters.
- `401`: Unauthorized
- `403`: Forbidden
- `404`: No such sales order in this organization.
- `409`: The order is already `Complete` or `Cancelled` (detail: `This order has already been completed or cancelled.`), or the key's creator is unknown.
- `429`: Too Many Requests
- `500`: Returning the stock or cancelling the transfers failed; everything was rolled back and the order is unchanged. Retry with the same `Idempotency-Key`.

---

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