Skip to main content

Reading exchange data for integrations

Native exchanges allow merchants to swap items on a return directly within Shopify.

This guide helps ERPs, Order Management Systems (OMS), and other integrations sync exchange data, manage fulfillments for exchanged items, and reconcile financial transactions.

If you're building a return app and you need to create or manage exchanges on behalf of a merchant, see Manage exchanges instead.


When a return with an exchange is created and processed:

  1. Exchange Items: New items are added to the return as ExchangeLineItem objects.
  2. Fulfillment: A new FulfillmentOrder is created on the original order for the exchange items.
  3. Financials: The value of the returned items is applied to the exchange items. If there is a balance due, the fulfillment order might be held until payment is captured.
Note

Native exchanges do not create a separate new order. They're updates to the existing order. To track the full history of changes to an order, including exchanges, use sales agreements.


Anchor to The exchange data modelThe exchange data model

Exchanges are part of the Return object.

  • Return: The parent object containing both returnLineItems (items coming back) and exchangeLineItems (items going out).
  • ExchangeLineItem: Represents the new item being sent to the customer.
  • FulfillmentOrder: The operational record for fulfilling the exchange items.
  • SalesAgreement: The financial record for what was sold (the exchange items) and what was returned.

Anchor to Fetching exchange dataFetching exchange data

To retrieve exchange details, you can query the Order object to access its returns and associated exchange line items.

Anchor to Querying exchange line itemsQuerying exchange line items

To retrieve exchange details, query the Return object directly using its ID. This is the most efficient method when responding to webhooks like returns/processed.

Get exchange line items

GraphQL query

query GetReturnWithExchanges($returnId: ID!) {
return(id: $returnId) {
id
order {
id
}
# Items being returned
returnLineItems(first: 10) {
edges {
node {
quantity
fulfillmentLineItem {
lineItem {
name
}
}
}
}
}
# New items being exchanged for
exchangeLineItems(first: 10) {
edges {
node {
quantity
variantId
lineItems {
name
sku
}
}
}
}
}
}

JSON response

{
"data": {
"return": {
"id": "gid://shopify/Return/123",
"order": {
"id": "gid://shopify/Order/456"
},
"returnLineItems": {
"edges": [
{
"node": {
"quantity": 1,
"fulfillmentLineItem": {
"lineItem": {
"name": "Medium Shirt"
}
}
}
}
]
},
"exchangeLineItems": {
"edges": [
{
"node": {
"quantity": 1,
"variantId": "gid://shopify/ProductVariant/789",
"lineItems": [
{
"name": "Large Shirt",
"sku": "SHIRT-L"
}
]
}
}
]
}
}
}
}
Tip

You can also access exchange data via the Order object using the returns connection. This is useful if you are syncing orders in bulk and need to check for associated returns.

Anchor to Understanding order evolution with sales agreementsUnderstanding order evolution with sales agreements

Use the agreements connection on the Order object to view the history of sales and returns. This allows you to track the evolution of an order, including the original sale, the return, and the exchange, in a unified log.

Returns and exchanges only appear in sales agreements after they're processed. Processing is when merchants confirm which items are being returned or exchanged, making it the appropriate point to record the sale.

Get order agreements

GraphQL query

query OrderAgreements($orderId: ID!) {
order(id: $orderId) {
agreements(first: 10) {
edges {
node {
__typename
id
happenedAt
... on ReturnAgreement {
return {
id
name
}
}
sales(first: 10) {
edges {
node {
actionType
lineType
quantity
totalAmount {
shopMoney {
amount
currencyCode
}
}
... on ProductSale {
lineItem {
id
name
}
}
}
}
}
}
}
}
}
}

Anchor to Reconciling financialsReconciling financials

When managing returns and exchanges, you may want to associate financial transactions (payments and refunds) with the specific return event. This ensures correct accounting and reporting.

For even exchanges—where the returned and exchange items have the same total value including taxes—there are no additional transactions. The original payment covers the new items.

Anchor to Access transactions on the return objectAccess transactions on the return object

You can access transaction data directly from the Return object using the transactions connection for most scenarios. This simplifies the process of associating payments and refunds with specific returns, removing the need to deduce relationships based on amounts and timestamps.

The transactions connection is populated for the following scenarios:

  • POS returns and exchanges: Includes both refunds and captured payments.
  • Online returns and exchanges: Includes refunds only.

Get return transactions

GraphQL query

query ReturnTransactions($returnId: ID!) {
return(id: $returnId) {
transactions(first: 5) {
edges {
node {
id
kind
status
amountSet {
shopMoney {
amount
currencyCode
}
}
}
}
}
}
}
Online exchange payments

For online exchanges, captured payments are not yet directly associated with the return. To reconcile these, you can fetch all transactions on the Order and match them to the SalesAgreement based on the amount and creation timestamp.


Anchor to Handling fulfillment ordersHandling fulfillment orders

When an exchange is processed, Shopify creates a new FulfillmentOrder for the exchange items.

  • The original fulfillment order (for the returned items) will typically be CLOSED or updated.
  • The new fulfillment order for the exchange items will be OPEN.
Holds on net payable exchanges

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 the exchange is ON_HOLD with a reason of AWAITING_PAYMENT, you must wait for the hold to be released before fulfilling.

Anchor to Fetching fulfillment ordersFetching fulfillment orders

Query the fulfillmentOrders connection on the Order to identify the new fulfillment requirements. Check the status and fulfillmentHolds fields to determine if the order is ready for fulfillment.

Get fulfillment orders

GraphQL query

query OrderFulfillments($orderId: ID!) {
order(id: $orderId) {
fulfillmentOrders(first: 10) {
edges {
node {
id
status
fulfillmentHolds {
reason
}
lineItems(first: 10) {
edges {
node {
lineItem {
name
sku
}
}
}
}
}
}
}
}
}

To stay synchronized with exchange events, you should subscribe to returns/processed and fulfillment_orders/order_routing_complete.

  • returns/processed: Triggered when a return is processed. If the return includes exchange items, a new fulfillment order is created at this point. Returns can be partially processed. Check the payload to see which items were processed.
  • fulfillment_orders/order_routing_complete: Triggered when the new fulfillment order has been routed to a location and is ready for fulfillment.

For a complete list of return-related webhooks, see the Return apps overview.


Was this page helpful?