Cancel order items#

This guide walks through cancelling items of an order before shipment: cancelling the items, approving the cancellation, optionally crediting back fees and restocking, then finishing the process. The flow follows a repeating pattern — mutate, inspect order.actions to determine the next required step, and repeat until you call finishOrderItems.

Overview#

  1. Cancel items with cancelOrderItems
  2. Approve the cancellation with approveOrderItems
  3. (Optional) Credit back fees with addContraFeesToOrderItems
  4. (Optional) Restock items with restockOrderItems
  5. Finish the process with finishOrderItems
  6. (Optional) Notify the customer with sendCancellationEmail

Understanding order actions#

After each mutation, the response includes order.actions. This array tells you which step to execute next and which item IDs are involved. Always check it before proceeding.

action valueNext step
HANDLE_APPROVALCall approveOrderItems
HANDLE_RESTOCKCall restockOrderItems
HANDLE_FINISHCall finishOrderItems

Understanding contra items#

When you cancel an item, you create a "contra" version of it. You generate a UUID v4 for contra_item_id yourself before calling the mutation (if you leave it empty one is generated for you, but then you won't know the ID). This ID is what you pass to subsequent steps (approve, restock, finish) instead of the original item_id.


Step 1 — Cancel the items#

Input: CancelOrderItemsInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
items
[CancelOrderItemInput!]!
RequiredItems to cancel.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation cancelProducts($input: CancelOrderItemsInput!) {
  cancelOrderItems(input: $input) {
    order {
      id
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "items": [
      {
        "item_id": "5a6b7c8d-9e0f-1234-5678-9abcdef01234",
        "contra_item_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "reason": "CUSTOMER_REQUEST"
      }
    ]
  }
}
Tip:

Generate one UUID per item before the request and store them locally — you will need the contra_item_id values in every step that follows.


Step 2 — Approve the cancellation#

Pass the contra_item_ids from step 1 as items. The response includes the current fees on the order and the next required action.

Input: ApproveOrderItemsInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
items
[String!]!
RequiredThe contra_item_id values from step 1.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation approveProducts($input: ApproveOrderItemsInput!) {
  approveOrderItems(input: $input) {
    order {
      id
      actions {
        action
        ids
      }
      fees {
        payment {
          id
        }
        shipping {
          id
        }
      }
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "items": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
  }
}

Check order.actions in the response to determine which step comes next.


Step 2b — (Optional) Credit back fees#

If the order had fees (e.g. payment or shipping costs) that should be credited back to the customer, add contra entries for them. Only include fee IDs that are still present in the approveOrderItems response — the available fee IDs are returned under order.fees.

Input: AddContraFeesToOrderItemsInput!

NameTypeRequiredDescription
item_ids
[String!]!
RequiredThe contra_item_id values from step 1.
fees
[ContraFeeInput!]!
RequiredFees to credit back.

Returns: AddContraFeesToOrderItemsPayload

NameTypeRequiredDescription
items
[CollectionItem!]!
RequiredContra items the fees were attached to
mutation addContraFeesToProducts($input: AddContraFeesToOrderItemsInput!) {
  addContraFeesToOrderItems(input: $input) {
    items {
      id
    }
  }
}
{
  "input": {
    "item_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
    "fees": [
      {
        "description": "Shipping fee",
        "amount": 595,
        "type": "SHIPPING_FEE",
        "contra_fee_id": "d0e1f2a3-b4c5-6789-abcd-ef0123456789"
      }
    ]
  }
}
Tip:amount is in cents — 595 means € 5,95.

Step 3 — (Optional) Restock items#

If order.actions contains HANDLE_RESTOCK, call this mutation to return the items to inventory. You can skip restocking by not calling this mutation — the items will not be added back to stock.

Input: RestockOrderItemsInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
items
[RestockItemInput!]!
RequiredItems to restock.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation restockProducts($input: RestockOrderItemsInput!) {
  restockOrderItems(input: $input) {
    order {
      id
      actions {
        action
        ids
      }
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "items": [
      {
        "item_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "location_id": "e1f2a3b4-c5d6-789a-bcde-f01234567890",
        "address_id": "f2a3b4c5-d6e7-89ab-cdef-012345678901",
        "position": "STOCK"
      }
    ]
  }
}

After restocking, order.actions will contain HANDLE_FINISH.


Step 4 — Finish#

Completes the cancellation. The items are fully processed after this call.

Input: FinishOrderItemsInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
items
[String!]!
RequiredThe contra_item_id values from step 1.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation finishProducts($input: FinishOrderItemsInput!) {
  finishOrderItems(input: $input) {
    order {
      id
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "items": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
  }
}

The cancellation is now complete.


Step 5 — (Optional) Send cancellation email#

Input: SendCancellationEmailInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
item_ids
[String!]!
RequiredThe contra_item_id values from step 1.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation SendCancellationEmail($input: SendCancellationEmailInput!) {
  sendCancellationEmail(input: $input) {
    order {
      id
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "item_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
  }
}

The customer receives a cancellation confirmation for the processed items.

Notes#

  • finishOrderItems must always be the last call — it locks the processed items and closes the flow.
  • Steps marked (Optional) can be skipped; the flow will continue to finishOrderItems without them.
  • amount values in fee inputs are integers in cents — e.g. 595 means € 5,95.
  • The order.actions array drives which steps are required. Its contents depend on the order's configuration (e.g. whether inventory tracking is enabled). Always read it rather than assuming a fixed sequence.
  • reason values are enums — see the full CancelReason value list in the schema reference.
  • To look up available inventory locations for restocking, query inventoryLocations.
  • For processing a customer return after delivery, see Return order items.
  • See Authentication for how to pass your API key.
Query Runnerhttps://afosto.app/graphql

No query loaded

Click play on any code block in the docs to load a query here.