Articles in this section

Sync exchanges and returns between Shopify and NetSuite

The Shopify – NetSuite D2C template aligns with Shopify's native exchanges and returns workflow using Shopify's GraphQL Return APIs. This support applies to both online and POS orders.

For return scenarios, a Return Authorization (RA) is created in NetSuite for the returned line items. For exchange scenarios, a new NetSuite order (Sales Order or Cash Sale) is also created for the exchange line items, matching the record type of the original order.

Shopify's native exchange workflow

Shopify's native exchange model allows merchants to process returns and add exchange items directly within the original order in Shopify Admin. Instead of creating a separate replacement order manually, merchants can:

  1. Open an existing order.

  2. Initiate a return.

  3. Select the items being returned.

  4. Add exchange items within the same return workflow.

  5. Process any price difference (a refund or an additional charge).

When processed this way, returned and exchanged items remain linked to the original Shopify order. Inventory restocking follows the restock settings selected during return processing.

End-to-end return lifecycle

To sync Shopify's native exchanges and returns with NetSuite, the template uses three coordinated flows. Each flow manages a specific stage of the lifecycle.

Flow

Type

What it does

Flow 1: Shopify requested returns to NetSuite return authorization (add)

Scheduled

Creates Return Authorization in NetSuite when a return is requested in Shopify

Flow 2: NetSuite pending receipt RMA to approved return in Shopify

Scheduled

Syncs NetSuite approval decisions back to Shopify

Flow 3: NetSuite received RMA to process return and exchange in Shopify

Scheduled

Processes the return in Shopify after items are received in NetSuite and issues a refund

Flow 4: Shopify hold fulfillments on payment pending release

Scheduled

Manages fulfillment holds for upsell exchanges

Together, these flows ensure that:

  • Returns created in Shopify are reflected in NetSuite as Return Authorizations.

  • Approvals made in NetSuite are synced back to Shopify without manual intervention.

  • Refunds are issued in Shopify only after returned items are physically received in NetSuite, maintaining financial accuracy.

A6.png

Prerequisites

  • Return Authorizations must be enabled in NetSuite (Setup > Company > Enable Features > Transactions > Return Authorizations).

  • The eTail custom fields must be enabled on the NetSuite Return Authorization and Item Receipt forms (see NetSuite Prerequisites).

  • The NetSuite bundle (81289) must be installed and up to date.

  • The Shopify connection must be re-authorized to grant the read_returns and write_returns API scopes.

  • The original sales order or cash sale must exist in NetSuite with the Shopify Order ID in the eTail Order ID field before any return flows are run.

Important

The NetSuite saved searches included with this D2C template are managed by the Celigo bundle (ID 81289). When Celigo releases a bundle update, any changes made directly to these saved searches will be overwritten, which can break your integration flows. Before making any changes to a template saved search, clone it first and use the cloned copy in your flow. Do not modify the original template saved searches directly. This ensures your customizations are preserved when the bundle is updated. To clone a saved search in NetSuite: open the saved search > click More > select Copy.

Key functionality

  • Even, upsell, and downsell exchanges: Creates exchange orders as Sales Orders or Cash Sales matching the original order type.

  • Multi-location exchange support: Returns and exchanges across multiple locations are supported using the Locations mapping.

  • Restocking fee handling: If a restocking fee is configured in Shopify, it is synced to NetSuite as a line item on the RA using the NetSuite item to track Shopify restocking fee setting.

  • Return shipping fee handling: The return shipping fee from Shopify is synced as a line item on the RA using the NetSuite item to track Shopify return shipping fee setting.

  • Unverified returns: Returns where the returned items cannot be individually identified (for example, POS returns without item-level detail) are handled as unverified returns. For unverified returns, the RA is created with status Pending Receipt, using line items with currentQuantity < 0 on the original order.

Flow 1: Shopify requested returns to NetSuite return authorization (add)

This scheduled flow exports returns that have been requested in Shopify and creates corresponding Return Authorizations in NetSuite.

How the flow works
  • For each return line item, the script finds the corresponding NetSuite line (matched by stOrderLineId) and calculates the rate using originalUnitPriceSet.

  • Cart-level discounts on return lines are calculated by finding the Celigo_Discount_Line_ entry on the original NetSuite order and applying it proportionally.

  • Restocking fees are added as separate RA lines using the configured NetSuite item for restocking fee.

  • Return shipping fees are added as a single RA line using the configured NetSuite item for return shipping fee.

  • The RA location is determined using the reverseFulfillmentOrders[0].lineItems[0].dispositions[0].location from Shopify, falling back to the returnable fulfillment location if not available.

  • When disabled (default): Shopify status REQUESTED maps to NetSuite RA status Pending Receipt; OPEN maps to Pending Receipt; CLOSED maps to Closed.

  • When enabled: REQUESTED maps to Pending Approval; OPEN maps to Pending Receipt; CLOSED maps to Closed.

  1. Get returned orders from Shopify (Export): Uses the GraphQL orders(first, query, after, reverse:true) query to find orders with returns(first: 10) that have been updated since the last flow run. The flow processes Shopify returns with status REQUESTED, OPEN, or CLOSED.

  2. Get return info from Shopify (Lookup): For each return, retrieves detailed return information from Shopify including: return status, return line items, fulfillment line items, exchange line items, reverse fulfillment orders, and return shipping fees. Uses the GraphQL return(id) query.

  3. Get returnable fulfillments from Shopify (Lookup): Uses the GraphQL returnableFulfillments(first, orderId, after, reverse) query to retrieve fulfillment line items that can be returned. These are used to match return line items to the correct NetSuite sales order lines during RA creation.

  4. Get order info from NetSuite (Lookup): Uses saved search customsearch_celigo_shopify_lines_return to retrieve the original NetSuite order's line items (NS line IDs, eTail line IDs, tax data). These are required to map each Shopify return line item to its corresponding NetSuite sales order line.

  5. postResponseMap - build the RA record: The postResponseMap hook builds the Return Authorization object for NetSuite import:

  6. Post verified return authorization to NetSuite (Import): Creates the Return Authorization in NetSuite with the appropriate status based on the Sync RMAs with Pending Approval status setting:

  7. Post unverified return authorization to NetSuite (Import): For unverified returns (where returnLineItems contain UnverifiedReturnLineItem type items), creates a separate RA with status Pending Receipt. Unverified return items are identified as order line items with currentQuantity < 0.

  8. Post item receipt to NetSuite (Import): Creates item receipt records in NetSuite for verified returns using distributed, one-to-many processing. Maps return line items, quantities, and locations, and links the receipt to the corresponding Return Authorization.

  9. Update return authorization in NetSuite (Import): Updates Return Authorization records in NetSuite after item receipt creation by closing processed return lines and marking the return as completed for further processing.

Flow 2: NetSuite pending receipt RMA to approved return in Shopify

This flow keeps the return approval status synchronized between NetSuite and Shopify. It is triggered when a Return Authorization in NetSuite is approved or closed, and automatically updates the corresponding Shopify return.

How the flow works
  1. Approve return in Shopify (Import): NetSuite RA status = Pending Receipt AND Shopify return status = REQUESTED: approves the return in Shopify.

  2. Cancel return in Shopify (Import): NetSuite RA status = Closed AND Shopify return status = REQUESTED: cancels the return in Shopify.

If the Shopify return is already in a state other than REQUESTED (for example, OPEN or CLOSED), neither branch matches and the record is skipped.

  1. Get return authorizations from NetSuite (Export): Uses saved search customsearch_celigo_shopify_rma_pending (type: once) to retrieve RAs in Pending Receipt or Closed status. For each RA, the Shopify Return ID (stReturnId) is retrieved from the eTail tab to identify the matching Shopify return.

  2. Get return status from Shopify (Lookup): Uses the GraphQL return(id).status query to check the current Shopify return status.

  3. Router: Approve or Cancel  

Flow 3: NetSuite received RMA to process return and exchange in Shopify

This scheduled flow processes the return in Shopify after the returned items have been physically received in NetSuite (an Item Receipt has been created against the Return Authorization). It also triggers the refund in Shopify based on the received items and any applicable exchange adjustments.

How the flow works
  1. Get return authorizations from NetSuite (Export): Uses saved search customsearch_celigo_shopify_received_rma (delta) to retrieve RAs in the following states: Pending Refund, Partially Received, or Closed. The search also includes the corresponding Item Receipt lines (SKU, quantity, location, restock flag) and the Shopify Return ID.

  2. Get related item receipt from NetSuite (Lookup): Uses saved search customsearch_celigo_shopify_receipts_rma (type: once) to identify Item Receipts associated with each RA. These are required to determine which return lines have been physically received and are ready for processing.

  3. Get return information from Shopify (Lookup): Retrieves the current state of the Shopify return using the GraphQL return(id) query, including exchange line items and any fulfillment orders on hold.

  4. postResponseMap - build lines to receive and remove: The postResponseMap script processes the RA and Item Receipt data to build two lists:

    • linesToReceive: Item Receipt lines where closed = false (items to be processed in Shopify)

    • linesToRemove: RA lines where closed = true (lines to be removed from the Shopify return, for example, cancelled lines or lines not received)

    If Process all exchange items is enabled, the script also builds exchangeLinesToConfirm exchange line items from the Shopify return that should be automatically processed once the return is received.

    The Item Receipt's restock flag is used to determine whether each returned item should be restocked in Shopify's inventory. When the Restock checkbox is checked on the NetSuite Item Receipt, the item is restocked. When unchecked, it is not restocked.

  5. Router - Receive or Remove (all matching branches) 

    • Post return received lines to Shopify (Import): linesToReceive.length > 0: posts the received return lines to Shopify and triggers the refund calculation. The refund respects the Shopify settings for refund method (original payment or store credit).

      • Release the fulfillment hold in Shopify (Import): Releases fulfillment holds in Shopify for fulfillment orders associated with the return, allowing order processing or exchange fulfillment to proceed.

    • Post return closed lines to Shopify (Import): linesToRemove.length > 0: removes the specified lines from the Shopify return (for example, when a return was partially cancelled in NetSuite).

    This router uses all matching branches, meaning both branches can execute on the same record if both conditions are true.

Flow 4: Shopify hold fulfillments on payment pending release

This flow monitors fulfillment orders that are on manual hold in Shopify, typically exchange orders in an upsell scenario where additional payment is pending. When payment is captured in NetSuite, this flow releases the fulfillment hold in Shopify so that the exchange order can be fulfilled.

The flow uses the GraphQL manualHoldsFulfillmentOrders(first, after, reverse:true) query to retrieve fulfillment orders currently on hold.

Note

This flow is only needed when the Auto-Release Fulfillment Hold setting (Returns flow group) is enabled. If you do not process upsell exchanges, you can leave this flow disabled.

How exchange orders are created

When the Process all exchange items setting is enabled, and the Shopify return includes exchange line items, the flow automatically processes the exchange items in Shopify once the return is received. This triggers the creation of the exchange order in Shopify.

The exchange order is then picked up by the Shopify order to NetSuite order – Exchanges flow, which creates the corresponding Sales Order or Cash Sale in NetSuite. The exchange order is routed to the same record type as the original order (Sales Order or Cash Sale).

If the Auto-Release Fulfillment Hold setting is enabled, the fulfillment hold on the exchange order in Shopify is automatically released after the exchange is processed, allowing the warehouse to fulfill the exchange items.

Returns settings

A7.png

To configure, navigate to SettingsReturns.

Setting

Description

Sync RMAs with Pending Approval status

Controls how Shopify return statuses map to NetSuite RA statuses. When enabled: REQUESTED: Pending Approval, OPEN: Pending Receipt, CLOSED: Closed. 

When disabled (default): REQUESTED: Pending Receipt, OPEN: Pending Receipt, CLOSED: Closed.

Notify Customer

When enabled, Shopify sends a notification to the customer when a return is approved. Default: disabled.

NetSuite item to track Shopify restocking fee as a line item (SettingsGeneral)

Select the NetSuite non-inventory, payment, or other charge item to use as the restocking fee line item on Return Authorizations. Must be configured to sync Shopify restocking fees to NetSuite.

NetSuite item to track Shopify return shipping fee as a line item (SettingsGeneral)

Select the NetSuite non-inventory, payment, or other charge item to use as the return shipping fee line item on Return Authorizations. Must be configured to sync Shopify return shipping fees to NetSuite.

Process all exchange items

When enabled, exchange items linked to a return are automatically processed in Shopify when the return is received in NetSuite. When disabled, exchange items must be processed manually in Shopify Admin.

Auto-Release Fulfillment Hold

When enabled, the fulfillment hold on exchange items is automatically released in Shopify after the exchange is processed. When disabled, the hold must be released manually using the Shopify hold fulfillments on the payment pending release flow or directly in Shopify Admin.

Scheduling guidance

  • Flow 1: Schedule every 15–30 minutes.

  • Flow 2: Schedule every 15–30 minutes. Run after Flow 1 in the same scheduling cycle.

  • Flow 3: Schedule every 15–30 minutes. Run after Flow 2. Do not enable Flow 3 before Flows 1 and 2 are stable and running without errors.

Known limitations

  • The template supports up to 10 exchange items per single Shopify order.

  • The template supports up to 250 return/refund records per order in Shopify.

  • Do not manually close a Shopify return until the integration has completed processing. If you need to stop a return in progress, cancel it in Shopify instead of closing it. Manually closing a return before the flow runs may cause errors or inconsistent records.

  • When an item receipt is created manually in NetSuite without restocking the item, and the return is later closed through Flow 3, the integration cannot retroactively update the Shopify restock status back to true.

  • The Shopify returns to NetSuite return authorization flow does not support mapping the StaffMember field.

  • The integration does not sync shipping refunds to the NetSuite Return Authorization.

Known issues

The following $0 exchange-refund scenarios are currently not supported. In these scenarios, the refund sync may fail with the error: "Failed to save record because the total cannot be negative."

  • Per-line tax disabled + Overwrite tax enabled: When an exchange generates a $0 refund and a separate manual refund exists, the refund flow may process both, causing the $0 exchange refund to fail.

  • Tax-inclusive with per-line tax enabled: The $0 exchange refund amount may not calculate correctly.

  • Tax-inclusive when the exchanged item matches the original item: A $0 refund may be triggered and fail.

  • Upsell exchange scenario: After the exchange order is created, a $0 refund may be initiated and fail during processing.

Common issues with returns and exchanges issues

Return Authorization not created in NetSuite
  • Verify the Shopify return status is REQUESTED, OPEN, or a state the flow is configured to process.

  • Check that the original Sales Order in NetSuite has the Shopify Order ID populated in the eTail Order ID field.

  • Ensure Return Authorizations are enabled in NetSuite (Setup > Company > Enable Features > Transactions).

RMA approval not syncing to Shopify
  • Check the Flow 2 run history to confirm it ran after the RA was approved in NetSuite.

  • Verify the Shopify return status is still REQUESTED; if it has already been approved or closed externally, the flow will skip it.

Access denied for returns field

Re-authorize the Shopify connection to grant the read_returns and write_returns API scopes. This error typically indicates that the Shopify connection was authorized before the returns scopes were added to the Celigo Shopify app.