Return order items#

This guide walks through processing a customer return after delivery: receiving the items back, approving the financial settlement, 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. (Bundles/parts only) Announce the return with returnOrderItems
  2. Receive the items back with receiveOrderItems
  3. (If required) Approve financials with approveOrderItems
  4. (Optional) Credit back fees with addContraFeesToOrderItems
  5. (Optional) Restock items with restockOrderItems
  6. Finish the process with finishOrderItems
  7. (Optional) Notify the customer with sendReturnEmail

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 announce a return for a bundle or part 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 (receive, approve, restock, finish) instead of the original item_id. Standard return items don't need a contra item — use their original item_id throughout.


Step 1 — (Bundles and parts only) Announce the return#

For items of type OFFER, PART, PRICED_PART, or ASSEMBLE_BUNDLE, announce the return before receiving. For standard RETURN or LOOSE_RETURN items, skip to step 2 and use the original item_id directly.

Input: ReturnOrderItemsInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
items
[ReturnItemInput!]!
RequiredItems to announce.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation announceReturnProducts($input: ReturnOrderItemsInput!) {
  returnOrderItems(input: $input) {
    order {
      id
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "items": [
      {
        "item_id": "9e0f1a2b-3c4d-5678-9abc-def012345678",
        "contra_item_id": "c4d5e6f7-a8b9-0123-cdef-456789012345",
        "reason": "DEFECTIVE"
      }
    ]
  }
}
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 — Receive the items#

Register the physical arrival of the returned goods. For standard return items use the original item_id; for announced items (step 1) use their contra_item_id.

Input: ReceiveOrderItemsInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
items
[ReceiveItemInput!]!
RequiredItems being received.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation receiveProducts($input: ReceiveOrderItemsInput!) {
  receiveOrderItems(input: $input) {
    order {
      id
      actions {
        action
        ids
      }
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "items": [
      { "item_id": "0f1a2b3c-4d5e-6789-abcd-ef0123456789", "position": "RETURN" },
      { "item_id": "c4d5e6f7-a8b9-0123-cdef-456789012345", "position": "RETURN" }
    ]
  }
}

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


Step 3 — (If required) Approve financials#

If order.actions contains HANDLE_APPROVAL, approve the financial settlement of the returned items using the IDs listed in that action entry. 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 item IDs from the HANDLE_APPROVAL action entry.

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": ["0f1a2b3c-4d5e-6789-abcd-ef0123456789", "c4d5e6f7-a8b9-0123-cdef-456789012345"]
  }
}

Check order.actions again before proceeding.


Step 3b — (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 item IDs being returned.
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": ["0f1a2b3c-4d5e-6789-abcd-ef0123456789"],
    "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 4 — (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": "0f1a2b3c-4d5e-6789-abcd-ef0123456789",
        "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 5 — Finish#

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

Input: FinishOrderItemsInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
items
[String!]!
RequiredAll item IDs processed in this return.

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": ["0f1a2b3c-4d5e-6789-abcd-ef0123456789", "c4d5e6f7-a8b9-0123-cdef-456789012345"]
  }
}

The return is now complete.


Step 6 — (Optional) Send return email#

Input: SendReturnEmailInput!

NameTypeRequiredDescription
order_id
String!
RequiredID of the order.
item_ids
[String!]!
RequiredAll item IDs processed in this return.

Returns: Order

NameTypeRequiredDescription
id
ID!
RequiredThe ID
number
String!
RequiredOrder number
total
Money!
RequiredTotal value
currency
Currency!
RequiredCurrency code
mutation SendReturnEmail($input: SendReturnEmailInput!) {
  sendReturnEmail(input: $input) {
    order {
      id
    }
  }
}
{
  "input": {
    "order_id": "72fca344-2a6f-4c3e-b4ca-029920b2522a",
    "item_ids": ["0f1a2b3c-4d5e-6789-abcd-ef0123456789", "c4d5e6f7-a8b9-0123-cdef-456789012345"]
  }
}

The customer receives a return 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 ReturnReason value list in the schema reference.
  • To look up available inventory locations for restocking, query inventoryLocations.
  • For cancelling items before shipment, see Cancel 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.