Shopify return v/s cancellation

You’re right! NO_RESTOCK is ambiguous on its own. It doesn’t tell you whether it was a cancellation without restock or just a standalone refund.

The missing piece here is the line item’s fulfillmentStatus. I tested this approach against my test store with 4 different scenarios and it works reliably:

query OrderRefundClassification($orderId: ID!) {
  order(id: $orderId) {
    id
    cancelledAt
    cancelReason
    refunds {
      id
      createdAt
      return {
        id
      }
      refundLineItems(first: 50) {
        nodes {
          lineItem {
            id
            title
            fulfillmentStatus  # This is the key field
          }
          quantity
          restockType
        }
      }
    }
  }
}

Your classification logic would be:

  • Return: refund.return is not null, OR restockType = RETURN

  • Cancellation: restockType = CANCEL, OR (restockType = NO_RESTOCK AND lineItem.fulfillmentStatus is unfulfilled)

  • Standalone refund: restockType = NO_RESTOCK AND lineItem.fulfillmentStatus is fulfilled

I validated this with actual refunds:

  1. CANCEL + unfulfilled → cancellation with restock

  2. NO_RESTOCK + unfulfilled → cancellation without restock (your ambiguous case)

  3. RETURN + fulfilled → return with restock

  4. NO_RESTOCK + fulfilled → standalone refund

Cancellations happen on unfulfilled items, so NO_RESTOCK + unfulfilled = cancellation without restock. If it’s NO_RESTOCK + fulfilled, it’s a standalone refund.

This came up in another thread where someone noted the API doesn’t have full parity with what the admin UI can do for cancellations without restock - the admin uses an internal removal field that’s not exposed publicly. But the fulfillmentStatus approach works for classification.

Let me know if that covers your scenario or if there’s more I can do to help!