How do I get an API refund to keep the item category and the tax?

I manage a bowling center and wrote a small internal app that rings our weekly
league dues into Square. It creates an order against the league’s item variation
with auto_apply_taxes, then takes a cash payment. Those sales report fine,
right category and right tax.

The trouble is reversing one. If somebody enters a night wrong, we call
POST /v2/refunds against the payment for the full amount. The money goes back
correctly, but the return Square generates comes out as:

{
  "item_type": "CUSTOM_AMOUNT",
  "quantity": "1",
  "total_money": { "amount": 263921, "currency": "USD" }
}

and return_amounts.tax_money is $0.00.

When one of our people refunds the same sort of sale from the POS instead, we
get item_type: ITEM with catalog_object_id and source_line_item_uid
populated and the tax broken out properly. So the POS can obviously do this. I
just can’t figure out how to ask for it over the API.

Two problems it causes here. The small one is that the refund doesn’t land in
the league category, so that category reads high all month.

The bigger one is the tax. A $2,639.21 sale charged $178.41 of sales tax, and
the return credits none of it back. I have seven of these in a single month,
$894.18 of tax we charged on refunds that Square’s
reporting still counts as collected.

Things I’ve already tried:

  • Building the return order myself with CreateOrder. Order.returns is
    readOnly in the spec, so it won’t accept one.
  • Looking for a way to attach a refund to a return order I’d made.
    RefundPaymentRequest has no order_id and no line item fields.
  • The Refunds and Exchanges page says “your application can use the Orders API
    for itemized refunds
    ”, which is what sent me looking in the first place, but I
    can’t find an endpoint that actually does it. Refunds and Exchanges

So am I missing an endpoint or a parameter somewhere? And if this really isn’t
possible today, is there any chance of either taking line item detail on
RefundPayment, or at minimum apportioning tax onto the generated
CUSTOM_AMOUNT return? The second one by itself would fix the part that
actually costs money.

Production, US, Square-Version: 2024-10-17, plain HTTP calls, no SDK. Happy to
send order and refund IDs over to staff if that helps.

Unfortunately itemized returns aren’t possible via the API today.

POST /v2/refunds is amount-based only: no order_id, no line item fields. So the return we generate is always a single CUSTOM_AMOUNT with tax_money at zero. Both things you tried are correctly ruled out. Order.returnsreally is readOnly, and there’s no way to attach a RefundPayment to a return order you built. The POS can do it because it drives an internal flow that isn’t publicly exposed.

That doc sentence describes the read shape / what returns looks like when an itemized refund already exists, not a write endpoint. The common pattern for apps in your spot is to have staff do refunds in Square POS, and have the app listen to refund.created / refund.updated webhooks to stay in sync for reporting. You’d keep your own ledger as the source of truth for month-end and treat our report as cash movement. I know that partly defeats the app you wrote, but it’s the only path that gets you item_type: ITEM with tax broken out.

I’ll flag this with our product team again as itemized returns/refunds is one of our top feature requests at the moment.