Can the Square API refund one item? Itemized refunds, explained

No. Square's docs say the Refunds API "doesn't support itemized refunds," and its RefundPayment request takes an amount and a payment ID, with no line item field. In September 2026 a Square staff member wrote on the developer forum that "itemized returns aren't possible via the API today." Itemized refunds are available in Square Point of Sale and the Dashboard only. On Flint, one API call refunds a line item with its share of the discount and tax worked out for you.
Verified against official documentationReviewed Send a correction (opens in a new tab)

An API refund on Square creates a return order with a single CUSTOM_AMOUNT line that names no item and breaks out no tax. Your app decides the amount, remembers which item it was for, puts the unit back in stock, and keeps the tax records Square's reports won't. On Flint the refund names the order's line item, and Flint computes the rest:

cURL
curl -X POST https://api.withflintpay.com/v1/refunds \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: refund-ord-1042-mug" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "ord_1kmn0aExample",
    "reason": "defective_product",
    "line_items": [
      {"order_line_item_id": "li_1kmn0aExample", "quantity": 1}
    ]
  }'

Which path applies to you

Where you areYour path
You want to refund one item from your own app on Square todayRefund an amount and keep the item record yourself
Staff can refund at the register, and your app only needs the recordRefund in Square Point of Sale, then read the refund order
Your API refunds already ran and the reports look wrongThe four traps
You want the API to refund the item, its tax, and put it back in stockLine-item refunds and returns on Flint

What Square supports, by surface

Square surfaceRefund specific itemsWhat the refund order records
Square Point of Sale appYesitem_type: ITEM, the catalog item, and its tax and discounts
Square DashboardYesitem_type: ITEM, the catalog item, and its tax and discounts
Refunds API (RefundPayment)No: an amount against one paymentOne CUSTOM_AMOUNT line with no catalog item
Orders APINo: Order.returns is read-onlyReads returns that the app or Dashboard created

The Orders API row is where most confusion starts. Square's returns page says an application "can use the Orders API for itemized refunds." A developer who tried every route in September 2026 found that Order.returns is read-only, so CreateOrder won't accept a return, and that RefundPayment has no order_id to link a refund to one. The Square staff reply said that sentence describes what an itemized refund looks like once it exists, and that the Point of Sale app can create one because "it drives an internal flow that isn't publicly exposed." Square staff have called itemized refunds a popular feature request since 2024.

The Square workaround: refund an amount, keep the item record yourself

Your app has to do four jobs Square does for itemized refunds in its own apps: work out the amount, refund it, record what it was for, and restock. Start from the order. Each line's total_money already includes its share of order discounts and its tax, so divide that by the quantity, in integer cents:

JavaScript
// Your code works out what one unit cost the buyer.
// total_money is read-only and already includes the line's share of
// order discounts and its tax. quantity is a string.
const { order } = await getOrder(orderId); // GET /v2/orders/{order_id}
const line = order.line_items.find((li) => li.uid === lineUid);

const lineTotal = Number(line.total_money.amount); // 3818 for three mugs
const units = Number(line.quantity);               // 3
const base = Math.floor(lineTotal / units);        // 1272
const remainder = lineTotal - base * units;        // 2 cents left over

// Give the leftover cents to the first units refunded, and check your own
// ledger so this unit hasn't been refunded already.
const alreadyRefunded = await refundedUnits(orderId, lineUid);
const amount = alreadyRefunded < remainder ? base + 1 : base; // 1273

Then refund that amount against the payment. The reason field, up to 192 characters, is the only place on the refund to note the item:

cURL
curl https://connect.squareup.com/v2/refunds \
  -X POST \
  -H "Square-Version: 2026-09-16" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "refund-order-1042-mug-unit-1",
    "payment_id": "PAYMENT_ID",
    "amount_money": {"amount": 1273, "currency": "USD"},
    "reason": "Mug arrived chipped (line 8uSwfzvUImn3IRrvciqlXC, 1 of 3)"
  }'

The refund comes back with an order_id for a new refund order. Its return line, abridged here, has no catalog item and says nothing about the mug:

JSON
"returns": [
  {
    "source_order_id": "ORIGINAL_ORDER_ID",
    "return_line_items": [
      {
        "quantity": "1",
        "item_type": "CUSTOM_AMOUNT"
      }
    ]
  }
]

That leaves three more pieces your app owns from now on:

  • A refund ledger keyed by order, line, and unit, so the same item can't be refunded twice and partial refunds add up to the line total. Square only checks the payment's remaining balance.
  • A restock call. A refund moves money only. Add the unit back with a BatchChangeInventory adjustment from UNLINKED_RETURN to IN_STOCK, which Square records as new stock rather than a return of that sale.
  • Tax records. The return order shows no tax refunded, so the tax you gave back lives in your ledger, and your filing comes from there instead of Square's reports.

The other workaround: refund in Square, then read the refund order

When staff can do the refund, the Square staff reply in the forum suggests the reverse flow: staff refund the items in Square Point of Sale or the Dashboard, and your app syncs from the refund.created and refund.updated webhooks. To see which items came back, retrieve the refund, then the order in its order_id. That refund order, not the original sale, holds the itemized returns. Square staff noted in 2025 that "not all refunds will be itemized," so the code has to handle CUSTOM_AMOUNT returns as well. This keeps the tax and item detail right, and it means your own app can never issue the refund.

The four places hand-built Square item refunds go wrong

  1. The refunded tax still counts as collected#

    Your sales tax report shows more tax collected than you kept, and the gap matches your API refunds.

    An API refund creates a return order with one CUSTOM_AMOUNT line. It names no item or category and breaks out no tax. On Square's developer forum in September 2026, a bowling center reported that full API refunds came back with $0.00 tax on the return, leaving "$894.18 of tax we charged on refunds that Square's reporting still counts as collected." A Square staff member confirmed that itemized returns aren't possible through the API.

    Confirm it

    Retrieve the refund, then the order in its order_id. If returns[].return_line_items[].item_type is CUSTOM_AMOUNT and the return's tax is zero, Square's reports did not reverse that tax.

    Fix

    Keep the tax you refunded in your own ledger and file from it, as the Square staff reply suggested. Or have staff run the refund in Square Point of Sale or the Dashboard, where an itemized refund reverses the item's tax.

  2. Refunding the list price overpays discounted orders#

    Refunds on orders with a promotion come out higher than what the buyer paid for the item.

    The item's price on the order is base_price_money, but an order-level discount spreads across every line. A refund for the base price plus tax ignores the line's share of the discount, so a buyer with 10% off gets back more than they paid for that item.

    Confirm it

    Compare each refund amount against the line's total_money divided by its quantity. Any refund above that per-unit figure overpaid.

    Fix

    Compute the amount from total_money, which Square reports after the line's discounts and taxes, and divide by the quantity in integer cents.

  3. Nothing stops the same item being refunded twice#

    A buyer was refunded twice for one item, or a line of three got back a cent more than it cost.

    Square caps a refund at the payment's total minus earlier refunds. It has no record of which item an API refund was for, so a second refund for the same mug succeeds as long as the payment has money left. Dividing a line's total by its quantity also leaves a remainder: a line of three at $38.18 is $12.7267 per unit, and three refunds of $12.73 add up to $38.19.

    Confirm it

    Group your refunds by the line item you recorded for each. Any line whose refunds exceed its total_money, or whose refunded units exceed its quantity, was overpaid.

    Fix

    Keep a per-line ledger of refunded units and amounts, check it before every refund, and give the remainder cents to the first units refunded so the parts sum to the line total exactly.

  4. Returned items don't go back into stock as returns#

    Counts are short after returns, or restocked units show up as new stock instead of returns.

    A refund moves money only. Square tracks returned units in RETURNED_BY_CUSTOMER, but that state is for items "returned through the Square Point of Sale application" and is read-only to the Inventory API. An app can add the unit back with an adjustment from UNLINKED_RETURN to IN_STOCK, which Square's docs say "introduces new stock into the system" rather than linking it to the sale.

    Confirm it

    Look up the variation's inventory history after an API refund. If nothing changed, or the change is an unlinked return, your returns aren't tied to the orders they came from.

    Fix

    After each item refund, call BatchChangeInventory with an adjustment from UNLINKED_RETURN to IN_STOCK for the variation and location, and store the refund ID in its reference_id so you can trace it later.

Line-item refunds on Flint

Flint is a payments API with orders built in, and cards are processed on Stripe. Because every payment belongs to an order, a refund can name what it is for. Send the order's order_line_item_id with a quantity, as in the first request above, or with an amount_money for a price adjustment. Flint computes the refund from what the line sold for and returns the arithmetic in line_item_allocations:

JSON
"line_item_allocations": [
  {
    "order_line_item_id": "li_1kmn0aExample",
    "quantity": 1,
    "refunded_money": {"amount": 1273, "currency": "USD"},
    "tax_refund_mode": "automatic",
    "automatic_refund": {
      "subtotal_money": {"amount": 1299, "currency": "USD"},
      "discount_money": {"amount": 130, "currency": "USD"},
      "tax_money": {"amount": 104, "currency": "USD"},
      "total_money": {"amount": 1273, "currency": "USD"}
    }
  }
]

The subtotal, the item's share of the order discount, and the tax that comes back are all on the refund, so reports and accounting read the item from Flint. Each line keeps its own refunded_quantity and refunded_money. Refunding the same unit twice fails with LINE_ITEM_REFUND_QUANTITY_EXCEEDS_REFUNDABLE instead of succeeding while the payment still has a balance. Shipping and other order charges are refunded the same way, by order_charge_id.

To keep a restocking fee, add an adjustment to the same line:

cURL
curl -X POST https://api.withflintpay.com/v1/refunds \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: refund-ord-1042-mug-restock" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "ord_1kmn0aExample",
    "reason": "customer_changed_mind",
    "line_items": [
      {
        "order_line_item_id": "li_1kmn0aExample",
        "quantity": 1,
        "refund_adjustments": [
          {
            "adjustment_type": "restocking_fee",
            "applies_to": "none",
            "amount_money": {"amount": 200, "currency": "USD"},
            "reason": {"code": "restocking_policy"}
          }
        ]
      }
    ]
  }'

When the item comes back: a Return restocks it

A refund on Flint moves money and never touches stock, as on Square. The difference is that Flint has a Return for merchandise that comes back. It applies your return policy, records what arrived, refunds the item, and puts it back on sale at the location you name. At a counter that is two calls:

Shell
# 1. Open the return for the item the buyer brought back
curl -X POST https://api.withflintpay.com/v1/returns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: return-ord-1042-create" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "ord_1kmn0aExample",
    "line_items": [{
      "order_line_item_id": "li_1kmn0aExample",
      "requested_quantity": 1,
      "return_reason_id": "rrsn_1T6HXAX0CEXP6YA5K1T392KJE6"
    }]
  }'

# 2. Receive it, refund it, and put it back on sale in one call
curl -X POST https://api.withflintpay.com/v1/returns/ret_1kmn0aExample/process \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: return-ord-1042-process" \
  -H "Content-Type: application/json" \
  -d '{
    "completion_behavior": "complete_when_ready",
    "receipt": {
      "receiving_location_id": "loc_1kmn0aExample",
      "source_system": {"source_system_type": "pos", "external_source_id": "store-1042"},
      "received_at": "2026-10-15T14:30:00Z"
    },
    "line_items": [{
      "return_line_item_id": "retli_1kmn0aExample",
      "decision": {
        "decision_basis": "explicit",
        "approved_quantity": 1,
        "return_required_quantity": 1,
        "allowed_resolution_types": ["refund"],
        "selected_resolution_type": "refund",
        "resolution_mode": "automatic",
        "refund_timing": "after_receipt",
        "is_inspection_required": false,
        "receiving_location_id": "loc_1kmn0aExample"
      },
      "received_quantity": 1,
      "resolution": {"resolution_type": "refund", "quantity": 1}
    }]
  }'

With no disposition in the request, a received item goes back as sellable and Flint writes the inventory receipt against that location, linked to the return line. For mail-in returns, buyers can start a return from their Flint account, and a warehouse key can record arrivals without the right to refund. Exchanges for another size are a resolution on the same Return.

Square API and Flint item refunds, side by side

Square Refunds APIFlint Refunds API
Refund one unit of an itemYou compute the amount and refund itline_items with a quantity
Item's discount and tax shareYou compute it from total_moneyComputed and returned on the refund
What the refund recordsOne CUSTOM_AMOUNT line, tax not itemizedThe line, quantity, discount, and tax
Same item refunded twiceAllowed while the payment has a balanceRejected per line
Restocking feeRefund a smaller amount and note itA restocking_fee adjustment
Back in stockYour adjustment, recorded as new stockA Return disposition, linked to the sale
ExchangesSquare products onlyAn exchange resolution on a Return

Who Flint is built for

Flint is for US businesses that sell on their own website or app and want orders, catalog and inventory, automatic US sales tax, shipping and pickup, returns, and refunds behind one API, paid in US dollars. Buyers can pay from anywhere with a card. Flint handles online and in-app payments only, with no card readers. The Square migration guide maps Square's orders, payments, subscriptions, invoices, and refunds to Flint.

Questions, answered

Can the Square API refund a single item?

No. Square's docs say the Refunds API doesn't support itemized refunds, and RefundPayment takes an amount and a payment ID with no line item field. A Square staff member wrote on the developer forum in September 2026 that itemized returns aren't possible via the API today. Sellers can refund specific items in Square Point of Sale and the Dashboard.

Does the Square Orders API support itemized refunds?

Not for creating them. Square's returns page says an application can use the Orders API for itemized refunds, but a Square staff member clarified in September 2026 that the sentence describes what the returns field looks like when an itemized refund already exists. Order.returns is read-only, so CreateOrder can't create a return.

What does a Square API refund create?

A refund order. The refund's order_id points to it, and its returns field holds one return line with item_type CUSTOM_AMOUNT and no catalog item. A refund made with specific items in Point of Sale or the Dashboard has item_type ITEM, the catalog_object_id, and the item's tax instead.

How do I refund one item with the Square API anyway?

Read the order, take the line's total_money (after its discount and tax) divided by its quantity, and refund that amount with RefundPayment. Record which item the refund was for in your own database, add the unit back to stock with a BatchChangeInventory adjustment, and keep your own tax ledger, because Square's reports won't show the item or its tax as refunded.

Does refunding through the Square API restock inventory?

No. A refund moves money only. The RETURNED_BY_CUSTOMER inventory state is read-only to the Inventory API, so an app restocks with an adjustment from UNLINKED_RETURN to IN_STOCK, which Square records as new stock.

Which payments API refunds by line item?

Flint does. POST /v1/refunds takes the order's line item ID and a quantity or amount, computes the item's share of discounts and tax, and caps each line at what is left on it. A Return on the same order refunds the item and puts it back in stock at the location you name.

Is Flint a good alternative to Square for online orders?

For US businesses selling online or in an app, yes: orders, catalog, inventory, automatic sales tax, returns, and line-item refunds come with the payments API, with cards processed on Stripe. Flint doesn't sell card readers or in-person payments, so in-store sales stay on your current point of sale.

Sources

All sources accessed October 15, 2026.