How do I determine the processing (return) location for a cross-location refund via the API?

How do I determine the processing (return) location for a cross-location refund via the API?

I have a multi-location Square account and I sync refunds into an external inventory/accounting system. I am hitting a case I cannot solve from the API and I want to confirm whether it is possible at all.

Scenario: an item is sold at Location A. Later the customer returns it at Location B (a different physical store, different terminal). I process this as a refund of the original payment.

What I see in the API (all point at Location A):

  • The refund.updated webhook and the PaymentRefund object both carry location_id = Location A.
  • The Payment being refunded is at Location A, and its device_details is the device from the original sale, not the device that processed the return.
  • The refund’s order_id resolves to an order whose location_id is also Location A. That order has tenders: null.

What the inventory shows (Location B): pulling BatchRetrieveInventoryChanges for the returned item returns:

  • A TRANSFER change moving quantity from Location A to Location B.
  • An ADJUSTMENT at Location B changing state SOLD to RETURNED_BY_CUSTOMER.

The dashboard confirms this: the return’s money shows under Location A’s sales report, but the stock is restocked at Location B, and the receipt shows the Location B device.

My questions:

  1. Is there any way to determine Location B (where the return was physically processed) for a given refund, other than inferring it from the inventory TRANSFER / ADJUSTMENT ledger? Is it exposed on any financial object I am missing?

  2. Is there a reliable identifier linking a PaymentRefund (or its return order) to the resulting inventory TRANSFER and ADJUSTMENT events? The InventoryChange objects have an id, catalog_object_id, quantity, state, and timestamps, but no order id or refund id, so today the only correlation I have is item + quantity + timestamp, which is not deterministic when two returns of the same item happen close together.

  3. Is the terminal/device that processed the refund exposed anywhere in the API? It appears on the dashboard receipt but I cannot find it on the PaymentRefund, the return order, or a tender.

  4. Would processing the return as an unlinked refund (or a Square for Retail cross-location return that creates its own order) change any of the above? Specifically, would the return location then appear directly on the refund or order object?

Environment: Square-Version: 2025-10-16, PHP SDK square/square 43.2.0.20251016.

Thanks for any guidance.

@CapeGlass - Thanks for the patience.

For a linked refund where payment_id is supplied, PaymentRefund.location_id is always the original payment’s location — the API does not expose the physical return location on any financial object.

For your specific questions:

  1. Return location (Location B): Not available on PaymentRefund, the refund order, or any financial object. Your current approach — inferring it from the InventoryAdjustment TRANSFER/state-change records — is the only supported method via the API.
  2. Linking a refund to inventory changes: The InventoryAdjustment object has a refund_id field (read-only) that is populated when the adjustment was caused by a refund. Use that to correlate:
    InventoryAdjustment.refund_id → GET /v2/refunds/{refund_id}
    This is more deterministic than matching on item + quantity + timestamp.
  3. Terminal/device that processed the refund: Not exposed. Payment.device_details reflects the original sale device. The refund object does not carry its own device_details or device_id. The dashboard receipt shows it, but the API does not surface it.
  4. Unlinked refund: An unlinked refund (POST /v2/refunds without payment_id) does accept a location_id, so the return location would appear directly on the refund object. However, unlinked refunds are not tied to the original payment — they’re meant for recording refunds outside of Square’s payment flow, so you lose the payment linkage.

TL;DR: For linked cross-location refunds, the return location is only determinable via inventory records. Use InventoryAdjustment.refund_id for reliable correlation rather than timestamp matching. Hope this helps!