After the payment
Disputes (chargebacks)
A dispute happens when a customer asks their card issuer, their bank or the payment method's provider to reverse a payment — every payment method can be disputed (see by payment method). VezmoPay records every dispute on your payments, tells you in real time, and lets you respond — always with your VezmoPay ids, never a processor's.
There are two kinds:
- Chargeback (
needs_responseand onward) — a formal dispute. The disputed amount is taken from your balance until it is decided (a bank dispute usually arrives already decided). - Inquiry (
warning_*statuses) — the customer's card issuer or bank asks a question before (or instead of) a chargeback. No funds are held and no dispute fee is charged. Answer it quickly: an unanswered inquiry usually becomes a chargeback.
What happens to the money
- When a dispute opens (for a bank payment, usually when it arrives already decided — see by payment method), the disputed amount is taken from your balance and your account's dispute fee is charged — you can see it in your dashboard under VezmoPay → Settings → Fees. It is charged once per dispute and is not returned if you win. An inquiry is charged only if it becomes a chargeback.
- When evidence is submitted to fight a chargeback (including a saved draft, which is sent automatically before the deadline), your account's dispute response fee is charged, once per dispute. It is listed under VezmoPay → Settings → Fees and refunded if you win. Answering an inquiry costs nothing.
- Won — the held amount returns to your balance, and so does the response fee.
- Lost — the held amount goes to the customer. Nothing more is charged at that point. A partly won dispute counts as lost here: the response fee is not refunded.
- The amount can be less than the payment (a partial dispute). Always use the dispute's own
amount. - The payment itself does not change: it was paid, so
GET /merchant/payment/:idkeeps itsstate(e.g.succeeded) and gains adisputeblock. Nopayment.*webhook fires for a dispute — thedispute.*events are the signal.
By payment method
Every payment method can be disputed, but not the same way. Each dispute tells you which one in paymentMethod.
paymentMethod | How it is disputed | Can you respond? |
|---|---|---|
card(incl. Apple Pay, Google Pay) | The cardholder asks their card issuer. Some issuers (mostly Amex and Discover) first send an inquiry (warning_needs_response). A chargeback opens at needs_response, and the amount and fee are taken then. | Yes — submit evidence before evidenceDueBy (usually 7–21 days). The issuer decides: won or lost. |
ach(US bank debit) | The account holder tells their bank the debit was not authorized, up to 60 days after it (2 business days for a business account). The bank's decision is final, so the dispute usually arrives already lost: you receive dispute.created and dispute.closed together, and the amount and fee are taken then. | Usually no. If the bank first asks for proof of authorization, the dispute shows warning_needs_response and the authorization your customer accepted at checkout is sent in reply; if the bank is not satisfied it closes as lost. A saved bank account cannot be charged again after a dispute. |
instantAch(Instant ACH) | The account holder tells their bank the payment was not authorized; the bank reports it as a debit not authorized (reason: "debit_not_authorized"). Unlike a standard bank debit, it opens at needs_response. | Yes — submit evidence within 14 days (evidenceDueBy). |
cashapp(Cash App Pay) | The customer disputes with Cash App (or with their card issuer when a linked card paid). It opens at needs_response, and the amount and fee are taken then. | Yes — evidence within about 13 days (evidenceDueBy); Cash App decides within about 58 days. |
upi(UPI, India) | The customer disputes with their bank or UPI app. When the bank accepts it, the money goes back to the customer straight away, so the dispute usually arrives already lost. | No — a UPI dispute the bank accepted can't be contested. |
pix(Pix, Brazil) | Pix payments can't be disputed, so none arrive. | — |
klarna | The customer complains or asks for a return in the Klarna app, up to 180 days after the payment. It starts as an inquiry (warning_needs_response); refunding the customer then ends it with no fee. After 21 days it becomes a chargeback (needs_response) and the amount is taken. | Not during the inquiry (refund or settle it with the customer). After it escalates, yes — one round of evidence within 12 days (5 for fraud); Klarna decides. A Klarna payment can't be refunded once it has escalated. |
affirm | Affirm opens it (no time limit, once per payment) and the amount is taken at once. | Yes — evidence within 15 days; Affirm decides within 60 days. |
afterpayClearpay(Afterpay) | Afterpay opens it, up to 120 days after the payment, and the amount is taken at once. | Yes — evidence within 14 days; Afterpay decides within 30 days. |
amazonPay | A chargeback, up to 240 days after the payment, or an Amazon A-to-z Guarantee claim (up to 90 days, about delivery or the item's condition). The amount is taken at once. | Yes — usually within 10 days for a chargeback and 6 for an A-to-z claim (always go by evidenceDueBy); Amazon decides within 90 days of your evidence. |
A bank payment that comes back for any other reason (insufficient funds, a closed account) is not a dispute: it arrives as payment.failed with the return reason. See Bank payments.
How to respond
Disputes are answered from your dashboard, not the API: evidence is mostly files (receipts, delivery proof, correspondence). Go to VezmoPay → Disputes, open the dispute, and either:
- Submit evidence — a guided 2-step wizard (product or service details, customer communication, refund policy, delivery proof and file uploads). Submit before
evidenceDueBy; a dispute with no response by then is lost. - Accept the dispute — concede it (the amount stays with the cardholder and the fee is not returned) instead of spending effort on a case you can't win.
Our team can also file evidence on your behalf when you raise it through your To-Do requests.
Webhooks
Subscribe to these in VezmoPay → Developers → Webhooks. Every delivery is signed and retried like any other event (see Webhooks).
| Event | Fires when |
|---|---|
dispute.created | A dispute (chargeback or inquiry) is opened on one of your payments. Always the first event for a dispute. |
dispute.updated | Its status changes without being final: evidence submitted (under_review), or an inquiry escalated to a chargeback (warning_needs_response → needs_response). |
dispute.closed | It reaches a final status: won, lost, warning_closed, charge_refunded or prevented. If we first learn of a dispute when it is already final, you get dispute.created and then dispute.closed. |
An event fires only when the status actually changes, so a dispute never sends the same status twice. All three events carry the same data:
{
"id": "evt_…", // unique per event: one id per status change
"event": "dispute.created",
"data": {
"id": "cmd8x2k1p0001ab12cdef3456", // the VezmoPay dispute id
"paymentId": "cmd7q9w0e0003xy98zyxw7654", // GET /merchant/payment/:id
"transactionId": "VTQ4Z3E6", // the payment's VezmoPay reference
"paymentMethod": "card", // card | ach | instantAch | cashapp | …
"amount": 357, // major units: 357 = $357.00
"currency": "USD",
"reason": "fraudulent",
"reasonCode": "10.4", // the card network's code, or null
"status": "needs_response",
"evidenceDueBy": "2026-10-20T23:59:59.000Z", // respond before this; null if none
"livemode": true, // false for a Test-mode dispute
"createdAt": "2026-10-08T14:03:11.000Z", // when the dispute was opened
"companyId": "…",
"userId": "…", // the payment's owner, or null
"merchantId": "VZM…", // your VezmoPay merchant id
"type": "vezmopay"
}
}| Field | Meaning |
|---|---|
id | The dispute id. Use it with GET /merchant/dispute/:id. |
paymentId / transactionId | The disputed payment — the same id and transactionId as the payment API and the payment.success webhook. null only if we cannot match the payment. |
paymentMethod | How the disputed payment was paid: card (incl. Apple Pay and Google Pay), ach (US bank debit), instantAch (Instant ACH), cashapp (Cash App Pay) or one of the other methods in the table above. Each behaves differently — see By payment method. |
amount | The disputed amount in major units, as a JSON number (357 = $357.00; 12.5 = $12.50). Can be less than the payment. |
reason | The cardholder's reason: fraudulent, product_not_received, product_unacceptable, duplicate, subscription_canceled, credit_not_processed, unrecognized, general and a few rarer ones. Treat unknown values as general. |
reasonCode | The card network's own reason code (e.g. Visa 10.4, Mastercard 4837), when the network sent one. |
evidenceDueBy | ISO 8601 (UTC). The deadline for your response. |
Idempotency: the envelope id is unique per event and stays the same across retries of that event — dedupe on it (or on data.id + data.status).
Amounts in every event (payments, refunds, payouts, disputes) are the same: major units, as a JSON number.
Statuses
| Status | Meaning |
|---|---|
warning_needs_response | An inquiry you can answer. No funds are held and no fee is charged. |
warning_under_review | You answered the inquiry; the bank is reviewing it. |
warning_closed | The inquiry closed without becoming a chargeback. Final. |
needs_response | A chargeback. Submit evidence before evidenceDueBy. |
under_review | Evidence submitted; the cardholder's bank is deciding. |
won | Decided for you; the held amount is back in your balance. Final. |
lost | Decided for the customer (or accepted by you). Final. A bank (ach) dispute usually arrives with this status. |
charge_refunded | The payment was refunded, which closed the dispute. Final. |
prevented | Resolved before it became a chargeback. Final. |
Read disputes with the API
For reconciliation, or to catch up after missed webhooks. Same token as the payment API (see Authentication) and the payment.read scope. A Test key sees sandbox disputes and a live key sees live ones. Each item is the same object as the webhook's data (without companyId, userId, merchantId and type).
List — GET /merchant/dispute
Newest first. Optional query parameters:
status— comma-separated statuses, oropen(still being decided) /closed(final).paymentId— only disputes on this payment.from/to— opened on or after / on or before (YYYY-MM-DDor ISO 8601; a bare date covers the whole day).page(default 1),limit(default 20, max 100).
curl "https://api.vezmo.com/api/v1/merchant/dispute?status=open&limit=50" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"success": true,
"message": "Disputes retrieved",
"data": {
"data": [
{
"id": "cmd8x2k1p0001ab12cdef3456",
"paymentId": "cmd7q9w0e0003xy98zyxw7654",
"transactionId": "VTQ4Z3E6",
"paymentMethod": "card",
"amount": 357,
"currency": "USD",
"reason": "fraudulent",
"reasonCode": "10.4",
"status": "needs_response",
"evidenceDueBy": "2026-10-20T23:59:59.000Z",
"livemode": true,
"createdAt": "2026-10-08T14:03:11.000Z"
}
],
"pagination": { "total": 1, "page": 1, "limit": 50, "totalPages": 1, "nextPage": null, "prevPage": null }
}
}Retrieve — GET /merchant/dispute/:id
One dispute by its id. 404 for an unknown id, another merchant's, or one from the other mode (Test vs live).
curl https://api.vezmo.com/api/v1/merchant/dispute/cmd8x2k1p0001ab12cdef3456 \
-H "Authorization: Bearer $ACCESS_TOKEN"On the payment
GET /merchant/payment/:id and the payment list carry a dispute block with the payment's newest dispute (id, status, amount, currency, reason, evidenceDueBy), or null.
Test disputes in sandbox
- Use your Test API key and a sandbox checkout.
- Pay with card
4000 0000 0000 0259(any future expiry, any CVC). The payment succeeds, then is disputed asfraudulent. - Your endpoint receives
dispute.createdwithlivemode: false, andGET /merchant/disputewith the Test key lists it. It usually arrives within seconds; allow up to 10 minutes. - Submit evidence on it from the dashboard (in Test mode) to receive
dispute.updated(under_review).
To test a bank (ACH) dispute, pay by bank in a sandbox checkout with routing number 110000000 and account number 000555555559. The payment succeeds and is then disputed; you receive dispute.created and dispute.closed with status: "lost" and paymentMethod: "ach".
Test and live events go to the same endpoint — always check livemode. Sandbox disputes move no money and charge no fee.
Reduce disputes
- Use a clear statement descriptor so customers recognize the charge on their statement.
- Refund questionable payments early — a refund costs less than a lost dispute, which also carries the fee and counts toward your dispute rate.
- Answer inquiries: most chargebacks that follow an inquiry follow one that went unanswered.
- If enabled, VezmoPay's pre-dispute alerts can refund flagged payments before they become chargebacks — ask us to turn on Pre-Dispute Protection.