# Orders

> Orders, their payments, fulfillments, refunds and changes.

Source: https://storeware.ai/docs/api-reference/rest/orders

## List orders

`GET /api/v1/stores/{storeId}/orders`

A page of orders, newest first unless `sort` says otherwise. `meta.units` says, for each order on the page, how many units were ordered and how many have been sent; `meta.labelled` names the ones with a postage label bought.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `status` | query | string | One of `created`, `confirmed`, `manual_review`, `processing`, `shipped`, `delivered`, `canceled`. |
| `queue` | query | string | `to_send`: paid for and not all sent, and not on hold. `on_hold`: kept back by the shop. One of `to_send`, `on_hold`. |
| `search` | query | string | An order number, with or without the `#`, matched exactly; or words in the buyer’s email or name. |
| `from` | query | string | Placed on or after this day, `YYYY-MM-DD`, in the shop’s own time. |
| `to` | query | string | Placed on or before this day, `YYYY-MM-DD`, in the shop’s own time. |
| `payment` | query | string | How it was paid, in the words of the export’s Financial Status column. One of `pending`, `paid`, `partially_refunded`, `refunded`, `voided`. |
| `origin` | query | string | Which door placed it. One of `storefront`, `agent`, `admin`, `plugin`, `imported`, `subscription`. |
| `tags` | query | string | Carrying any of these tags, separated by commas. |
| `sort` | query | string | Newest first (`placed_desc`) when not given. One of `placed_desc`, `placed_asc`, `total_desc`, `total_asc`. |
| `limit` | query | integer | How many to return, up to 250. Fifty when not given. From 1 to 250. |
| `offset` | query | integer | How many to skip, for the next page. At least 0. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [Order](https://storeware.ai/docs/api-reference/rest/objects#order) |  |
| `meta` | object |  |
| `meta.total` | integer | How many match, across every page. |
| `meta.units` | map of object | Units ordered and sent, by order id, for the orders on this page. |
| `meta.labelled` | array of string | The orders on this page with a bought, unvoided label going out. |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders" \
  -H "Authorization: Bearer $TOKEN"
```

## Get an order

`GET /api/v1/stores/{storeId}/orders/{orderId}`

The order with its lines and its adjustments. With `format=acp`, the same order in the Agentic Commerce Protocol’s shape instead.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `format` | query | string | `acp` for the Agentic Commerce Protocol’s shape. One of `acp`. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID" \
  -H "Authorization: Bearer $TOKEN"
```

## Cancel an order

`DELETE /api/v1/stores/{storeId}/orders/{orderId}`

Cancels the order and puts back the stock not yet sent. It refunds nothing: refund it separately. Cancelling an order already cancelled changes nothing. Emits `order.canceled.v1`.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**, which may be left out: it is then read as `{}`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string | No | Why, kept on the order’s history. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [Order](https://storeware.ai/docs/api-reference/rest/objects#order) |  |

```bash
curl -X DELETE "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Correct an order’s contact details or address

`PATCH /api/v1/stores/{storeId}/orders/{orderId}`

Corrects the buyer’s email, name or phone, or the delivery address. Never moves money: tax stays as charged, and a difference in delivery is settled by editing the order. Refused for an order wholly sent, one with a live label or a supplier, and for a paid order moving to another country. Changing the email does not change whose order it is, and the old address is told.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `buyerEmail` | string (email) | No | Up to 320 characters. |
| `buyerFirstName` | string or null | No | Up to 120 characters. |
| `buyerLastName` | string or null | No | Up to 120 characters. |
| `buyerPhone` | string or null | No | Up to 40 characters. |
| `shippingAddress` | object | No |  |
| `shippingAddress.name` | string or null | No | Up to 200 characters. |
| `shippingAddress.line_one` | string | Yes | 1 to 200 characters. |
| `shippingAddress.line_two` | string or null | No | Up to 200 characters. |
| `shippingAddress.city` | string | Yes | 1 to 120 characters. |
| `shippingAddress.state` | string or null | No | Up to 120 characters. |
| `shippingAddress.country` | string | Yes | Matches `^[A-Z]{2}$`. |
| `shippingAddress.postal_code` | string | Yes | 1 to 32 characters. |
| `acknowledged` | string | No | 64 to 64 characters. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.fields` | array of string | Which fields would change, by name. |
| `data.notices` | array of object |  |
| `data.notices[].kind` | string | One of `tax_stays`, `delivery_differs`, `delivery_unquoted`. |
| `data.notices[].charged` | integer | What was charged for delivery, in minor units. |
| `data.notices[].quoted` | integer | What this address would come to, in minor units. |
| `data.digest` | string | Pass this as `acknowledged` when there are notices. |

```bash
curl -X PATCH "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List orders waiting on a card check

`GET /api/v1/stores/{storeId}/orders/authentications`

Orders whose payment is held for a card issuer’s check (such as 3D Secure) that the buyer has not finished, the link soonest to expire first. The links themselves are never listed.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `limit` | query | integer | How many to return. Fifty when not given. From 1 to 200. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].sessionId` | string (uuid) | The checkout session. Renew its link with this id. |
| `data[].orderId` | string (uuid) |  |
| `data[].orderNumber` | string |  |
| `data[].buyerEmail` | string |  |
| `data[].totalAmount` | integer | Minor units. |
| `data[].currency` | string |  |
| `data[].origin` | string | Which door placed the order. |
| `data[].expiresAt` | string (date-time) or null | When the link stops working. Null once it is opened. |
| `data[].state` | string | `outstanding`: nobody has opened the link. `expired`: nobody opened it in time. `opened`: the buyer is answering the challenge now. One of `outstanding`, `expired`, `opened`. |
| `data[].createdAt` | string (date-time) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/authentications" \
  -H "Authorization: Bearer $TOKEN"
```

## Renew a card check link

`POST /api/v1/stores/{storeId}/orders/authentications/{sessionId}`

Makes a new link for a buyer to finish their card check, and answers with it. The new link replaces the old one, which stops working. Whoever holds the link can pay for and then read the order, so send it only to the buyer and do not store it. Refused for a checkout that is not waiting on a check.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `sessionId` | path | string | The session. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.url` | string | The link, shown this once. |
| `data.expiresAt` | string (date-time) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/authentications/$SESSION_ID" \
  -H "Authorization: Bearer $TOKEN"
```

## Approve or reject a held order

`POST /api/v1/stores/{storeId}/orders/{orderId}/approval`

Answers an order an AI agent placed above the shop’s approval limit, which waits in `manual_review`. Approving takes the payment, which is the first time money moves, so it can fail as any payment can; it answers the order and anything the payment still needs. Rejecting cancels the order, puts its stock back and emits `order.canceled.v1`; it answers the order.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `decision` | string | Yes | One of `approve`, `reject`. |
| `reason` | string | No | Why it was rejected. Read only when rejecting. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object or [Order](https://storeware.ai/docs/api-reference/rest/objects#order) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/approval" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List an order’s changes

`GET /api/v1/stores/{storeId}/orders/{orderId}/changes`

Every change proposed or made to the order since it was placed, oldest first.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [OrderChange](https://storeware.ai/docs/api-reference/rest/objects#orderchange) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/changes" \
  -H "Authorization: Bearer $TOKEN"
```

## Open an order change

`POST /api/v1/stores/{storeId}/orders/{orderId}/changes`

Opens an empty change to an order, to edit what it holds. Add lines to it, then confirm it. Nothing moves until it is confirmed.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**, which may be left out: it is then read as `{}`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | string | No | One of `edit`, `return`, `exchange`, `claim`. Default `edit`. |
| `reason` | string | No | Up to 500 characters. |
| `note` | string | No | Up to 2,000 characters. |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [OrderChange](https://storeware.ai/docs/api-reference/rest/objects#orderchange) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/changes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Get an order change

`GET /api/v1/stores/{storeId}/orders/{orderId}/changes/{changeId}`

The change, its lines, and in `delta` what it would cost if confirmed now.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `changeId` | path | string | The change. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.change` | object |  |
| `data.change.id` | string (uuid) |  |
| `data.change.storeId` | string (uuid) |  |
| `data.change.orderId` | string (uuid) |  |
| `data.change.kind` | string | One of `edit`, `return`, `exchange`, `claim`. |
| `data.change.status` | string | One of `pending`, `confirmed`, `declined`, `canceled`. |
| `data.change.reason` | string or null |  |
| `data.change.note` | string or null |  |
| `data.change.deltaSubtotalAmount` | number |  |
| `data.change.deltaDiscountAmount` | number |  |
| `data.change.deltaFulfillmentAmount` | number |  |
| `data.change.deltaTaxAmount` | number |  |
| `data.change.deltaTotalAmount` | number | What the change came to, signed and in minor units: positive means the customer owes more. Zero until it is confirmed, as are the other deltas. |
| `data.change.currency` | string |  |
| `data.change.adjustmentId` | string (uuid) or null | The refund it made, when it gave money back. |
| `data.change.paymentId` | string (uuid) or null |  |
| `data.change.actorType` | string or null | One of `staff`, `customer`, `plugin`, `agent`, `system`. |
| `data.change.actorId` | string or null |  |
| `data.change.createdAt` | string (date-time) |  |
| `data.change.updatedAt` | string (date-time) |  |
| `data.change.confirmedAt` | string (date-time) or null |  |
| `data.change.declinedAt` | string (date-time) or null |  |
| `data.change.canceledAt` | string (date-time) or null |  |
| `data.actions` | array of object |  |
| `data.actions[].id` | string (uuid) |  |
| `data.actions[].storeId` | string (uuid) |  |
| `data.actions[].changeId` | string (uuid) |  |
| `data.actions[].action` | string | One of `item_add`, `item_quantity`, `item_remove`, `shipping_add`, `shipping_remove`. |
| `data.actions[].lineItemId` | string (uuid) or null |  |
| `data.actions[].variantId` | string (uuid) or null |  |
| `data.actions[].quantityDelta` | number | Signed. Negative takes units off the line. |
| `data.actions[].unitAmount` | number | Minor units. |
| `data.actions[].amount` | number | Minor units. |
| `data.actions[].details` | any |  |
| `data.actions[].createdAt` | string (date-time) |  |
| `data.delta` | object | What the change would cost if confirmed now, priced against the catalogue as it stands. Signed and in minor units: positive means the customer owes more. |
| `data.delta.subtotalAmount` | integer |  |
| `data.delta.discountAmount` | integer |  |
| `data.delta.fulfillmentAmount` | integer |  |
| `data.delta.taxAmount` | integer |  |
| `data.delta.totalAmount` | integer |  |
| `data.delta.currency` | string |  |
| `data.delta.lines` | array of object |  |
| `data.delta.lines[].actionId` | string (uuid) |  |
| `data.delta.lines[].lineItemId` | string (uuid) or null |  |
| `data.delta.lines[].title` | string |  |
| `data.delta.lines[].quantityDelta` | integer |  |
| `data.delta.lines[].unitAmount` | integer |  |
| `data.delta.lines[].amount` | integer |  |
| `data.delta.lines[].taxAmount` | integer |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/changes/$CHANGE_ID" \
  -H "Authorization: Bearer $TOKEN"
```

## Confirm, decline or cancel an order change

`POST /api/v1/stores/{storeId}/orders/{orderId}/changes/{changeId}`

Settles a pending change. Confirming moves the stock and the money and emits `order.changed.v1`: a change that gives money back refunds it to the card, and one that leaves the customer owing more is recorded as owed, not charged. A change that would refund an order paid in stablecoins, imported from another platform, or not yet paid for is refused. Declining or cancelling closes it with nothing moved.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `changeId` | path | string | The change. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `decision` | string | Yes | One of `confirm`, `decline`, `cancel`. |
| `reason` | string | No | Why. Read only when declining or cancelling. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [OrderChange](https://storeware.ai/docs/api-reference/rest/objects#orderchange) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/changes/$CHANGE_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Add a line to an order change

`POST /api/v1/stores/{storeId}/orders/{orderId}/changes/{changeId}/actions`

Adds one action to a pending change: add a variant, change a line’s quantity, remove a line, or add or take off delivery. Checked against the order as it stands now. Amounts are in minor units.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `changeId` | path | string | The change. |

**Body**

Any JSON.

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [OrderChange](https://storeware.ai/docs/api-reference/rest/objects#orderchange) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/changes/$CHANGE_ID/actions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Remove a line from an order change

`DELETE /api/v1/stores/{storeId}/orders/{orderId}/changes/{changeId}/actions/{actionId}`

Takes an action off a change nobody has confirmed yet.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `changeId` | path | string | The change. |
| `actionId` | path | string | The action. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [OrderChange](https://storeware.ai/docs/api-reference/rest/objects#orderchange) |  |

```bash
curl -X DELETE "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/changes/$CHANGE_ID/actions/$ACTION_ID" \
  -H "Authorization: Bearer $TOKEN"
```

## List an order’s downloads

`GET /api/v1/stores/{storeId}/orders/{orderId}/downloads`

Every file the order bought, with how many times it has been downloaded and how many more it may be. An unpaid, cancelled or refunded order is listed too, with `available` false and the reason.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [DownloadGrant](https://storeware.ai/docs/api-reference/rest/objects#downloadgrant) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/downloads" \
  -H "Authorization: Bearer $TOKEN"
```

## Set a download allowance

`PATCH /api/v1/stores/{storeId}/orders/{orderId}/downloads`

Sets how many times one line of the order may download one file. It cannot be set below the downloads already taken; set it to that count to stop further downloads.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `lineItemId` | string (uuid) | Yes |  |
| `assetId` | string (uuid) | Yes | The file. |
| `downloadLimit` | integer or null | Yes | How many downloads in all. Null for unlimited. From 1 to 1,000. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [DownloadGrant](https://storeware.ai/docs/api-reference/rest/objects#downloadgrant) |  |

```bash
curl -X PATCH "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/downloads" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List an order’s fulfillments

`GET /api/v1/stores/{storeId}/orders/{orderId}/fulfillments`

Every despatch of the order, with its tracking events and the lines it carries.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [Fulfillment](https://storeware.ai/docs/api-reference/rest/objects#fulfillment) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/fulfillments" \
  -H "Authorization: Bearer $TOKEN"
```

## Create a fulfillment

`POST /api/v1/stores/{storeId}/orders/{orderId}/fulfillments`

Records a despatch of some or all of an order’s lines, and takes the stock off the shelf. A line cannot be sent more times than is left of it. Emits `fulfillment.created.v1`. Send an `Idempotency-Key` header to make a retry safe: a second request with the same key answers what the first did.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | Yes | One of `shipping`, `pickup`, `digital`. |
| `lineItems` | array of object | Yes | At least 1 item. |
| `lineItems[].lineItemId` | string (uuid) | Yes |  |
| `lineItems[].quantity` | integer | Yes |  |
| `locationId` | string (uuid) | No |  |
| `carrier` | string | No | Up to 120 characters. |
| `trackingNumber` | string | No | Up to 120 characters. |
| `trackingUrl` | string (uri) | No | Up to 2,000 characters. |
| `estimatedDeliveryEarliest` | string (date-time) | No | The earliest it should arrive. |
| `estimatedDeliveryLatest` | string (date-time) | No | The latest it should arrive. |
| `digitalAccessUrl` | string (uri) | No |  |
| `digitalLicenseKey` | string | No | Up to 500 characters. |
| `notifyBuyer` | boolean | No | Default `true`. |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [Fulfillment](https://storeware.ai/docs/api-reference/rest/objects#fulfillment) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/fulfillments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Record a tracking event

`POST /api/v1/stores/{storeId}/orders/{orderId}/fulfillments/{fulfillmentId}/events`

Says where a despatch has got to, and moves its status and the order’s with it. `failed_attempt` leaves the status as it is; `returned` marks the despatch failed. Emits `fulfillment.updated.v1`, and `delivered` writes to the buyer. Send an `Idempotency-Key` header to make a retry safe: a second request with the same key answers what the first did.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `fulfillmentId` | path | string | The fulfillment. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | Yes | One of `processing`, `shipped`, `in_transit`, `out_for_delivery`, `ready_for_pickup`, `delivered`, `failed_attempt`, `returned`. |
| `occurredAt` | string (date-time) | No | When it happened. Now when not given. |
| `description` | string | No | Up to 500 characters. |
| `location` | string | No | Up to 200 characters. |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [Fulfillment](https://storeware.ai/docs/api-reference/rest/objects#fulfillment) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/fulfillments/$FULFILLMENT_ID/events" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Get what a shipping label needs

`GET /api/v1/stores/{storeId}/orders/{orderId}/label-quotes`

What a label for this order would start from: what is left to send and what it weighs, the shop’s boxes, and whether a card and a ship from address are in place. Asks no carrier.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.unavailable` | string or null | Why no label can be bought for this shop. Null when one can. One of `unconfigured`, `mode_mismatch`, `not_offered`. |
| `data.mode` | string or null | One of `test`, `live`. |
| `data.lines` | array of object |  |
| `data.lines[].lineItemId` | string (uuid) |  |
| `data.lines[].title` | string |  |
| `data.lines[].variantTitle` | string or null |  |
| `data.lines[].free` | integer | Units not sent yet and not held by a label still being bought. |
| `data.lines[].weightGrams` | integer or null | One unit. Null when the product has no weight. |
| `data.packages` | array of object | The boxes the shop keeps. |
| `data.packages[].id` | string (uuid) |  |
| `data.packages[].storeId` | string (uuid) |  |
| `data.packages[].name` | string |  |
| `data.packages[].lengthMm` | number |  |
| `data.packages[].widthMm` | number |  |
| `data.packages[].heightMm` | number |  |
| `data.packages[].emptyWeightGrams` | number |  |
| `data.packages[].isDefault` | boolean |  |
| `data.packages[].createdAt` | string (date-time) |  |
| `data.packages[].updatedAt` | string (date-time) |  |
| `data.hasShipFrom` | boolean | Whether the shop has said where parcels leave from. |
| `data.card` | object or null | The card postage is charged to. Null when there is none. |
| `data.card.brand` | string or null |  |
| `data.card.last4` | string or null |  |
| `data.blocked` | string or null | Why label buying is paused on the account, if it is. One of `postage_owed`, `dispute`, `review`. |
| `data.international` | boolean | Whether the parcel crosses a border and needs customs. |
| `data.chosen` | object or null | The delivery the shopper paid for, when they chose one. |
| `data.chosen.title` | string |  |
| `data.chosen.amount` | integer | Minor units. |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/label-quotes" \
  -H "Authorization: Bearer $TOKEN"
```

## Quote a shipping label

`POST /api/v1/stores/{storeId}/orders/{orderId}/label-quotes`

Asks the carrier what it would cost to send a parcel for this order, by every service it offers, and checks the address. Name one of: the lines the parcel carries, a despatch with no label yet, or a return to bring back. Buys nothing.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `lines` | array of object | No | Up to 250 items. At least 1 item. |
| `lines[].lineItemId` | string (uuid) | Yes |  |
| `lines[].quantity` | integer | Yes |  |
| `fulfillmentId` | string (uuid) | No |  |
| `returnId` | string (uuid) | No |  |
| `parcel` | object | Yes |  |
| `parcel.packageId` | string (uuid) | No |  |
| `parcel.packageName` | string | No | Up to 120 characters. |
| `parcel.lengthMm` | integer | Yes | From 1 to 5,000. |
| `parcel.widthMm` | integer | Yes | From 1 to 5,000. |
| `parcel.heightMm` | integer | Yes | From 1 to 5,000. |
| `parcel.weightGrams` | integer | Yes | From 1 to 1,000,000. |
| `options` | object | No | Default `{}`. |
| `options.insuranceAmount` | integer | No | At most 100,000,000. |
| `options.signature` | string | No | One of `standard`, `adult`. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.shipmentId` | string | Send it back, with a `rateId`, to buy. |
| `data.rates` | array of object |  |
| `data.rates[].rateId` | string | What a label is bought with. |
| `data.rates[].carrier` | string |  |
| `data.rates[].carrierName` | string |  |
| `data.rates[].serviceToken` | string |  |
| `data.rates[].serviceName` | string |  |
| `data.rates[].amount` | integer | The carrier’s price, in minor units. |
| `data.rates[].currency` | string |  |
| `data.rates[].estimatedDays` | integer |  |
| `data.rates[].attributes` | array of string | The carrier’s own words: `CHEAPEST`, `FASTEST`, `BESTVALUE`. |
| `data.rates[].price` | object | What the card would be charged. |
| `data.rates[].price.postage` | integer | The carrier’s price, in minor units. |
| `data.rates[].price.fee` | integer | Card processing on the whole charge, in minor units. |
| `data.rates[].price.total` | integer |  |
| `data.rates[].payOnUse` | boolean | A return label charged only when a carrier first scans it. |
| `data.refusal` | string or null | Why the carrier offered nothing, when it did not. |
| `data.chosenServiceToken` | string or null | The service the shopper paid for, when it is among the rates. |
| `data.recipient` | object |  |
| `data.recipient.name` | string |  |
| `data.recipient.line_one` | string |  |
| `data.recipient.line_two` | string |  |
| `data.recipient.city` | string |  |
| `data.recipient.state` | string |  |
| `data.recipient.country` | string |  |
| `data.recipient.postal_code` | string |  |
| `data.recipient.phone` | string |  |
| `data.recipient.email` | string |  |
| `data.addressCheck` | object |  |
| `data.addressCheck.checked` | boolean | False when nobody was asked. That is not a verdict. |
| `data.addressCheck.valid` | boolean or null |  |
| `data.addressCheck.messages` | array of string | The carrier’s own sentences about the address. |
| `data.addressCheck.suggestion` | object | The address as the carrier writes it. |
| `data.addressCheck.suggestion.name` | string |  |
| `data.addressCheck.suggestion.line_one` | string |  |
| `data.addressCheck.suggestion.line_two` | string |  |
| `data.addressCheck.suggestion.city` | string |  |
| `data.addressCheck.suggestion.state` | string |  |
| `data.addressCheck.suggestion.country` | string |  |
| `data.addressCheck.suggestion.postal_code` | string |  |
| `data.addressCheck.suggestion.phone` | string |  |
| `data.addressCheck.suggestion.email` | string |  |
| `data.shipFrom` | object |  |
| `data.shipFrom.locationName` | string |  |
| `data.shipFrom.address` | object |  |
| `data.shipFrom.address.name` | string |  |
| `data.shipFrom.address.company` | string |  |
| `data.shipFrom.address.line_one` | string |  |
| `data.shipFrom.address.line_two` | string |  |
| `data.shipFrom.address.city` | string |  |
| `data.shipFrom.address.state` | string |  |
| `data.shipFrom.address.country` | string |  |
| `data.shipFrom.address.postal_code` | string |  |
| `data.shipFrom.address.phone` | string |  |
| `data.shipFrom.address.email` | string |  |
| `data.parcel` | object |  |
| `data.parcel.packageId` | string (uuid) |  |
| `data.parcel.packageName` | string |  |
| `data.parcel.lengthMm` | integer |  |
| `data.parcel.widthMm` | integer |  |
| `data.parcel.heightMm` | integer |  |
| `data.parcel.weightGrams` | integer |  |
| `data.customs` | object or null | What the parcel declares to customs. Null when it crosses no border. |
| `data.customs.contentsType` | string | One of `MERCHANDISE`, `GIFT`, `SAMPLE`, `RETURN_MERCHANDISE`. |
| `data.customs.nonDeliveryOption` | string | One of `RETURN`, `ABANDON`. |
| `data.customs.certifySigner` | string |  |
| `data.customs.incoterm` | string | One of `DDU`, `DDP`. |
| `data.customs.items` | array of object |  |
| `data.customs.items[].description` | string |  |
| `data.customs.items[].quantity` | integer |  |
| `data.customs.items[].netWeightGrams` | integer |  |
| `data.customs.items[].valueAmount` | integer | Minor units. |
| `data.customs.items[].valueCurrency` | string |  |
| `data.customs.items[].originCountry` | string |  |
| `data.customs.items[].tariffNumber` | string |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/label-quotes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List an order’s shipping labels

`GET /api/v1/stores/{storeId}/orders/{orderId}/labels`

Every label the order has had, bought or not, newest first. At most fifty.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [ShippingLabel](https://storeware.ai/docs/api-reference/rest/objects#shippinglabel) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/labels" \
  -H "Authorization: Bearer $TOKEN"
```

## Buy a shipping label

`POST /api/v1/stores/{storeId}/orders/{orderId}/labels`

Buys a label at a rate from a quote and charges the account’s postage card. For lines, it also records the despatch with its tracking number. Answers 201 once the label is bought, and 202 while it is not yet: the bank wants the cardholder (`clientSecret` is set), or the carrier has not answered. Send the same `purchaseKey` again, or resume the label, to carry on; it never buys twice.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `lines` | array of object | No | Up to 250 items. At least 1 item. |
| `lines[].lineItemId` | string (uuid) | Yes |  |
| `lines[].quantity` | integer | Yes |  |
| `fulfillmentId` | string (uuid) | No |  |
| `returnId` | string (uuid) | No |  |
| `parcel` | object | Yes |  |
| `parcel.packageId` | string (uuid) | No |  |
| `parcel.packageName` | string | No | Up to 120 characters. |
| `parcel.lengthMm` | integer | Yes | From 1 to 5,000. |
| `parcel.widthMm` | integer | Yes | From 1 to 5,000. |
| `parcel.heightMm` | integer | Yes | From 1 to 5,000. |
| `parcel.weightGrams` | integer | Yes | From 1 to 1,000,000. |
| `options` | object | No | Default `{}`. |
| `options.insuranceAmount` | integer | No | At most 100,000,000. |
| `options.signature` | string | No | One of `standard`, `adult`. |
| `purchaseKey` | string | Yes | 8 to 200 characters. |
| `shipmentId` | string | Yes | 1 to 100 characters. |
| `rateId` | string | Yes | 1 to 100 characters. |
| `notifyBuyer` | boolean | No | Default `true`. |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [ShippingLabel](https://storeware.ai/docs/api-reference/rest/objects#shippinglabel) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/labels" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Resume a shipping label purchase

`POST /api/v1/stores/{storeId}/orders/{orderId}/labels/{labelId}/resume`

Carries an unfinished purchase on, once the bank’s check has been answered. Does nothing to a finished label, so it is safe to call any number of times. Answers 202 while the label is still not bought.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `labelId` | path | string | The label. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [ShippingLabel](https://storeware.ai/docs/api-reference/rest/objects#shippinglabel) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/labels/$LABEL_ID/resume" \
  -H "Authorization: Bearer $TOKEN"
```

## Void a shipping label

`POST /api/v1/stores/{storeId}/orders/{orderId}/labels/{labelId}/void`

Asks the carrier to refund a label that has not been used. Usually answers 202 with the label `void_pending`: a carrier takes days to agree, and the postage goes back to the card when it does. The card processing is not returned. The despatch stays, with its tracking number cleared, so a new label can be bought for it. Asking twice is safe.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `labelId` | path | string | The label. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [ShippingLabel](https://storeware.ai/docs/api-reference/rest/objects#shippinglabel) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/labels/$LABEL_ID/void" \
  -H "Authorization: Bearer $TOKEN"
```

## List an order’s metafields

`GET /api/v1/stores/{storeId}/orders/{orderId}/metafields`

Every metafield value set on an order, each with its definition.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].definitionId` | string (uuid) |  |
| `data[].namespace` | string |  |
| `data[].key` | string |  |
| `data[].label` | string | The merchant’s name for the field. |
| `data[].type` | string | single_line_text, multi_line_text, integer, decimal, boolean, date, url and the other definition types. |
| `data[].isList` | boolean |  |
| `data[].storefrontVisible` | boolean |  |
| `data[].searchFacet` | boolean |  |
| `data[].position` | integer |  |
| `data[].value` | any | The value, or a list of values when `isList`. |
| `data[].displayValue` | string | The value as one line of text, lists joined. |
| `data[].source` | string | Who set it: the merchant, an import, or an app’s slug. |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/metafields" \
  -H "Authorization: Bearer $TOKEN"
```

## Set an order’s metafields

`PUT /api/v1/stores/{storeId}/orders/{orderId}/metafields`

Sets up to 25 values in one call. A null value clears its field; keys not named are left alone. An app may set only the fields its manifest declares.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `metafields` | array of object | Yes | Up to 25 items. At least 1 item. |
| `metafields[].namespace` | string | No | Default `custom`. |
| `metafields[].key` | string | Yes |  |
| `metafields[].value` | any | No |  |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].definitionId` | string (uuid) |  |
| `data[].namespace` | string |  |
| `data[].key` | string |  |
| `data[].label` | string | The merchant’s name for the field. |
| `data[].type` | string | single_line_text, multi_line_text, integer, decimal, boolean, date, url and the other definition types. |
| `data[].isList` | boolean |  |
| `data[].storefrontVisible` | boolean |  |
| `data[].searchFacet` | boolean |  |
| `data[].position` | integer |  |
| `data[].value` | any | The value, or a list of values when `isList`. |
| `data[].displayValue` | string | The value as one line of text, lists joined. |
| `data[].source` | string | Who set it: the merchant, an import, or an app’s slug. |

```bash
curl -X PUT "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/metafields" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Add a return reason

`POST /api/v1/stores/{storeId}/return-reasons`

A reason of the shop’s own, in its own words. It goes last and is offered from the next return. `code` is one of the platform’s, because review rules read that and not the words.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | Yes | One of `wrong_size`, `wrong_item_sent`, `damaged_in_transit`, `defective`, `not_as_described`, `looks_different_to_photo`, `quality_below_expectation`, `arrived_late`, `changed_mind`, `other`. |
| `label` | string | Yes | 1 to 80 characters. |
| `refundsShipping` | boolean | No |  |
| `waivesRestockingFee` | boolean | No |  |
| `requiresReturn` | boolean | No |  |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string (uuid) |  |
| `data.storeId` | string (uuid) |  |
| `data.code` | string | One of `wrong_size`, `wrong_item_sent`, `damaged_in_transit`, `defective`, `not_as_described`, `looks_different_to_photo`, `quality_below_expectation`, `arrived_late`, `changed_mind`, `other`. |
| `data.label` | string |  |
| `data.refundsShipping` | boolean |  |
| `data.waivesRestockingFee` | boolean |  |
| `data.requiresReturn` | boolean |  |
| `data.defaultDisposition` | string | One of `restock`, `damaged`, `discard`, `quarantine`, `return_to_vendor`. |
| `data.position` | number |  |
| `data.shopAdded` | boolean |  |
| `data.archivedAt` | string (date-time) or null |  |
| `data.createdAt` | string (date-time) |  |
| `data.updatedAt` | string (date-time) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/return-reasons" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Take back a despatch

`DELETE /api/v1/stores/{storeId}/orders/{orderId}/fulfillments/{fulfillmentId}`

For a parcel recorded and never really sent. Units go back to the shelves they came from, held for the order again, and the order steps back. Refused once a carrier has it, for a supplier’s or a digital despatch, for units refunded or returned, and while a label is live. The buyer is not emailed.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `fulfillmentId` | path | string | The fulfillment. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.fulfillment` | [Fulfillment](https://storeware.ai/docs/api-reference/rest/objects#fulfillment) |  |
| `data.restocked` | integer | Units that went back on a shelf. |
| `data.estimated` | boolean | True when no shelf record exists and the despatch’s one location was used. |

```bash
curl -X DELETE "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/fulfillments/$FULFILLMENT_ID" \
  -H "Authorization: Bearer $TOKEN"
```

## Correct a despatch’s tracking

`PATCH /api/v1/stores/{storeId}/orders/{orderId}/fulfillments/{fulfillmentId}`

Corrects a carrier or tracking number somebody typed. Refused for a label bought here, whose number belongs to the label, and for a cancelled despatch. No email is sent.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |
| `fulfillmentId` | path | string | The fulfillment. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `carrier` | string | Yes | 1 to 120 characters. |
| `trackingNumber` | string | Yes | 1 to 120 characters. |
| `trackingUrl` | string (uri) or null | No | Up to 2,000 characters. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [Fulfillment](https://storeware.ai/docs/api-reference/rest/objects#fulfillment) |  |

```bash
curl -X PATCH "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/fulfillments/$FULFILLMENT_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## See what correcting an order’s details would mean

`POST /api/v1/stores/{storeId}/orders/{orderId}/details/preview`

Says which fields would change, and whether tax or delivery is affected. Changes nothing. When it has notices, hand its `digest` to `updateOrderDetails` as `acknowledged`.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `buyerEmail` | string (email) | No | Up to 320 characters. |
| `buyerFirstName` | string or null | No | Up to 120 characters. |
| `buyerLastName` | string or null | No | Up to 120 characters. |
| `buyerPhone` | string or null | No | Up to 40 characters. |
| `shippingAddress` | object | No |  |
| `shippingAddress.name` | string or null | No | Up to 200 characters. |
| `shippingAddress.line_one` | string | Yes | 1 to 200 characters. |
| `shippingAddress.line_two` | string or null | No | Up to 200 characters. |
| `shippingAddress.city` | string | Yes | 1 to 120 characters. |
| `shippingAddress.state` | string or null | No | Up to 120 characters. |
| `shippingAddress.country` | string | Yes | Matches `^[A-Z]{2}$`. |
| `shippingAddress.postal_code` | string | Yes | 1 to 32 characters. |
| `acknowledged` | string | No | 64 to 64 characters. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.fields` | array of string | Which fields would change, by name. |
| `data.notices` | array of object |  |
| `data.notices[].kind` | string | One of `tax_stays`, `delivery_differs`, `delivery_unquoted`. |
| `data.notices[].charged` | integer | What was charged for delivery, in minor units. |
| `data.notices[].quoted` | integer | What this address would come to, in minor units. |
| `data.digest` | string | Pass this as `acknowledged` when there are notices. |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/details/preview" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Keep an order back from being sent

`POST /api/v1/stores/{storeId}/orders/{orderId}/hold`

Stops a paid order being sent until it is released. Nothing else about it changes and the shopper is not told. An order already on hold keeps the reason it was first held for.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string | Yes | 1 to 500 characters. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.held` | boolean |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/hold" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Release an order kept back

`DELETE /api/v1/stores/{storeId}/orders/{orderId}/hold`

Lets the order be sent again. An order that was not held is left as it is.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.held` | boolean |  |

```bash
curl -X DELETE "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/hold" \
  -H "Authorization: Bearer $TOKEN"
```

## List the tags a shop’s orders carry

`GET /api/v1/stores/{storeId}/orders/facets`

For the controls that narrow the list. The commonest first, at most a hundred.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.tags` | array of string |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/facets" \
  -H "Authorization: Bearer $TOKEN"
```

## Change an order’s tags

`PATCH /api/v1/stores/{storeId}/orders/{orderId}/tags`

Puts tags on and takes them off by name, so two people labelling one order do not lose each other’s work. Sending the same change twice leaves the order as the first left it. Tags are the shop’s own and are never shown to a shopper.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `add` | array of string | No | Up to 50 items. Default `[]`. |
| `remove` | array of string | No | Up to 50 items. Default `[]`. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.tags` | array of string |  |

```bash
curl -X PATCH "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/tags" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List an order’s notes

`GET /api/v1/stores/{storeId}/orders/{orderId}/notes`

What the shop has written about the order, oldest first.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [OrderNote](https://storeware.ai/docs/api-reference/rest/objects#ordernote) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/notes" \
  -H "Authorization: Bearer $TOKEN"
```

## Add a note to an order

`POST /api/v1/stores/{storeId}/orders/{orderId}/notes`

Adds a note for the shop’s staff. Notes cannot be edited or deleted; a correction is another note.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `body` | string | Yes | Trimmed of spaces before it is measured. 1 to 5,000 characters. |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [OrderNote](https://storeware.ai/docs/api-reference/rest/objects#ordernote) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/notes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Record an invoice payment

`POST /api/v1/stores/{storeId}/orders/{orderId}/payments`

Records that an order placed on account has been paid, by bank transfer, cheque or any other way outside the shop. Only for an order on payment terms, and only once. Emits `order.paid.v1`, so what follows a card payment follows this too.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**, which may be left out: it is then read as `{}`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount` | integer | No | At least 0. |
| `method` | string | No | Up to 80 characters. |
| `reference` | string | No | Up to 200 characters. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [Order](https://storeware.ai/docs/api-reference/rest/objects#order) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/payments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List an order’s refunds

`GET /api/v1/stores/{storeId}/orders/{orderId}/refunds`

Every adjustment to the order’s money, newest first: refunds, and store credit, returns, exchanges, cancellations and disputes.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [OrderAdjustment](https://storeware.ai/docs/api-reference/rest/objects#orderadjustment) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/refunds" \
  -H "Authorization: Bearer $TOKEN"
```

## Refund an order

`POST /api/v1/stores/{storeId}/orders/{orderId}/refunds`

Refunds all or part of an order, everything still refundable when `amount` is left out. A card payment is refunded to the card; an order on account or paid with credit is recorded as refunded with nothing sent. Refused for an order imported from another platform, one paid in stablecoins, one whose payment has not completed, and an amount past what is left. Emits `order.refunded.v1`. Send an `Idempotency-Key` header to make a retry safe: a second request with the same key answers what the first did.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**, which may be left out: it is then read as `{}`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount` | integer | No |  |
| `reason` | string | No | One of `duplicate`, `fraudulent`, `requested_by_customer`, `other`. |
| `description` | string | No | Up to 500 characters. |
| `lineItems` | array of object | No |  |
| `lineItems[].lineItemId` | string (uuid) | Yes |  |
| `lineItems[].quantity` | integer | Yes |  |
| `restock` | boolean | No | Default `false`. |
| `valueOfLines` | boolean | No |  |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.adjustment` | [OrderAdjustment](https://storeware.ai/docs/api-reference/rest/objects#orderadjustment) |  |
| `data.refundedAmount` | integer | Refunded on the order so far, this included. |
| `data.refundableAmount` | integer | The total less what has been refunded. |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/refunds" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List an order’s returns

`GET /api/v1/stores/{storeId}/orders/{orderId}/returns`

The returns asked for on this order, and in `returnable` each line with what could still come back and why not. Also open to `returns:read`, a permission for the shop front.

Needs `orders:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.returns` | array of [Return](https://storeware.ai/docs/api-reference/rest/objects#return) |  |
| `data.returnable` | array of object |  |
| `data.returnable[].lineItemId` | string (uuid) |  |
| `data.returnable[].variantId` | string (uuid) or null |  |
| `data.returnable[].productId` | string (uuid) or null |  |
| `data.returnable[].title` | string |  |
| `data.returnable[].variantTitle` | string or null |  |
| `data.returnable[].sku` | string or null |  |
| `data.returnable[].imageUrl` | string or null |  |
| `data.returnable[].quantityOrdered` | integer |  |
| `data.returnable[].quantityShipped` | integer |  |
| `data.returnable[].quantityReturned` | integer |  |
| `data.returnable[].returnable` | integer | What could be asked to come back now. |
| `data.returnable[].unitAmount` | integer | Minor units. |
| `data.returnable[].refundValue` | integer | What those units are worth back, tax included, in minor units. |
| `data.returnable[].taxValue` | integer | The tax in `refundValue`. |
| `data.returnable[].deliveredAt` | string (date-time) or null |  |
| `data.returnable[].windowClosesAt` | string (date-time) or null |  |
| `data.returnable[].eligible` | boolean |  |
| `data.returnable[].ineligibleReason` | string or null | One of `window_closed`, `nothing_shipped`, `fully_returned`. |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/returns" \
  -H "Authorization: Bearer $TOKEN"
```

## Open a return

`POST /api/v1/stores/{storeId}/orders/{orderId}/returns`

Opens a return of some of an order’s sent lines. With `orders:write` a closed return window can be overridden. Approved at once when the shop’s policy approves returns on its own. Emits `return.requested.v1`. Also open to `returns:write`, a permission for the shop front.

Needs `orders:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `orderId` | path | string | The order. |

**Body**, which may be left out: it is then read as `{}`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `lines` | array of object | Yes | At least 1 item. |
| `lines[].lineItemId` | string (uuid) | Yes |  |
| `lines[].quantity` | integer | Yes |  |
| `lines[].reasonId` | string (uuid) | No |  |
| `lines[].note` | string | No | Up to 500 characters. |
| `overrideWindow` | boolean | No | Default `false`. |
| `origin` | string | No | One of `storefront`, `agent`, `admin`, `plugin`. |
| `merchantNote` | string | No | Up to 2,000 characters. |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [Return](https://storeware.ai/docs/api-reference/rest/objects#return) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/orders/$ORDER_ID/returns" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```
