Skip to content

Orders Cancel

Cancel Order — PATCH /orders/v1/{orderId}/cancel

Section titled “Cancel Order — PATCH /orders/v1/{orderId}/cancel”

The cancel endpoint checks whether the order is still eligible for cancellation before acting. That check is made server-side, so the client does not need to reason about internal order state:

Outcome What happens
Order can be cancelled Cancellation is submitted. Returns 204 No Content.
Order already cancelled Treated as a no-op. Returns 204 No Content.
Order not eligible Cannot be cancelled in its current state. Returns 422 Unprocessable Entity.

status: "Open" does not guarantee cancellable. Eligibility is evaluated at a finer grain than the status field. To check up front, call GET /orders/v1/{orderId}/allowed-actions and inspect canBeCancelled. Either way, act on the cancel response: 204 means it is (or will be) cancelled, 422 means it is not eligible.

The operation is asynchronous. A 204 response means the cancellation request was accepted, not that the order has already reached the Cancelled state. Poll GET /orders/v1/{orderId} until events.Cancelled appears if you need to confirm the final state.


Order exists, is eligible for cancellation, and the caller has access. The cancellation request is submitted.

Request

PATCH /orders/v1/2000100001/cancel
Authorization: ******
Ocp-Apim-Subscription-Key: <subscription_key>

Response — 204 No Content

(empty body)

After the cancellation is processed, a subsequent GET /orders/v1/2000100001 will show:

{
"order": {
"orderId": "2000100001",
"events": {
"Placed": "2026-06-18T09:38:03.0729832+02:00",
"Cancelled": "2026-06-18T12:01:58.565+02:00"
},
"orderLines": [
{
"lineNumber": "100",
"name": "Logitech MX Keys S Trådløst Tastatur",
"quantity": { "total": 1, "fulfilled": 0, "cancelled": 1 },
"categories": ["PHYSICAL_ITEM"],
"orderLines": []
},
{
"lineNumber": "200",
"name": "Hjemlevering",
"quantity": { "total": 1, "fulfilled": 0, "cancelled": 1 },
"categories": ["DELIVERY"],
"orderLines": []
}
],
"expectedDeliveries": [],
"deliveries": [],
"billings": []
}
}

Key points:

  • events.Cancelled is added with the cancellation timestamp
  • All order line quantities: cancelled incremented to match total; fulfilled stays 0
  • expectedDeliveries is cleared to []

Order was previously cancelled. The endpoint treats this as a success and returns 204 without resubmitting the cancellation. This makes the operation safe to retry.

Request

PATCH /orders/v1/2000100003/cancel
Authorization: ******
Ocp-Apim-Subscription-Key: <subscription_key>

Response — 204 No Content

(empty body)

The order state is unchanged. The Cancelled event timestamp in events is the original one.


422 — Order Shows Open But Is Not Eligible

Section titled “422 — Order Shows Open But Is Not Eligible”

The status field can still read Open while the order is no longer (or not yet) eligible for cancellation. This is not distinguishable from a cancellable Open order by looking at status alone — attempt the cancellation and handle the 422.

Request

PATCH /orders/v1/2000100009/cancel
Authorization: ******
Ocp-Apim-Subscription-Key: <subscription_key>

Response — 422 Unprocessable Entity

{
"code": "ERR_PROCESSING",
"traceId": "TRACE000000000009:00000001",
"type": "https://<store-url>/swagger#ProcessingError",
"title": "Unprocessable Request",
"status": 422,
"detail": "Order cannot be cancelled in its current state.",
"instance": "/orders/v1/2000100009",
"extensions": {}
}

A completed order has events.Completed present. All lines are either fulfilled or cancelled. Cancellation is not applicable.

Request

PATCH /orders/v1/2000100002/cancel
Authorization: ******
Ocp-Apim-Subscription-Key: <subscription_key>

Response — 422 Unprocessable Entity

{
"code": "ERR_PROCESSING",
"traceId": "TRACE000000000005:00000001",
"type": "https://<store-url>/swagger#ProcessingError",
"title": "Unprocessable Request",
"status": 422,
"detail": "Order cannot be cancelled in its current state.",
"instance": "/orders/v1/2000100002",
"extensions": {}
}

Returned when orderId is empty or whitespace-only.

{
"errors": [
{ "fieldName": "OrderId", "message": "OrderId cannot be null or empty." }
],
"code": "ERR_VALIDATION_0001",
"traceId": "TRACE000000000006:00000001",
"type": "https://<store-url>/swagger#Validation",
"title": "Validation Failed",
"status": 400,
"detail": "Some fields have validation errors",
"instance": "/orders/v1/%20",
"extensions": {}
}

No response body. The request is missing or has an invalid Authorization header.

No response body. The token is valid but does not include the order:write scope.

Having order:write scope is necessary but not sufficient — the API also checks ownership. An order is accessible to your token if your account matches either soldTo (the account the goods are billed to) or placedBy (the account that placed the order). If neither matches, you receive ERR_ACCESS even with a valid, scoped token. See the order ownership model for details.

Returned when the caller’s token is valid and scoped, but the order belongs to a different account.

{
"code": "ERR_ACCESS",
"traceId": "TRACE000000000007:00000001",
"type": "https://<store-url>/swagger#AccessError",
"title": "Access Denied",
"status": 403,
"detail": "You do not have access to this order.",
"instance": "/orders/v1/2000100008",
"extensions": {}
}

Returned when the orderId does not exist.

{
"code": "ERR_NOTFOUND",
"traceId": "TRACE000000000008:00000001",
"type": "https://<store-url>/swagger#NotFound",
"title": "Not Found",
"status": 404,
"detail": "Order not found.",
"instance": "/orders/v1/non-existing-id",
"extensions": {}
}