Skip to main content

Managing exchanges with the GraphQL Admin API

An exchange represents the intent of a buyer to swap one or more items from an order for different items from the same merchant or a third-party fulfillment location. Merchants can manage their exchanges in Shopify, and exchanges apps can take actions on behalf of merchants.

This guide shows you how to manage exchanges using the GraphQL Admin API.



Anchor to Step 1: Query returnable fulfillmentsStep 1: Query returnable fulfillments

Exchanges are applied to a returnable fulfillment. A returnable fulfillment is an order that has been delivered and is eligible to be returned to the merchant or third-party fulfillment service.

To retrieve the fulfillment line items that can be returned in an order, provide the ID of the order in the returnableFulfillments query. The response returns the fulfillment line item ID, which you'll use as input to create a return.

Tip

You can retrieve the ID of an order using the orders query.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query returnableFulfillmentsQuery {
returnableFulfillments(orderId: "gid://shopify/Order/1", first: 10) {
edges {
node {
id
fulfillment {
id
}
# Return the first ten returnable fulfillment line items that belong to the order.
returnableFulfillmentLineItems(first: 10) {
edges {
node {
fulfillmentLineItem {
id
}
quantity
}
}
}
}
}
}
}

JSON response

{
"data": {
"returnableFulfillments": {
"edges": [
{
"node": {
"id": "gid://shopify/ReturnableFulfillment/1",
"fulfillment": {
"id": "gid://shopify/Fulfillment/1"
},
"returnableFulfillmentLineItems": {
"edges": [
{
"node": {
"fulfillmentLineItem": {
"id": "gid://shopify/FulfillmentLineItem/1"
},
"quantity": 1
}
}
]
}
}
}
]
}
}
}

Anchor to Step 2 (Optional): Calculate return financials with exchangesStep 2 (Optional): Calculate return financials with exchanges

You can use the suggestedFinancialOutcome field to calculate recommended financials for a return with exchanges. This query provides a comprehensive suggestion for refunds, exchanges, and fees, which you can use as input to the returnProcess mutation.

Example:

query {
return(id: "gid://shopify/Return/123") {
suggestedFinancialOutcome(
returnLineItems: [
{ id: "gid://shopify/ReturnLineItem/456", quantity: 1 }
],
exchangeLineItems: [
{ id: "gid://shopify/ExchangeLineItem/789", quantity: 1 }
]
) {
totalReturnAmount { shopMoney { amount currencyCode } }
financialTransfer {
suggestedTransactions {
amountSet { shopMoney { amount currencyCode } }
}
}
// ...other fields
}
}
}

For more details, see Migrate to return processing.


Anchor to Step 3: Create a return with exchangesStep 3: Create a return with exchanges

Use the returnCreate mutation to create a return with exchange line items. This step records the merchant's intent to process a return and/or exchange, but does not confirm the exchange or create fulfillment orders.

Note

You must process the return using the returnProcess mutation to confirm the exchange and create fulfillment orders. See the next step.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation returnCreateMutation {
returnCreate(
returnInput: {
# The ID of the order to return.
orderId: "gid://shopify/Order/1",
# A return shipping fee to apply to this return.
returnShippingFee: {
amount: {
amount: 10,
currencyCode: CAD
}
},
returnLineItems: [
{
# The ID of the related fulfillment line item to return.
fulfillmentLineItemId: "gid://shopify/FulfillmentLineItem/1",
# The number of items that were previously fulfilled.
quantity: 1,
# The item is returned for another reason. For the `OTHER` value, you must also provide a return reason note.
returnReason: OTHER
# The additional information about the return reason.
returnReasonNote: "I need a bigger size."
# A restocking fee, as a percentage, to apply to this return line item.
restockingFee: {
percentage: 10
},
},
{
fulfillmentLineItemId: "gid://shopify/FulfillmentLineItem/2",
quantity: 1,
# The item is returned because the size was too small.
returnReason: SIZE_TOO_SMALL
}
],
# The exchange items to add to this return.
exchangeLineItems: [
{
# The product variant ID of the exchange item to add.
variantId: "gid://shopify/ProductVariant/1",
# The number of items for the exchanged variant
quantity: 1
}
]
# Send an email to the customer to notify them about the return.
# The return is associated to the `Order` object, specified by the `orderId`.
# If `notifyCustomer` is set to `true`, then `Order.email` is required.
notifyCustomer: true,
# The UTC date and time when the customer first requested the return.
requestedAt: "2022-05-04T00:00:00Z"
}
)
{
return {
id
}
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"returnCreate": {
"return": {
"id": "gid://shopify/Return/1"
},
"userErrors": []
}
}
}

Anchor to Step 4: Process the return to confirm exchangesStep 4: Process the return to confirm exchanges

After creating the return, call the returnProcess mutation. This step:

  • Confirms the return and exchange items.
  • Creates fulfillment orders for the exchange items.
  • Records the return and exchanges in merchant financial reports.

Example:

mutation ReturnProcess {
returnProcess(
input: {
returnId: "gid://shopify/Return/123",
returnLineItems: [
{
id: "gid://shopify/ReturnLineItem/456",
quantity: 1,
dispositions: [
{
reverseFulfillmentOrderLineItemId: "gid://shopify/ReverseFulfillmentOrderLineItem/789",
quantity: 1,
locationId: "gid://shopify/Location/101112",
dispositionType: RESTOCKED
}
]
}
],
exchangeLineItems: [
{
id: "gid://shopify/ExchangeLineItem/131415",
quantity: 1
}
],
notifyCustomer: true
}
) {
return {
id
status
}
userErrors {
field
message
}
}
}
  • Use the output from suggestedFinancialOutcome to help populate the input for this mutation.
  • Only after this step are fulfillment orders for exchanges created and merchant financials updated.

For more details and advanced scenarios, see Migrate to return processing.


Anchor to Step 5: Collect payment for the exchangeStep 5: Collect payment for the exchange

If a balance is due on the exchange, use the orderInvoiceSend mutation to send an invoice to the customer before releasing the hold on the exchange.

Note

Only exchanges with a net payable balance due by the buyer are placed on hold. Even or refundable exchanges are not placed on hold and can be fulfilled immediately after processing. If your exchange does not require additional payment from the buyer, you can skip the steps related to fulfillment holds and proceed directly to fulfillment.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation OrderInvoiceSend {
orderInvoiceSend(
# The ID of the order to exchange.
id: "gid://shopify/Order/1",
# The email details.
email: {
# The email recipient.
to: "customer@email.com",
# The email sender.
from: "merchant <merchant.email@shopify.com>",
# The email subject.
subject: "Invoice #1092",
# The email message.
customMessage: "Email message",
# The email bcc recipients.
bcc: [],
# The email cc recipients.
cc: []
}) {
order {
id
}
userErrors {
message
}
}
}

JSON response

{
"data": {
"orderInvoiceSend": {
"order": {
"id": "gid://shopify/Order/1"
},
"userErrors": []
}
}
}

Anchor to Step 6: Query for the ID of the fulfillment order on holdStep 6: Query for the ID of the fulfillment order on hold

Note

This step is only required if your exchange fulfillment order is on hold due to a net payable balance.

To fulfill the item for the exchange, you need to release the hold on the fulfillment order by querying for the ID of the fulfillment order that contains the hold.

To retrieve the fulfillment order ID, provide the ID of the order in the order query. The response returns the fulfillmentOrders which you can filter to locate the fulfillment order with a status of ON_HOLD and a fulfillmentHold with a reason of AWAITING_PAYMENT. You'll use the ID of that fulfillmentOrder to release the hold in the next step.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query Order {
order(id: "gid://shopify/Order/1") {
id
fulfillmentOrders (first: 10) {
nodes {
id
status
fulfillmentHolds {
id
reason
}
}
}
}
}

JSON response

{
"data": {
"order": {
"id": "gid://shopify/Order/1",
"fulfillmentOrders": {
"nodes": [
{
"id": "gid://shopify/FulfillmentOrder/1",
"status": "ON_HOLD",
"fulfillmentHolds": [
{
"id": "gid://shopify/FulfillmentHold/1",
"reason": "AWAITING_PAYMENT"
}
]
}
]
}
}
}
}

Anchor to Step 7: Release the hold in order to fulfill the itemStep 7: Release the hold in order to fulfill the item

Note

This step is only required if your exchange fulfillment order is on hold.

After a payment has been received, you can use the fulfillmentOrderReleaseHold mutation to release the item for the exchange so that it can be fulfilled.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation FulfillmentOrderReleaseHold {
fulfillmentOrderReleaseHold(
# The fulfillment order ID of the fulfillment order that is on hold.
id: "gid://FulfillmentOrder/Order/1"
) {
order {
id
}
userErrors {
message
}
}
}

JSON response

{
"data": {
"fulfillmentOrderReleaseHold": {
"fulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/1"
},
"userErrors": []
}
}
}

Step 8: Fulfill the item

After the hold has been released (if applicable), or immediately after processing for even or refundable exchanges, you can fulfill the item using the fulfillmentOrderSubmitFulfillmentRequest or fulfillmentCreateV2 mutation mutation.

Tip

For most exchanges (even or refundable), you can fulfill the item immediately after processing the return, without any need to check or release a fulfillment hold.



Anchor to Legacy Exchange Workflow (Deprecated)Legacy Exchange Workflow (Deprecated)

Caution

The following workflow applies to API versions prior to 2025-07, or if you have not yet migrated to the new return processing flow. Legacy behavior is still supported for a limited time, but will be removed in a future API version. All new integrations should use the Return Processing workflow.

In earlier API versions, creating a return with exchange line items using returnCreate would immediately confirm the exchange and create fulfillment orders (all placed on hold). No further processing step was required.

This behavior is deprecated and will be removed in a future API version. For migration steps, see Migrate to return processing.

StepBefore (Legacy)After (Return Processing)
Create return with exchangesreturnCreatereturnCreate
Confirm exchanges, create fulfillment orders, update financials(implicit in returnCreate)Explicit call to returnProcess required
Calculate financialssuggestedRefundsuggestedFinancialOutcome
Hold logic for exchange fulfillment ordersAll exchanges on holdOnly net payable exchanges on hold

Was this page helpful?