Vezmo App

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_response and 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/:id keeps its state (e.g. succeeded) and gains a dispute block. No payment.* webhook fires for a dispute — the dispute.* 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.

paymentMethodHow it is disputedCan 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.—
klarnaThe 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.
affirmAffirm 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.
amazonPayA 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).

EventFires when
dispute.createdA dispute (chargeback or inquiry) is opened on one of your payments. Always the first event for a dispute.
dispute.updatedIts status changes without being final: evidence submitted (under_review), or an inquiry escalated to a chargeback (warning_needs_response → needs_response).
dispute.closedIt 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:

dispute.created
{
  "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"
  }
}
FieldMeaning
idThe dispute id. Use it with GET /merchant/dispute/:id.
paymentId / transactionIdThe disputed payment — the same id and transactionId as the payment API and the payment.success webhook. null only if we cannot match the payment.
paymentMethodHow 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.
amountThe disputed amount in major units, as a JSON number (357 = $357.00; 12.5 = $12.50). Can be less than the payment.
reasonThe 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.
reasonCodeThe card network's own reason code (e.g. Visa 10.4, Mastercard 4837), when the network sent one.
evidenceDueByISO 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

StatusMeaning
warning_needs_responseAn inquiry you can answer. No funds are held and no fee is charged.
warning_under_reviewYou answered the inquiry; the bank is reviewing it.
warning_closedThe inquiry closed without becoming a chargeback. Final.
needs_responseA chargeback. Submit evidence before evidenceDueBy.
under_reviewEvidence submitted; the cardholder's bank is deciding.
wonDecided for you; the held amount is back in your balance. Final.
lostDecided for the customer (or accepted by you). Final. A bank (ach) dispute usually arrives with this status.
charge_refundedThe payment was refunded, which closed the dispute. Final.
preventedResolved 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, or open (still being decided) / closed (final).
  • paymentId — only disputes on this payment.
  • from / to — opened on or after / on or before (YYYY-MM-DD or ISO 8601; a bare date covers the whole day).
  • page (default 1), limit (default 20, max 100).
Request
curl "https://api.vezmo.com/api/v1/merchant/dispute?status=open&limit=50" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
200 OK
{
  "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).

Request
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

  1. Use your Test API key and a sandbox checkout.
  2. Pay with card 4000 0000 0000 0259 (any future expiry, any CVC). The payment succeeds, then is disputed as fraudulent.
  3. Your endpoint receives dispute.created with livemode: false, and GET /merchant/dispute with the Test key lists it. It usually arrives within seconds; allow up to 10 minutes.
  4. 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.