Disputes & chargebacks

Dispute flow, defense requirements, reason codes, and the Disputes API.

View as MarkdownInstall skills

A dispute (chargeback) is initiated by a cardholder through their issuing bank. When opened, VINR debits the disputed amount from your balance, notifies you, and gives you a window to submit evidence. This page explains the dispute lifecycle, how to respond, and how to use the Disputes API.

Dispute lifecycleAsk

Opened → Evidence due → Under review → Won / Lost
                                     ↘ Accepted (no contest)
StageWhat happensTime limit
OpenedIssuer files dispute; funds debited; dispute.opened webhook fired.
Evidence dueYou upload evidence to contest the dispute.Varies by network (7–20 days)
Under reviewIssuer reviews evidence; no further action from you.30–75 days
WonFunds returned to your balance; dispute.won webhook fired.
LostDecision final; dispute.lost webhook fired.
AcceptedYou accepted the chargeback; dispute.accepted webhook fired.

Missing the evidence deadline forfeits the dispute automatically. Subscribe to dispute.opened and dispute.evidence_due webhooks, and set up Dashboard notifications so nothing slips through.

Dispute reason codesAsk

Reason codes identify the cardholder's claim. Defense requirements differ by code.

CodeClaimKey evidence
consumer_dispute.not_as_describedProduct/service differs from descriptionClear product description, photos, delivery confirmation
consumer_dispute.not_receivedGoods/services not receivedProof of delivery, tracking, customer communication
consumer_dispute.cancelledSubscription cancelled but chargedCancellation policy, confirmation email, timestamp
consumer_dispute.duplicateCharged more than onceTransaction log showing single charge, refund if applicable
fraud.card_not_presentCardholder claims they didn't authorize3DS authentication result, AVS match, device fingerprint
fraud.card_presentCardholder disputes in-person chargeEMV chip data, signed receipt, terminal log
credit_not_processedRefund promised but not receivedRefund record or proof refund was processed

Full code reference is available in the API reference.

Responding to a disputeAsk

Review the claim

In Dashboard → Operations → Disputes, open the dispute. Read the cardholder's statement and reason code carefully — the evidence you submit must directly address their specific claim.

Gather evidence

Collect what you need based on the reason code table above. The evidence object accepts structured fields (preferred) and/or file attachments.

Submit

You have one submission window. Make it complete — you cannot add evidence after submission.

import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

await vinr.disputes.update('dis_...', {
  evidence: {
    customerName: 'Maria Santos',
    customerEmailAddress: 'maria@example.com',
    productDescription: 'Annual SaaS subscription — confirmed in onboarding email',
    accessActivityLog: 'Customer logged in 47 times between purchase and dispute date',
    refundPolicy: 'No refunds after 14-day trial per Terms of Service section 8',
    // Attach files as base64 or upload separately via vinr.files.create()
    receiptUrl: 'https://cdn.example.com/receipts/inv_2026_04.pdf',
  },
  submit: true,   // false to save as draft, true to submit immediately
});

Accept (optional)

If a dispute is clearly valid (e.g. a genuine duplicate charge), accept it immediately rather than contesting. This keeps your dispute rate clean — a contested loss counts the same as an accepted dispute.

await vinr.disputes.accept('dis_...');

Uploading evidence filesAsk

Large evidence (PDFs, screenshots) should be uploaded as files first, then referenced in the evidence object:

import { readFileSync } from 'fs';

const file = await vinr.files.create({
  purpose: 'dispute_evidence',
  file: {
    data: readFileSync('tracking-screenshot.png'),
    name: 'tracking-screenshot.png',
    type: 'image/png',
  },
});

await vinr.disputes.update('dis_...', {
  evidence: { shippingDocumentation: file.id },
  submit: true,
});

Dispute webhooksAsk

Subscribe to these events to drive automated workflows:

EventWhen
dispute.openedDispute created; funds debited. Act immediately.
dispute.evidence_due48-hour reminder before the evidence deadline.
dispute.updatedStatus changed (e.g. issuer requests more information).
dispute.wonFunds returned; dispute closed in your favor.
dispute.lostDispute closed against you; funds not returned.
dispute.acceptedYou accepted the dispute; resolved.
// Webhook handler
const event = vinr.webhooks.verify(payload, signature);

if (event.type === 'dispute.opened') {
  const dis = event.data;
  await alertOpsTeam({
    id: dis.id,
    amount: dis.amount,
    currency: dis.currency,
    reason: dis.reason,
    evidenceDueBy: dis.evidenceDueBy,   // ISO 8601
  });
}

Preventing disputesAsk

Most disputes are preventable. High-impact interventions:

  • 3D Secure — Shifts liability to the issuer for fraud.* reason codes. Enable on high-value or high-risk transactions.
  • Clear billing descriptors — Cardholder confusion ("I don't recognize this charge") drives consumer_dispute.* disputes. Use a recognizable name.
  • Prompt refunds — Process refunds within 5 business days to head off credit_not_processed disputes.
  • Cancel subscriptions instantly — A customer who cancelled but was charged 30 days later is almost certain to dispute. Honor cancellations immediately.
  • Send receipts — A receipt email gives you evidence and reduces post-purchase doubt.

Card networks monitor your dispute rate. Visa and Mastercard have early-warning programs that trigger at 0.65%; exceeding 1% for two consecutive months places your account in a monitoring program with fees and potential suspension risk.

Chargeback Uploader (bulk)Ask

For platforms or high-volume merchants, upload evidence files in bulk using the Chargeback Uploader. Prepare a ZIP archive following the upload format spec, then:

await vinr.disputes.bulkUpload({
  file: readFileSync('evidence-bundle.zip'),
  // Evidence is matched to disputes by payment ID from the manifest file inside the ZIP
});

Test a dispute scenarioAsk

Use test card 4000000000000259 (any future expiry, any CVC) to trigger an automatic dispute in test mode. The dispute is created approximately 7 seconds after the charge is captured, giving you a sandbox to test your webhook handler and evidence submission flow end-to-end.

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page