> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Refund Records, IDs, Notifications and Timing

> Every identifier a refund carries and which one to quote to whom, why some refunds ask you for the customer's bank account and name, what to do when you typed those details wrong, when the refund webhook fires and where it goes, how long a refund typically takes to reach the customer, and whether a customer can start a refund themselves.

Whether a refund is *allowed* is covered elsewhere; this page is about the refund
**record** — its identifiers, the extra details some refunds ask you for, how you
are told the outcome, and what to tell a customer who is waiting.

| Page | Use it for |
| - | - |
| [Refund FAQ](/docs/en/support/refund-faq) | The core rules, cross-currency amounts, retrying safely |
| [Refund Limits by Payment Method](/docs/en/support/payment-method-refund-limits-faq) | How many, how much, by when, and per-method differences |
| [Where Refund Money Comes From](/docs/en/support/refund-funding-source-faq) | Balances, insufficient funds, settlement impact |
| [Batch Refunds](/docs/en/support/batch-refund-faq) | The CSV flow and batch tracking |
| **This page** | IDs, extra payee fields, notifications, timing, exports |

## Which identifiers does a refund carry, and which do I quote to whom?

A refund carries up to six identifiers, three of which are yours and three of
which are Waffo's:

| Field | Who generates it | What it identifies |
| - | - | - |
| `refundRequestId` | **You** (max 32) | This refund **request**. It is the idempotency key — reuse it to retry the same refund safely |
| `merchantRefundOrderId` | **You** (max 64) | Your own refund order number, for your books |
| `acquiringRefundOrderId` | **Waffo** | The refund order. **This is the one to quote to Waffo support** |
| `acquiringOrderId` | **Waffo** | The **original payment** being refunded |
| `origPaymentRequestId` | **You**, on the original payment | Your request id for the original payment — how a refund is tied back to your own payment record |
| Refund Batch ID | **Waffo** | Present only for refunds created from a batch file — see [Batch Refunds](/docs/en/support/batch-refund-faq) |

Choosing between them:

* **Talking to Waffo support** → `acquiringRefundOrderId`, plus
  `acquiringOrderId` if the question involves the original payment.
* **Reconciling your own books** → `merchantRefundOrderId` and
  `origPaymentRequestId`. These are the only two that mean anything inside your
  system.
* **Retrying a refund** → `refundRequestId`, unchanged. A new one creates a new
  refund; that is the whole point of the field.
* **Talking to the customer** → none of the above. The customer sees the
  *original payment's* reference in their own payment history, which comes back
  on the original order as `payMethodRefId` inside `payMethodResponse`. If you
  need to point a third party at the money, that is the reference they will
  recognise — see
  [Which Amount and Currency Fields Do I Check?](/docs/en/support/order-amount-and-currency-fields-faq).

Both `merchantRefundOrderId` and `refundRequestId` are visible as columns in
Portal → Transactions → Refunds, so a refund raised through the API can be found
in the Portal by your own number, and one raised in the Portal can be found by
its Waffo number.

## Why does a refund ask me for the customer's bank account, name or nationality?

Because for some payment methods the money cannot simply be pushed back along the
path it came — the refund is executed as a payout to the customer, and a payout
needs payee details that a payment never required. It is a property of the
payment method, not of your account or of the individual refund.

The API models this on the refund request as an optional `userInfo` object that
becomes mandatory when the method requires it:

| Group | Fields |
| - | - |
| Bank details | `userInfo.userBankInfo.bankAccountNo`, `bankCode`, `bankName`, `bankCity`, `bankBranch` |
| Identity | `userInfo.userType` (`INDIVIDUAL` / `BUSINESS`), `userFirstName`, `userMiddleName`, `userLastName`, `nationality` |
| Contact | `userInfo.userEmail`, `userInfo.userPhone` (`+CountryCode-Number`) |
| Identification | `userInfo.userIDType`, `userIDNumber`, `userIDIssueDate`, `userIDExpiryDate` (dates as `dd/mm/yyyy`) |

**Where do you get the values?** From the customer. Waffo does not hold the
customer's bank account, and neither did the original payment — which is exactly
why the field is being asked for. In practice this means a refund on one of these
methods is not a one-click operation: you have to go back to the customer for
their account details first, so build that into your returns process rather than
discovering it at the moment you press Refund.

**You are told which fields are missing, rather than having to guess.** Both
entry points work the same way:

* **In the Portal**, opening the refund drawer asks for exactly the fields that
  order needs, with a searchable bank list where a bank has to be chosen from a
  fixed set and a country list for nationality. Names are capped at 60
  characters, other text fields at 64, and the phone number is validated against
  the payment's country dial code. Middle name is the one optional field.
* **Through the API**, a refund submitted without a required field comes back
  telling you which field paths are missing, so you can collect those and
  resubmit. It is normal for a second round to ask for more than the first —
  supply what you are asked for and resubmit rather than pre-filling everything.

The general mechanism, and the notation used for the field paths, is in
[Conditionally Required Fields by Payment Method](/docs/en/support/conditional-required-fields-faq).

## I typed the bank account wrong and the refund failed. Can I fix that refund?

No. A refund record is not editable, and there is no "correct and resend" action
on a failed one. Raise a **new refund**, with a **new `refundRequestId`**, and the
corrected details.

Two things to check before you resubmit:

* **The refundable amount.** Use `remainingRefundAmount` from the refund response
  or refund inquiry rather than assuming the failed attempt released everything —
  it is the authoritative figure and it already accounts for every refund on the
  order.
* **The details themselves.** A refund that failed on wrong payee details will
  fail again on the same details. Confirm the account with the customer, not from
  your own records.

Reusing the *same* `refundRequestId` will not help either: it is the idempotency
key, so resending it asks for the original refund back rather than for a new
attempt with new details. Idempotent-retry behaviour is covered in
[Refund FAQ](/docs/en/support/refund-faq).

## Can a customer start a refund themselves?

**No.** A refund is initiated by you — through the Portal or through the refund
API — and there is no customer-facing path to one. The Waffo checkout does not
offer a refund action, and neither does the subscription self-management page a
customer can reach: cancelling a subscription there stops future charges, it does
not reverse past ones.

`refundSource` on a refund record makes the same point in field form. Its four
values are:

| `refundSource` | Meaning |
| - | - |
| `MERCHANT` | You initiated it |
| `RDR` | Raised automatically by the chargeback alert service |
| `ETHOCA` | Raised automatically by the chargeback alert service |
| `CDRN` | Raised automatically by the chargeback alert service |

There is no customer value, because there is no customer path. What a customer
*can* do is raise a dispute with their bank, which is a different mechanism with
different money and different deadlines — see
[Chargeback FAQ](/docs/en/support/chargeback-faq).

Practical consequence for your product: if you want self-service returns, build
the request flow on your side and call the refund API when you approve it. Do not
wait for a customer-facing Waffo screen to point them at.

## I already refunded the customer myself. Will calling the refund API pay them twice?

Yes. A refund raised through Waffo is a real movement of money, and Waffo has no
knowledge of anything you settled with the customer outside the platform — a bank
transfer you made yourself, a store credit, a cash refund at a counter. Nothing
reconciles the two, so a refund call after an off-platform refund pays the
customer a second time.

The safeguard is the refund record itself, not a check Waffo can perform for you:

* **Read the order's refund history before refunding**, in the Portal or via
  refund inquiry, and read `remainingRefundAmount`. That tells you what Waffo has
  already sent — including refunds you did not raise.
* **Record off-platform settlements in your own system** and gate your refund
  action on that. This is the only place that information exists.
* **Prefer refunding through Waffo** where the payment method allows it, precisely
  so that the refund lands in the same record as the payment.

The same logic applies in reverse: an order Waffo has already refunded does not
need a second gesture from you, and paying the customer again off-platform is the
mirror-image mistake.

## Can I refund only part of the amount from the Portal?

Yes. The Portal's refund drawer opens with the full refundable amount pre-filled
and the amount field is editable: you may enter any amount **greater than zero and
not more than** the payment amount, in the order's currency and within that
currency's decimal precision. A reason is mandatory alongside it, and you confirm
the exact figure in a dialog before the refund is created.

Whether a *second* partial refund is then available on the same order is a
payment-method question, not a Portal one — some methods permit one partial refund
and no more. See
[Refund Limits by Payment Method](/docs/en/support/payment-method-refund-limits-faq)
before you plan a staged refund, and note that a stablecoin order refunded from
the Portal additionally requires a receiving address on the original chain.

## The refund record says `RDR`, `ETHOCA` or `CDRN`. Can I still refund the order?

That order has already been refunded automatically, to head off a dispute the
alert service saw coming. So:

* **No further refund is available on that order.** As soon as the order carries
  a non-failed refund raised by the chargeback alert service, every later refund
  attempt is rejected with a dedicated error — `A0039 Order has already been
  refunded by chargeback alert service` — **whatever the amount**. The check does
  not look at whether the automatic refund covered the full payment, so a
  `remainingRefundAmount` greater than zero on such an order does not mean you
  can refund the rest through the API or the Portal. If you believe money is
  still owed to the customer, that is a support case.
* **Do not refund the customer again outside Waffo** on the assumption that the
  automatic refund did not happen. Read the refund record first — that is what
  `refundSource` is for.

An order under an active dispute is separately blocked from refunds, with its own
error (`A0019`). The distinction matters: `A0039` means *already refunded*,
`A0019` means *locked while the dispute runs*. Neither is retryable as-is.

## When does the refund webhook fire, and where does it go?

It fires when the refund reaches its **final** state — completed or failed. There
is no intermediate "refund accepted" webhook, so you will not receive a
notification for `REFUND_IN_PROGRESS`.

Where it is delivered:

| Situation | Destination |
| - | - |
| `refundNotifyUrl` sent on the refund request | That URL |
| Not sent, but a refund notification URL is configured on your account | The configured URL |
| Neither | No webhook. Use `/api/v1/refund/inquiry` to get the outcome |
| Refunds created from a batch file | The configured account URL — batch rows carry no per-row URL |

The event type is `REFUND_NOTIFICATION`, and its body is the same structure as
the refund inquiry response, so one parser serves both. That is deliberate: an
integration that can read refund inquiry can read the webhook, and polling is a
legitimate fallback rather than a second implementation.

Two operational notes. `refundNotifyUrl` is capped at 256 characters — shorter
than the payment `notifyUrl` — and the same delivery rules apply as for payment
webhooks: reachable HTTPS on a standard port, and the expected success response
body. Delivery troubleshooting is in
[Webhook retry](/docs/en/developer-docs/webhook/retry) and
[Portal webhook log](/docs/en/support/portal-webhook-log-faq).

## Can I get the refund result by polling instead of waiting for a webhook?

Yes. `/api/v1/refund/inquiry` returns the refund's current state and is the
documented fallback when you have no notification URL configured, when signature
verification fails, or when you simply want the answer now. It returns
`refundStatus`, `refundFailedReason` when it failed, `refundCompletedAt`,
`remainingRefundAmount` and the full identifier set.

The same applies to payments — Order Inquiry is a first-class way to get a
result, not a workaround. What polling does not do is remove the need to handle
the webhook: build the webhook path, and use inquiry to close gaps and to answer
"where is it now" questions from your support team.

## How long does a refund take to reach the customer?

It depends on how they paid, and the honest range is wide. Waffo's part — moving
the refund to its final state — is what `refundStatus` reports; the last leg is
in the hands of the customer's bank, card issuer or wallet provider, and Waffo
does not commit to a delivery date for it. The Portal's support screen shows
indicative processing times per payment method; use those as the reference when
you talk to a customer. The shape of the answer is stable even if the figures are
not:

| How the customer paid | What to expect |
| - | - |
| Credit or debit card | The slowest path — the refund lands on a statement cycle set by the issuer, not on a day anyone can predict |
| Online banking or bank transfer | Working days rather than hours, varying by bank and country |
| Wallet — refunded to wallet balance | Usually fast, often the same day |
| Wallet — refunded another way | Working days, varying by wallet or bank |
| Manual transfer, ATM or cash deposit | Working days, affected by the bank and the country |

How to use this with a customer:

* **While the refund shows as in progress**, tell them the refund is moving;
  there is nothing to escalate.
* **Once the refund shows as completed** and the money still has not arrived
  after the indicative window for their method, that is worth reporting — with
  the `acquiringRefundOrderId`.
* **Do not promise a date.** Card refunds in particular land on a statement
  cycle, not on a day you can predict.

Whether a refund can be *made* at all on a given method, and the per-method
deadlines for making it, are a different question — see
[Refund Limits by Payment Method](/docs/en/support/payment-method-refund-limits-faq).

## Does a refund give the customer back the fees and taxes they paid?

Not necessarily, and the API is explicit about it: on a refund, the
`finalDealAmount` — *the amount the customer receives* — may **exclude** a
service or tax fee the customer originally paid, which is why it can be smaller
than the corresponding order amount.

So there are two different amounts to keep straight when you communicate with a
customer:

| Field | What it is |
| - | - |
| `refundAmount` | What you asked to refund, in the order currency |
| `finalDealAmount` | What the customer actually receives |

Tell the customer the second one. If your policy is to make them whole including
fees, you have to price that in yourself; a refund of the order amount does not
automatically carry the fees back.

What happens to the fee **you** paid on the original payment, and to a
merchant-borne tax, is a settlement question rather than a refund-record one —
see [Refund FAQ](/docs/en/support/refund-faq) and
[Fee & Tax Treatment](/docs/en/support/fee-and-tax-treatment-faq).

## How long can the refund reason be, and does the customer see it?

`refundReason` is **mandatory** on every refund. The limit depends on where you
raise it: the API accepts up to **256** characters, and the Portal's refund form
caps the field at **128** with a live character count.

It is an internal annotation: it is carried on the refund record, appears in the
Portal and comes back on refund inquiry, and it is what your own support team
reads six months later. Write it for that reader — an order reference, a returns
ticket number, a short reason — rather than as a message to the customer.

## Where do I see my refunds, and what can I export?

Portal → Transactions → **Refunds** is the list. Its columns cover the whole
identifier set plus the state of each refund:

| Group | Columns |
| - | - |
| Identifiers | Refund order id, original order id, your refund order id, your original order id, refund request id, Refund Batch ID, sub-merchant id where you use them |
| Amount | Refund amount and currency |
| Method | Payment method, type and country, masked card number |
| State | Refund status, settlement status |
| Times | Created date and time, original payment date and time |
| Links | Related subscription id, your user id |

The list filters on the refund identifiers, status, currency, Refund Batch ID and
created datetime, and it can be exported. Exports run as background tasks with
their own span and size limits and their own status list — including the case
where an export succeeds with a non-zero failed count — all of which is in
[Export Center](/docs/en/support/export-center-faq). Which columns the transaction and
refund exports carry is in
[Payment Method Config & Limits](/docs/en/support/payment-method-config-and-limits-faq).

If a refund you raised through the API is not in the list, search by your own
`refundRequestId` or `merchantRefundOrderId` before concluding it was lost — those
are columns, and a refund is findable by your own number.

## The refund says it succeeded but the customer says the money never arrived

Work through it in this order, because three different situations produce the
same complaint:

1. **Check the indicative window for their payment method** (the Portal's
   support screen shows it). A card refund that has not yet reached the next
   statement cycle is not late.
2. **Check `finalDealAmount`**, not `refundAmount`. If a fee was not returned,
   the customer received less than they expected and may read that as "not
   arrived".
3. **Check where the money was sent.** On methods refunded to a wallet balance it
   goes to the wallet, not the card behind it — a customer looking at their card
   statement will not see it.

If all three check out and the money is still missing, that is a support case:
send the `acquiringRefundOrderId` and the date. Refunds that report success but
fail downstream, and refunds that end in `ORDER_REFUND_FAILED`, are covered in
[Refund Limits by Payment Method](/docs/en/support/payment-method-refund-limits-faq).

Refunding a stablecoin payment has its own required field — a receiving address
on the same chain — and its own rules about who pays the network fee; those are
in [Stablecoin exceptions](/docs/en/support/stablecoin-exceptions-faq).

***

Need a refund traced? [Contact support](mailto:support@waffo.com) with the
`acquiringRefundOrderId`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.