# POST /api/public/orders/{orderId}/cancel

> Cancel Order

Cancels an order that has not been paid. Unlike `POST /api/public/orders/{orderId}/refund`, this endpoint never
moves money: if the order can no longer be cancelled — typically because the customer has already paid — nothing
happens and the response says why, with a machine-readable `errorCode`:

- **204**: the order is cancelled, or was already cancelled.
- **409**: the order cannot be cancelled right now, but the same request can succeed later. `order_changed`: send
the same request again. `payment_in_progress`: send it again once the other payment has finished or been
abandoned.
- **400**: the order cannot be cancelled and retrying will not change that: `order_paid` (refund the order
instead), `payment_pending` (a payment has been started and can no longer be stopped), `order_refunded`,
`order_not_cancellable`.

The codes say what to do, not which payment method is involved; `message` carries the details.

**Retrying safely.** Without a header, a retry is safe as long as the order stays cancelled: an order that is
already cancelled gives **204**. Send an `Idempotency-Key` header (a new UUID for every cancellation) to also make a
late retry safe. The key must be a random value and must not contain personal data. It is scoped to your API client
(and the merchant, when the token is issued for one; or the merchant alone, for a token issued with merchant
credentials only) and to this endpoint. The first response with a 2xx status is stored for 24 hours, and a request
with the same key and the same order and body gets that response again, with the header
`Idempotent-Replayed: true`, instead of a second cancellation. That holds also when the order has been opened
again with `PUT /api/public/orders/{orderId}` in between: a retry with the old key does not cancel it. A response
that refused the cancellation (4xx) is not stored, so the same key can be retried once the cause is gone. With the
header, the response can also carry these `errorCode` values, always with `propertyName` `Idempotency-Key`:

- **400** `idempotency_key_invalid`: the header is not a single value of 1 to 255 printable ASCII characters, or
the token identifies neither an API client nor a merchant. Nothing was done.
- **409** `idempotency_in_progress`: a request with the same key is still being processed. Retry shortly with the
same key. If Rivr was interrupted while processing it, this answer can last for up to 24 hours; if it persists
for more than a few minutes, check the order before you retry with a new key.
- **422** `idempotency_mismatch`: the key was already used for another order or another body. Nothing was done.
Use a new key.
- **500** `idempotency_outcome_unknown`: the request failed after the cancellation may have been made. The same
key gives this answer again for 24 hours. Check the order before you retry with a new key.
- **503** `idempotency_unavailable`: the key could not be checked, and nothing was done. Retry with the same key.

Without the header the endpoint behaves exactly as before.

- Surface: `/api/public` · operationId `cancelOrder` · Group: Order
- Authorisation: Token with scope `public:orders:write`
- Organisation key: needs the capabilities `orders:manage` + `customers:read` on the clinic, all together
- The spec: https://developers.rivr.io/openapi/api-public.json
- The spec's introduction: https://developers.rivr.io/reference/api-public.md
- Page in the portal: https://developers.rivr.io/reference/api-public/cancelOrder

The examples target the sandbox (`https://api.test.rivr.io`) and need test credentials.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orderId` | path | string | yes | The identifier of the order to cancel. |
| `Idempotency-Key` | header | string | no | Optional. A key of 1 to 255 printable ASCII characters, unique for every cancellation (a UUID is recommended). Retrying with the same key and the same request returns the stored response instead of cancelling again. Stored for 24 hours. |

## Responses

### 204

No Content

### 400

The order cannot be cancelled, and retrying will not change that: `order_paid`, `payment_pending`, `order_refunded` or
`order_not_cancellable`. The order id is not a UUID: `order_id_invalid`, with `propertyName` `orderId`. With an
Idempotency-Key header also `idempotency_key_invalid`. A rule in Rivr that refuses the cancellation gives
`request_rejected` with the reason in `message`.

Error codes: [request_rejected](https://developers.rivr.io/errors/request_rejected.md), [order_id_invalid](https://developers.rivr.io/errors/order_id_invalid.md), [order_paid](https://developers.rivr.io/errors/order_paid.md), [payment_pending](https://developers.rivr.io/errors/payment_pending.md), [order_refunded](https://developers.rivr.io/errors/order_refunded.md), [order_not_cancellable](https://developers.rivr.io/errors/order_not_cancellable.md), [idempotency_key_invalid](https://developers.rivr.io/errors/idempotency_key_invalid.md)

`application/json`

```json
{
  "propertyName": "Status",
  "message": "The order has been paid and can no longer be cancelled. Use POST /api/public/orders/{orderId}/refund to refund the payment.",
  "errorCode": "order_paid",
  "code": "order_paid",
  "type": "https://developers.rivr.io/errors/order_paid"
}
```

### 401

The token is missing (`token_missing`), not valid (`token_invalid`), expired (`token_expired`) or could not be
checked (`authentication_unavailable`).

Error codes: [token_missing](https://developers.rivr.io/errors/token_missing.md), [token_invalid](https://developers.rivr.io/errors/token_invalid.md), [token_expired](https://developers.rivr.io/errors/token_expired.md), [authentication_unavailable](https://developers.rivr.io/errors/authentication_unavailable.md)

`application/json`

```json
{
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid Authorization Header",
  "propertyName": null,
  "message": "The request has no valid Authorization header. Send the token in the Authorization header, after Bearer.",
  "errorCode": "token_missing",
  "code": "token_missing",
  "type": "https://developers.rivr.io/errors/token_missing"
}
```

### 403

The token lacks the scope (`invalid_scope`), is a rivr-auth token that is not a clinic token
(`clinic_token_required`), or the order belongs to another merchant on the same platform (`merchant_mismatch`). The
body also carries `error` and `error_description`, as before.

Error codes: [invalid_scope](https://developers.rivr.io/errors/invalid_scope.md), [clinic_token_required](https://developers.rivr.io/errors/clinic_token_required.md), [merchant_mismatch](https://developers.rivr.io/errors/merchant_mismatch.md)

`application/json`

```json
{
  "error": "invalid_scope",
  "error_description": "The scope(s) 'public:orders:write' is required.",
  "propertyName": null,
  "message": "The scope(s) 'public:orders:write' is required.",
  "errorCode": "invalid_scope",
  "code": "invalid_scope",
  "type": "https://developers.rivr.io/errors/invalid_scope"
}
```

### 404

No order with this id is available to the API key: `order_not_found`. Nothing was cancelled.

Error codes: [order_not_found](https://developers.rivr.io/errors/order_not_found.md)

`application/json`

```json
{
  "propertyName": "orderId",
  "message": "The order was not found",
  "errorCode": "order_not_found",
  "code": "order_not_found",
  "type": "https://developers.rivr.io/errors/order_not_found"
}
```

### 409

The same request can succeed later: `payment_in_progress` or `order_changed`; with an Idempotency-Key header also
`idempotency_in_progress`.

Error codes: [payment_in_progress](https://developers.rivr.io/errors/payment_in_progress.md), [order_changed](https://developers.rivr.io/errors/order_changed.md), [idempotency_in_progress](https://developers.rivr.io/errors/idempotency_in_progress.md)

`application/json`

```json
{
  "propertyName": "HasInFlightTerminalPaymentAttempt",
  "message": "A terminal payment attempt is in flight for this order and it cannot be cancelled right now. Retry once the attempt has completed or failed.",
  "errorCode": "payment_in_progress",
  "code": "payment_in_progress",
  "type": "https://developers.rivr.io/errors/payment_in_progress"
}
```

### 422

With an Idempotency-Key header: `idempotency_mismatch`.

Error codes: [idempotency_mismatch](https://developers.rivr.io/errors/idempotency_mismatch.md)

`application/json`

```json
{
  "propertyName": "Idempotency-Key",
  "message": "The Idempotency-Key has already been used for a different request (another order or another request body). Nothing was done. Use a new key for every new request.",
  "errorCode": "idempotency_mismatch",
  "code": "idempotency_mismatch",
  "type": "https://developers.rivr.io/errors/idempotency_mismatch"
}
```

### 500

With an Idempotency-Key header: `idempotency_outcome_unknown`; the same key gives this answer again for 24 hours. Without the header: `internal_error`, something unexpected failed. In both cases check the order before you retry.

Error codes: [idempotency_outcome_unknown](https://developers.rivr.io/errors/idempotency_outcome_unknown.md), [internal_error](https://developers.rivr.io/errors/internal_error.md)

`application/json`

```json
{
  "propertyName": "Idempotency-Key",
  "message": "The request failed after the change may have been made, so its outcome is unknown. The request is not repeated for this Idempotency-Key. Check the order or subscription before you retry with a new key.",
  "errorCode": "idempotency_outcome_unknown",
  "code": "idempotency_outcome_unknown",
  "type": "https://developers.rivr.io/errors/idempotency_outcome_unknown"
}
```

### 503

With an Idempotency-Key header: `idempotency_unavailable`. Without the header the endpoint does not return 503 itself.

Error codes: [idempotency_unavailable](https://developers.rivr.io/errors/idempotency_unavailable.md)

`application/json`

```json
{
  "propertyName": "Idempotency-Key",
  "message": "The Idempotency-Key could not be checked right now, so nothing was done. Retry the request with the same key.",
  "errorCode": "idempotency_unavailable",
  "code": "idempotency_unavailable",
  "type": "https://developers.rivr.io/errors/idempotency_unavailable"
}
```

## Example request

```bash
curl -X POST https://api.test.rivr.io/api/public/orders/da4316b7-e2f4-42b0-b0a8-1ed6c24f9738/cancel \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```
