Understand how InStore delegates returns and refunds to your integration, and implement the webhook notifications your integration receives.
Process delegated refunds
InStore supports two ways to handle returns and refunds:
- Delegated refunds: InStore initiates and manages the refund, then notifies your integration through webhooks. Your integration consumes these notifications and updates your own systems. Your payment processor reverses the original charge. Your integration does not post the refund to commercetools or call a refund-session API.
- InStore-managed refunds: you build refund processors similar to payment processors. InStore posts the refund to commercetools on your behalf using Custom Fields. This topic does not cover that integration path.
InStore processes returns and refunds through a refund session. A single return can span multiple tenders, such as the original payment method and a fallback tender. You can also define a fallback payment option to use when the original tender type is unavailable for a refund.
InStore creates and manages refund sessions internally as a store associate completes a return. You do not call a refund session API directly. Your integration implements the two webhook notifications described below.
GiftCard) refunds, see Credit and stored value refund processing. InStore records the tender against the session. It does not create or reverse a commercetools Payment transaction on your behalf. Your payment processor integration remains responsible for reversing the original charge. For details on your payment processor's refund endpoint, see Processor webhook paths that we provide for specific payment types.Cash tenders work differently.
Refund object
{
"returnCart": { "typeId": "cart", "key": "cart-1" },
"returnOrders": [
{
"order": { "typeId": "order", "key": "order-1" },
"returnLineItem": [{ "typeId": "line-item", "id": "193eb21f-a5e7-4327-a6c9-705282da9f30" }]
}
],
"refunds": [
{
"refundAmount": { "amount": 4999, "currency": "USD" },
"paymentOption": { "typeId": "paymentOption", "key": "option-payment-key" },
"transactionId": "451sd5f1",
"order": { "typeId": "order", "key": "order-1" },
"originalCTPayment": { "typeId": "payment", "id": "pay_3Nk82jLkQ4eR1mWx" },
"pspData": { "originalPaymentMethodName": "MasterCard 4444" },
"metaData": {
"prompt": "Refund will be made to original payment",
"promptInfo": "Mastercard ending 4444: -$50.00"
},
"fallbackRefundTypes": [
{
"paymentOption": { "typeId": "paymentOption", "key": "option-credit-us-1" },
"pspData": {},
"metaData": {
"prompt": "Refund will be made to original payment",
"promptInfo": "Mastercard ending 4444: -$50.00"
},
"processingSequence": 0.9
}
]
}
]
}
| Element | Data type | Description |
|---|---|---|
| returnCart | Object | A reference to the Cart created based on the items refunded. InStore uses this to calculate totals and to get information for the refund receipt. |
| returnOrders | Array<Object> | The Orders that contain the items being returned. |
| returnOrders[].order | Object | A reference to the Order related to the returned Line Items. Contains typeId and key. |
| returnOrders[].returnLineItem | Array<Object> | The Line Items from the Order that are being returned. |
| refunds | Array<Object> | The refund instructions to be processed. |
| refunds[].refundAmount | Object | The refund amount, as { amount, currency }. |
| refunds[].paymentOption | Object | The payment option selected for the refund. Contains typeId and key. |
| refunds[].transactionId | String | The identifier of the original payment transaction to refund against. A commercetools Payment can carry multiple prior refunds, so this tells InStore which transaction to refund. |
| refunds[].order | Object | A reference to the Order associated with this refund. |
| refunds[].originalCTPayment | Object | A reference to the original commercetools Payment used for the purchase. |
| refunds[].pspData | Object | Payment service provider data related to the refund. Can have any value. |
| refunds[].metaData | Object | Display metadata used to communicate refund information to the user. |
| refunds[].metaData.prompt | String | The main prompt message shown for the refund. |
| refunds[].metaData.promptInfo | String | Additional prompt information, such as masked card details and the refund amount. |
| refunds[].fallbackRefundTypes | Array<Object> | Fallback refund options to use if the primary refund option cannot be processed. A refund can use a configured credit option as a fallback. |
| refunds[].fallbackRefundTypes[].processingSequence | Float (0-1) | The priority or order in which to process the fallback refund option. |
Credit and stored value refund processing
GiftCard) tender types. InStore communicates with your processor following the same pattern as other card payments. For details, see Processor webhook paths that we provide for specific payment types.Refund processing for cash
When a store associate finalizes a return, InStore sends your integration two kinds of notifications:
- Refund webhook: sent once the refund is finalized, so you can update your own systems.
- Refund receipt data webhook: sent when InStore needs data to print the refund receipt.
Refund webhook
When a refund session is finalized, InStore sends a webhook notification to the webhook URL configured for your tenant.
X-Signature and X-Correlation-Id, this request also includes an x-tenant-id header that identifies the tenant.{
"tenant": { "type": "tenant", "key": "acme-retail" },
"location": { "type": "location", "key": "01" },
"workstation": { "type": "workstation", "key": "001" },
"action": "Refund",
"data": {
"refundSessionId": "6a3c08d503353f78bbe2c08f",
"refundTransactions": [
{
"paymentOption": { "typeId": "paymentOption", "id": "691bb93ff14fd895922ceb16" },
"amount": { "currencyCode": "USD", "centAmount": 4999, "fractionDigits": 2 },
"originalRefundTransactionId": "txn_original_payment_1",
"tenderInformation": {
"type": "Credit",
"tenderItemId": "6a3c091003353f78bbe2c090",
"transactionId": "8156515",
"payment": { "typeId": "payment", "id": "pay_3Nk82jLkQ4eR1mWx" }
}
},
{
"paymentOption": { "typeId": "paymentOption", "id": "691bb93ff14fd895922ceb17" },
"amount": { "currencyCode": "USD", "centAmount": 500, "fractionDigits": 2 },
"originalRefundTransactionId": "txn_original_payment_1",
"tenderInformation": {
"type": "Cash",
"tenderItemId": "6a3c091003353f78bbe2c091",
"roundedRemainder": { "currencyCode": "USD", "centAmount": -1, "fractionDigits": 2 }
}
}
]
}
}
| Element | Data type | Description |
|---|---|---|
| action | String | Always Refund for this notification. Branch your webhook handler on this value alongside the payment, refund, and receipt actions you already handle. |
| data.refundSessionId | String | The internal identifier of the InStore refund session that was finalized. |
| data.refundTransactions | Array<Object> | One entry per tender that made up the refund. A session can have multiple entries when a return is split between the original payment method and a fallback tender. |
| data.refundTransactions[].paymentOption | Object | A reference to the payment option the tender was recorded against. Contains typeId and id. |
| data.refundTransactions[].amount | Object | The tender amount: { currencyCode, centAmount, fractionDigits }. |
| data.refundTransactions[].originalRefundTransactionId | String | The identifier of the original payment transaction that this tender refunds. Use this to match the refund back to the payment you are reversing. |
| data.refundTransactions[].tenderInformation | Object | Tender-specific data for the tender being refunded. Shape depends on the tender type. |
| data.refundTransactions[].tenderInformation.type | String | The tender type: Cash, Credit, GiftCard (stored value), or Pay on Account (POA), a business account paid by purchase order. |
| data.refundTransactions[].tenderInformation.tenderItemId | String | The internal identifier of the InStore tender. Present for every tender type. |
| data.refundTransactions[].tenderInformation.transactionId | String | Credit only. The payment provider or terminal transaction identifier for the refund. |
| data.refundTransactions[].tenderInformation.payment | Object | Credit and POA only. A reference to the commercetools Payment associated with the tender. Contains typeId and id. |
| data.refundTransactions[].tenderInformation.roundedRemainder | Object | Cash only. The cash-rounding adjustment applied to the refund, as commercetools money: { currencyCode, centAmount, fractionDigits }. Omitted when the refund needed no rounding. |
| data.refundTransactions[].tenderInformation.account | Object | POA only. The business account the tender was charged to: { company_name, account_number, authorized_user_id, purchase_order }. purchase_order is optional. |
200 status code to acknowledge receipt.If your webhook service doesn't acknowledge the request, InStore retries up to three times using exponential backoff. InStore waits 1 second before the first retry, 2 seconds before the second retry, and 4 seconds before the third retry. Then, InStore stops retrying. This does not affect the finalized refund session. Your integration is responsible for reconciling any missed notifications through your own means.
Refund receipt data webhook
X-Signature and X-Correlation-Id, this request also includes the following headers:| Name | Purpose |
|---|---|
X-Api-Key | A key to authenticate the InStore API call to your webhook service. |
x-tenant-id | Identifies the tenant that is being called. |
{
"tenant": { "typeId": "tenant", "key": "acme-retail" },
"location": { "typeId": "location", "key": "01" },
"workstation": { "typeId": "workstation", "key": "001" },
"action": "RefundReceiptDataRetrieval",
"data": {
"refundSession": {
"refundSessionId": "6a3c08d503353f78bbe2c08f",
"returnCart": { "typeId": "cart", "key": "cart-1", "id": "1cda6cae-0ceb-460a-b1f6-f483f750bd81" },
"returnOrders": [
{
"order": { "typeId": "order", "key": "order-1" },
"returnLineItem": [{ "typeId": "line-item", "id": "193eb21f-a5e7-4327-a6c9-705282da9f30" }]
}
],
"refunds": [
{
"refundAmount": { "amount": 4999, "currency": "USD" },
"paymentOption": { "typeId": "paymentOption", "key": "option-credit-us-1" },
"order": { "typeId": "order", "key": "order-1" },
"originalCTPayment": { "typeId": "payment", "id": "pay_3Nk82jLkQ4eR1mWx" },
"metaData": { "prompt": "Show1", "promptInfo": "Show2" },
"fallbackRefundTypes": []
}
],
"status": "Refunded"
},
"tenderItems": [
{
"_id": "6a3c091003353f78bbe2c090",
"session_id": "6a3c08d503353f78bbe2c08f",
"transaction_type": "refund",
"tender_type": "Credit",
"amount": { "amount": 4999, "currency": "USD", "precision": 2 },
"reference": "cart-1",
"payment_id": "pay_3Nk82jLkQ4eR1mWx",
"original_reference": "order-1",
"payment_option_id": "option-credit-us-1",
"created_at": "2026-07-22T10:25:43.194Z"
}
]
}
}
| Element | Data type | Description |
|---|---|---|
| action | String | Always RefundReceiptDataRetrieval for this request. |
| data.refundSession | Object | The refund session and its current status. It includes the return Cart, return Orders, and refunds that InStore built when the return was initiated. |
| data.refundSession.refundSessionId | String | The internal identifier of the InStore refund session. |
| data.refundSession.returnCart | Object | A reference to the Cart created for the returned items. Contains typeId, key, and id. |
| data.refundSession.returnOrders | Array<Object> | The Orders and Line Items being returned, each with order (a reference) and returnLineItem (an array of references). |
| data.refundSession.refunds | Array<Object> | The refund instructions for the session. Includes refundAmount, paymentOption, order, originalCTPayment, metaData (prompt and promptInfo), and fallbackRefundTypes. |
| data.refundSession.status | String | The refund session's current status, for example Refunded. |
| data.tenderItems | Array<Object> | Every tender recorded against the session, in the raw shape InStore persists it. |
| data.tenderItems[].tender_type | String | The tender type, such as Cash, Credit, or GiftCard. |
| data.tenderItems[].amount | Object | The tender amount: { amount, currency, precision }. |
| data.tenderItems[].cash | Object | Cash tenders only. The cash-rounding adjustment applied to the tender: { roundedRemainder: { amount, currency, precision } }. |
| data.tenderItems[].reference / original_reference / payment_id / payment_option_id | String | Identifiers for the return Cart, original Order, original Payment, and payment option for this tender. |
Refund receipt data response
2xx status code within five seconds, and a JSON body containing the receipt data. InStore renders this body directly, and unmodified, into the refund receipt template's placeholders. If your webhook service returns a non-2xx status, times out after five seconds, or is unreachable, InStore does not retry the request. The refund session is already finalized, so the return is not affected. The InStore POS shows an empty receipt preview, and the associate can still complete the refund without printing or emailing a receipt. For the full list, see Refund receipt template. Your response must include every applicable field the template references, including location, which InStore does not fill in for you on this flow. The receipt logo is loaded into the printer's memory separately and isn't part of this payload.Money values use two different shapes depending on where they originate:
- Cart, tax, and total amounts use the commercetools money shape:
{ type, currencyCode, centAmount, fractionDigits }. - Refund tender amounts use
{ amount, currency, precision }, the same shape InStore sent you indata.tenderItems[].amounton the request above.
{
"order_number": "30001401000149",
"timestamp_formatted": "24/08/2026, 01:36:48",
"workstation_id": "01",
"user_id": "jsmith",
"tax_id": "12-3456789",
"reprint": false,
"location": {
"description": "Acme Retail - Downtown",
"address_line_1": "123 Main St",
"address_line_2": "Suite 100",
"city": "Springfield",
"state": "IL",
"postal_code": "62701",
"telephone_number": "+1 217-555-0100"
},
"total": { "type": "centPrecision", "currencyCode": "GBP", "centAmount": 31216, "fractionDigits": 2 },
"carts": [
{
"line_items": [
{
"description": "Product Name",
"quantity": 1,
"price": { "type": "centPrecision", "currencyCode": "GBP", "centAmount": 31216, "fractionDigits": 2 },
"extended_price": { "type": "centPrecision", "currencyCode": "GBP", "centAmount": 31216, "fractionDigits": 2 },
"tax_code": "UK",
"price_modifiers": [],
"attributes": [{ "label": "brand", "value": "Brand Name" }]
}
]
}
],
"refunds": [
{
"type": "Cash",
"amount": { "amount": 31216, "currency": "GBP", "precision": 2 },
"cash": { "roundedRemainder": { "amount": 0, "currency": "GBP", "precision": 2 } },
"reference": "30001401000150",
"originalReference": "30001401000149",
"payment": {
"paymentMethodInfo": { "paymentInterface": "Custom", "method": "Credit", "name": { "en-US": "credit,4444,mastercard" } }
}
}
],
"tax_summary": [
{
"code": "UK",
"tax_rate": 0.2,
"total_net": { "type": "centPrecision", "currencyCode": "GBP", "centAmount": 26013, "fractionDigits": 2 },
"total_tax": { "type": "centPrecision", "currencyCode": "GBP", "centAmount": 5203, "fractionDigits": 2 }
}
],
"refundSession": {
"status": "Refunded",
"returnOrders": [{ "order": "30001401000149" }]
},
"workstation": { "id": "01", "description": "Front Register" },
"originalPayments": [
{
"amountPlanned": { "type": "centPrecision", "currencyCode": "GBP", "centAmount": 31216, "fractionDigits": 2 },
"paymentMethodInfo": { "paymentInterface": "Custom", "method": "Credit", "name": { "en-US": "credit,4444,mastercard" } },
"orderNumber": "30001401000149"
}
]
}
| Element | Data type | Description |
|---|---|---|
| order_number | String | The Order or transaction number, printed on the receipt and encoded in its barcode. |
| timestamp_formatted | String | The refund date and time, pre-formatted for display. |
| workstation_id | String | The workstation where the return was processed. |
| user_id | String | Optional. The associate who processed the return. Omit to leave the cashier line blank. |
| tax_id | String | Optional. The tax identifier to print on the receipt. Omit to leave the VAT ID line blank. |
| reprint | Boolean | Optional. Whether this response is for a reprint rather than the original receipt. Omit or set to false for the original receipt; the template only renders the "Reprinted Receipt" banner when this is true. |
| location | Object | The store location the return was processed at: { description, address_line_1, address_line_2, city, state, postal_code, telephone_number }. These should match the values configured on your tenant's Location Information page in the InStore Center. address_line_2 is optional. |
| total | Object | The total amount refunded, as commercetools money. Printed next to Refund: on the receipt. |
| carts | Array<Object> | One entry per returned Cart. Each entry includes line_items, and optionally price_modifiers and attributes per Line Item. |
| refunds | Array<Object> | One entry per refund tender. Each entry includes type and amount, plus the tender-specific fields below. |
| refunds[].cash | Object | Cash tenders only. { roundedRemainder }. Omit when the refund needed no rounding. |
| refunds[].charge_card | Object | Credit tenders only. Card and processor receipt data, such as last4, brand, and the additional_data.receiptData fields returned by your processor. |
| refunds[].gift_card | Object | Stored value (GiftCard) tenders only. { last4, type }. |
| refunds[].voucher | Object | Voucher tenders only. { number }. |
| refunds[].wallet | Object | Wallet tenders only. { method, transaction_id, merchant_id, terminal_id }. |
| tax_summary | Array<Object> | One entry per tax code. Each entry includes code, tax_rate, total_net, and total_tax. |
The refund receipt template doesn't render the following fields, but you can include them if a custom template you build needs them:
| Element | Data type | Description |
|---|---|---|
| refunds[].payment | Object | Information about the original payment method being refunded: { paymentMethodInfo: { paymentInterface, method, name } }. |
| refunds[].reference / originalReference | String | Identifiers for the return transaction and the original transaction it refunds. |
| refundSession | Object | The refund session's status and returnOrders, echoing values from the request InStore sent you. |
| workstation | Object | A fuller workstation reference than workstation_id alone, if you resolve one. |
| originalPayments | Array<Object> | The original payment(s) being refunded, including amountPlanned, paymentMethodInfo, and orderNumber. |
InStore does not validate the shape of your response. Fields you omit or format incorrectly appear blank on the printed or emailed receipt rather than causing an error.