Stripe disputes start when a cardholder asks their bank to reverse a payment. Stripe debits the disputed amount and a dispute fee from your balance, notifies you through the Dashboard, email and the `charge.dispute.created` webhook, and gives you one chance to submit evidence before the deadline in `evidence_details.due_by`. The cardholder's bank, not Stripe, decides whether you win.

This guide is written for the developers and operations leads who own that process. It walks through the dispute lifecycle as Stripe documents it (statuses, inquiries versus chargebacks, deadlines), the webhook events worth handling, how to stage and submit evidence through the API, which evidence fits the common dispute reasons, and the prevention layers that keep disputes rare: Stripe Radar rules, 3D Secure, statement descriptors and early fraud warnings. It ends with Connect implications, an internal workflow and the metrics to watch. Where Stripe publishes a number, we link the page it comes from; where a rule depends on the card network or your contract, we say so.

What is a Stripe dispute, and how is it different from a chargeback?

A dispute is the cardholder's claim against a payment; a chargeback is the money movement that follows when the claim becomes formal. In Stripe's API both are represented by the same Dispute object, and the `status` field tells you which stage you are in.

Some card networks start with a preliminary stage that Stripe calls an inquiry, also known as a retrieval or a request for information. The issuer is asking for clarification, often because the cardholder does not recognize the charge. No funds have moved yet. In the API, inquiry statuses carry a `warning_` prefix: `warning_needs_response`, `warning_under_review` and `warning_closed`. Stripe's documentation notes that American Express and Discover use this stage most often, and that Mastercard and Visa no longer use it. Mexico domestic charges disputed across card brands also go through inquiries first.

A chargeback is the formal dispute. The card network pulls the disputed amount from your Stripe balance and holds it for the duration of the dispute, and Stripe debits a dispute fee as well. You can no longer refund the payment outside the dispute process, and the dispute counts toward your dispute activity with that network whether you win or lose. The statuses here are `needs_response`, `under_review`, `won` and `lost`. The API reference also lists `prevented` (a dispute stopped before it became a formal chargeback) and two appeal statuses, `appeal_needs_response` and `appeal_under_review`, which your code should store even if it never acts on them.

Do not treat an inquiry as a soft warning. Stripe's How disputes work page states that failing to respond to an inquiry can signal acceptance of the claim and escalate it to a formal chargeback that is probably unwinnable. You can resolve an inquiry without a dispute fee by answering it with suitable evidence or by issuing a full refund. If an inquiry stays open for 120 days without escalating, Stripe marks it `warning_closed`; card networks do not send an explicit win for inquiries.

How does the Stripe dispute lifecycle work?

A Stripe dispute moves from payment, through an optional inquiry, to a chargeback that needs a response, then to review by the issuer and a final decision. Two clocks matter: the time you have to respond, recorded in `evidence_details.due_by`, and the time the issuer takes to decide.

The dispute lifecycle with the two clocks that matter. Day ranges are the typical ones Stripe documents; the exact deadline for each dispute is in evidence_details.due_by.

When can a customer open a dispute?

Stripe states that card networks typically allow cardholders to dispute within 120 days of the original payment, with more time in some situations. For payments made in advance for a future event or service, such as a reservation or a ticket, the window generally starts on the event date rather than the payment date. In practice that means a dispute can arrive months after your books for that period closed, so your ledger and your case system need to handle late arrivals without manual digging.

How long do you have to respond?

Usually 7 to 21 days after the chargeback is created, depending on the card network. Do not hard-code a number. Read `evidence_details.due_by` (a Unix timestamp) from the Dispute object and drive every reminder from it. Stripe's Respond to disputes guide is explicit that if you do not respond before the deadline, you automatically lose the dispute and cannot retrieve the funds. `evidence_details.past_due` tells you when that line has been crossed.

How long does the issuer take to decide?

After you submit evidence, the status becomes `under_review` and the issuer usually takes 60 to 75 days to decide, depending on the network. Stripe describes the full lifecycle as taking two to three months, and says you cannot reliably shorten it except by accepting the dispute. When the decision arrives, the status changes to `won` or `lost` and Stripe sends `charge.dispute.closed`. If you win, the issuer returns the disputed amount and Stripe passes it back to you. If you lose, no further money moves from your side. Stripe also documents rare late wins, where a `lost` dispute changes to `won` because the issuer moved funds outside the normal lifecycle, and notes that it does not support the arbitration phase.

Can one payment have more than one dispute?

Yes, rarely. A customer can file a second dispute with a different reason, for a different line item, or the issuer can refile with new information. Each dispute has its own ID and needs its own response. Key your case records on the dispute ID, never on the charge or PaymentIntent, and list disputes by `charge` or `payment_intent` when you need to see all of them for one payment. The disputed amount can also differ from the charge, after currency conversion or a partial dispute, so always read `amount` and `currency` from the dispute itself.

Which webhook events should you handle for Stripe disputes?

At minimum, handle `charge.dispute.created`, `charge.dispute.updated` and `charge.dispute.closed`, and add `charge.dispute.funds_withdrawn` and `charge.dispute.funds_reinstated` if you keep your own ledger. If you act on fraud signals before a dispute exists, also handle `radar.early_fraud_warning.created`.

Here is what each event means, using Stripe's event reference wording. `charge.dispute.created` occurs whenever a customer disputes a charge with their bank, inquiries included. `charge.dispute.updated` occurs when the dispute is updated, usually with evidence, which includes your own staged changes. `charge.dispute.funds_withdrawn` occurs when funds are removed from your account because of a dispute. `charge.dispute.funds_reinstated` occurs when funds are returned after a dispute closes, which includes some partially refunded payments. `charge.dispute.closed` occurs when the status changes to `lost`, `warning_closed` or `won`.

The dispute events worth subscribing to and the single job each one does in your system. Every handler re-reads the dispute from Stripe before acting.

The Dispute object exposes `charge` and a nullable `payment_intent`, but no direct link to an invoice or subscription. If disputes affect access in a subscription product, resolve the subscription from the charge or PaymentIntent in your own records; our subscription webhooks guide covers how to map those events to access decisions. Delivery mechanics such as signature checks, retries, duplicate events and secret rotation are the same as for any Stripe webhook, and we cover them in depth in Stripe webhooks best practices. The handler below applies those rules to disputes: verify, enqueue, return 200 quickly, then process in a worker that re-reads the dispute.

Receiving dispute events and syncing the case record (Node.js)
import express from "express";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const app = express();

// Signature verification needs the raw request body.
app.post("/webhooks/stripe", express.raw({ type: "application/json" }), async (req, res) => {
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      req.headers["stripe-signature"],
      process.env.STRIPE_WEBHOOK_SECRET,
    );
  } catch (err) {
    return res.status(400).send("Signature verification failed");
  }

  if (event.type.startsWith("charge.dispute.")) {
    // Store first, work later. The queue ignores an event.id it has already seen.
    await disputeQueue.enqueue({
      eventId: event.id,
      type: event.type,
      disputeId: event.data.object.id,
      account: event.account ?? null, // set for Connect events from connected accounts
    });
  }
  res.sendStatus(200);
});

// Worker: events can arrive twice or out of order, so read current state from Stripe.
export async function processDisputeEvent({ eventId, type, disputeId, account }) {
  if (await cases.alreadyProcessed(eventId)) return;
  const opts = account ? { stripeAccount: account } : undefined;
  const dispute = await stripe.disputes.retrieve(disputeId, {}, opts);
  const dueBy = dispute.evidence_details?.due_by;

  await cases.upsert({
    disputeId: dispute.id,
    chargeId: dispute.charge,
    paymentIntentId: dispute.payment_intent,
    connectedAccount: account,
    status: dispute.status,
    reason: dispute.reason,
    amount: dispute.amount,
    currency: dispute.currency,
    dueBy: dueBy ? new Date(dueBy * 1000) : null,
    submissionCount: dispute.evidence_details?.submission_count ?? 0,
  });

  if (type === "charge.dispute.created") await alerts.assignOwner(dispute.id);
  if (type === "charge.dispute.closed") await settlement.onDisputeClosed(dispute, opts);
  await cases.markProcessed(eventId);
}

Note what the worker does: it reads the dispute from Stripe instead of trusting the payload, so a late `updated` event cannot reopen a closed case; it keeps `event.account`, needed for follow-up calls on connected accounts; and it stores `due_by` once, so every reminder reads the same deadline.

How do you submit Stripe dispute evidence through the API?

Upload any documents with the File Upload API using the purpose `dispute_evidence`, then update the dispute with an `evidence` hash of text fields and file IDs. Set `submit: false` to stage the evidence for review, and send it with `submit: true`, the default, when it is complete. You get one submission.

Text fields and file fields

The `evidence` hash has two kinds of fields. Text fields take a string, for example `customer_name`, `customer_email_address`, `customer_purchase_ip`, `billing_address`, `product_description`, `service_date`, `shipping_address`, `shipping_carrier`, `shipping_date`, `shipping_tracking_number`, `access_activity_log`, `cancellation_policy_disclosure`, `cancellation_rebuttal`, `refund_policy_disclosure`, `refund_refusal_explanation`, `duplicate_charge_explanation`, `duplicate_charge_id` and `uncategorized_text`. File fields take the ID of an uploaded file: `receipt`, `customer_communication`, `customer_signature`, `cancellation_policy`, `refund_policy`, `service_documentation`, `shipping_documentation`, `duplicate_charge_documentation` and `uncategorized_file`.

Stripe's update a dispute reference sets a combined limit of 150,000 characters across text fields and notes that updating any field in the hash submits all fields in the hash for review. The safe pattern is to assemble the whole packet in your own system and write it in a single update, rather than patching fields one at a time from different services. The `uncategorized_text` and `uncategorized_file` fields exist for anything that does not fit elsewhere, but Stripe advises filling as many specific fields as you can.

File requirements

Stripe's response guidance asks for one file per evidence type, so several documents of the same type should be combined into a single multi-page file. It limits the combined evidence file size to 4.5 MB and Mastercard evidence to 19 pages in total. Issuers will not review external content, so do not rely on links, audio, video or requests to call you. An uploaded file ID can be used in only one API request, so upload fresh files for each dispute instead of caching IDs across cases. Annotated or merged PDFs can fail Stripe's validation; regenerating them through a print-to-PDF step usually fixes that.

Staging versus submitting

The `submit` parameter is what makes a review step possible. With `submit: false`, evidence is staged on the dispute: it is visible in the API and the Dashboard, but nothing has gone to the bank. A later request with `submit: true` sends it. Stripe forwards the response to the issuer immediately, and you cannot edit it or add files afterwards. `evidence_details.has_evidence` and `evidence_details.submission_count` let your system confirm what happened. Build your process so automation stages and a named person submits, at least until you trust the packets your system produces.

Assembling a response: automation gathers and stages, a person checks, and the packet is sent once. Illustrative values.
Staging and submitting product_not_received evidence (Node.js)
import fs from "node:fs/promises";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

// Upload one combined PDF per evidence type. Each file ID can be used in one request.
async function uploadEvidence(path, name, opts) {
  const file = await stripe.files.create(
    {
      purpose: "dispute_evidence",
      file: { data: await fs.readFile(path), name, type: "application/pdf" },
    },
    opts,
  );
  return file.id;
}

// Stage a product_not_received response. Nothing reaches the issuer yet.
export async function stageProductNotReceived(disputeId, order, opts) {
  const shippingDoc = await uploadEvidence(order.labelPdfPath, "label-" + order.id + ".pdf", opts);
  const receiptDoc = await uploadEvidence(order.receiptPdfPath, "receipt-" + order.id + ".pdf", opts);

  return stripe.disputes.update(
    disputeId,
    {
      evidence: {
        customer_name: order.customerName,
        customer_email_address: order.customerEmail,
        product_description: order.description,
        shipping_address: order.shippingAddressOneLine,
        shipping_carrier: order.carrier,
        shipping_tracking_number: order.trackingNumber,
        shipping_date: order.shippedOn, // for example "2026-10-02"
        shipping_documentation: shippingDoc,
        receipt: receiptDoc,
        uncategorized_text: order.reviewerSummary,
      },
      metadata: { order_id: order.id, case_owner: order.caseOwner },
      submit: false,
    },
    opts,
  );
}

// After a person has reviewed the staged packet, send it once.
export async function submitStagedEvidence(disputeId, opts) {
  const dispute = await stripe.disputes.retrieve(disputeId, {}, opts);
  const open = ["needs_response", "warning_needs_response"];
  if (!open.includes(dispute.status)) {
    throw new Error("Dispute " + disputeId + " is " + dispute.status + "; nothing to submit.");
  }
  if (dispute.evidence_details.past_due) {
    throw new Error("The response deadline for " + disputeId + " has passed.");
  }
  return stripe.disputes.update(disputeId, { submit: true }, opts);
}

The same functions work for connected accounts on direct charges: pass `{ stripeAccount: "acct_..." }` as the options argument so both the file upload and the dispute update run on that account. For destination charges and separate charges and transfers, the dispute lives on the platform account and you call without that option. Stripe also offers Smart Disputes, which can submit responses automatically when it detects Stripe evidence; the `intended_submission_method` field on the dispute records which approach you intend to use. If you adopt it, decide per dispute type which path applies so the two do not compete.

What evidence fits each Stripe dispute reason?

Match the evidence to the `reason` on the dispute. Stripe groups hundreds of network reason codes into a small set of categories and publishes, for each one, what the issuer needs to see; the specific network code is in `payment_method_details.card.network_reason_code`.

Stripe's dispute categories guide splits recommendations by product type: physical products, where evidence shows the customer has the goods; digital products or services, where it shows usage, login or download; and offline services such as reservations, where the cancellation policy often decides the case. The summaries below follow that guide. Read it in full for your product type before you write templates.

Fraudulent and unrecognized

The cardholder says they did not authorize the payment, or does not recognize it. Stripe calls fraudulent the most common reason and a difficult one to win, and describes unrecognized as effectively indistinguishable from it. Strong responses show that the cardholder or an authorized person made the payment: for physical goods, delivery to an address verified with AVS or linked to the cardholder, signed delivery and correspondence; for digital goods, the purchase IP address, customer name and email, and an `access_activity_log` showing use after payment. Evidence of earlier undisputed payments on the same card helps. If the payment was authenticated with 3D Secure, say so; Stripe adds the Electronic Commerce Indicator automatically. For Visa disputes with reason code 10.4, Stripe evaluates eligibility for Visa Compelling Evidence 3.0 and flags eligible disputes; Stripe notes that eligibility typically makes overturning the dispute significantly more likely, and that you should not edit the fields it pre-populates.

Product not received

The customer says the goods or service never arrived. Show that it was delivered, or that the agreed delivery date has not yet passed. For shipped goods that means the carrier, tracking number, ship date, shipping address and the label or proof of delivery; Stripe expands tracking numbers into carrier details when it compiles your evidence. For digital goods, use access or download logs tied to the customer. State whether the customer contacted you before disputing, and include that conversation if they did.

Subscription canceled

The customer says you kept charging after they canceled. Show that the subscription was still active and that the customer knew the cancellation procedure and did not follow it. Use `cancellation_policy` for the policy as shown, `cancellation_policy_disclosure` to explain how and when it was shown before purchase, and `cancellation_rebuttal` for why the customer is not entitled to a refund. Usage after the claimed cancellation date is strong evidence for software products. Prevention lives in your product: a cancellation flow that actually cancels in Stripe and sends a confirmation, and our failed payments and dunning guide covers the renewal side of the same problem.

Duplicate

The customer says they were charged twice for one purchase. Either show that each payment was for something different, using `duplicate_charge_id`, `duplicate_charge_explanation` and `duplicate_charge_documentation`, or show that you already refunded the duplicate. Stripe's prevention advice for this category includes making sure your own integration handles errors without double-charging, which in practice means idempotency keys on every payment request.

Credit not processed and product unacceptable

For credit not processed, the customer says they are owed a refund that never came. Show that you already refunded what they were entitled to, or that your refund policy, as disclosed before purchase, does not entitle them to one; use `refund_policy`, `refund_policy_disclosure` and `refund_refusal_explanation`. For product unacceptable, the customer says the goods were defective, damaged or not as described. Show that the product matched its description and worked as sold, and include any return the customer did or did not make under your policy. In both categories, a clear policy that the customer accepted at checkout does most of the work.

General and compliance disputes

A `general` dispute is uncategorized; Stripe advises contacting the customer to learn why. `noncompliant` covers Visa and Mastercard compliance disputes, where contesting adds a network fee on top of Stripe's dispute fees, refunded if you win.

How do you reduce chargebacks on Stripe?

Reduce chargebacks in layers: make every charge recognizable, stop risky payments with Radar and 3D Secure, keep customers informed after they pay, act on early fraud warnings selectively, and answer inquiries before they escalate. No single layer is enough, and won disputes still count toward your dispute activity, so prevention matters more than win rate.

The prevention stack, from checkout to dispute response. The order shows where each control acts, not its relative effect for your business.

How do statement descriptors reduce friendly fraud?

Some disputes, often called friendly fraud, come from customers who see a name they do not recognize and call their bank. Stripe's prevention advice for both the fraudulent and the unrecognized categories starts with a statement descriptor that reflects the business or URL the customer associates with the purchase, and with sending receipts. Stripe's statement descriptor rules require Latin characters, a short length, at least one letter and a name that reflects your doing-business-as name, and they forbid a handful of special characters.

For card payments you can combine a static prefix set in the Dashboard with a dynamic suffix per payment through `statement_descriptor_suffix` on the PaymentIntent, or `payment_intent_data.statement_descriptor_suffix` on a Checkout Session. Use the suffix for something the customer will recognize, such as the product or the order month, not an internal code. Subscription payments take their descriptor from the invoice or the product instead. Check `calculated_statement_descriptor` on a real charge to see exactly what was sent; some banks still display it differently.

Receipts, delivery updates and cancellations

Customers dispute when they cannot reach you. Send receipts they can reply to, send shipping and delay updates, and make cancellation one honest flow that cancels in Stripe and confirms by email. Pass Stripe the details you would later need as evidence: name, email, billing and shipping address, and purchase IP. Stripe notes that this data helps both to prevent disputes and to challenge them.

Early fraud warnings

Early fraud warnings are reports that issuers on Visa, Mastercard and JCB generate for payments they suspect are fraudulent. They arrive as `radar.early_fraud_warning.created` and need no response, but Stripe reports that about 40% of Visa and Mastercard warnings become fraud disputes if the payment is not refunded. Stripe advises against refunding every warning automatically. Its analysis suggests refunding warnings on charges roughly at or below your dispute fee, and that refunding is unlikely to be worthwhile on charges more than 35 percent above it, unless the dispute itself would hurt your account. A refund does not remove the warning, which networks still count, so keep fixing the cause.

How should you use Stripe Radar rules against disputes?

Radar screens every payment with Stripe's default rules. If your Radar plan supports custom rules, you can block, review, allow or request 3D Secure using the syntax `{action} if {attribute} {operator} {value}`, test each rule against past payments, and roll it out to a percentage of traffic before enforcing it fully.

Stripe's Radar rules documentation describes the default AI rules, such as reviewing payments at `:risk_level: = 'elevated'`, and optional built-in rules that block payments failing CVC or postal code checks unless Stripe scores them as low risk. Custom rules use attributes such as `:risk_level:`, `:amount_in_usd:`, `:card_country:`, `:ip_country:`, `:card_funding:`, `:is_off_session:` and your own metadata in double colons, for example `::product_sku::`. Rules apply only to future payments, request-3DS rules are evaluated before review, block and allow rules, and allow rules override everything else, so use them sparingly.

Examples adapted from Stripe's documentation:

  • `Request 3D Secure if :risk_level: != 'normal' and :amount_in_usd: > 25` to add authentication only where risk is above normal.
  • `Review if :is_disposable_email: and :card_funding: = 'prepaid'` instead of reviewing every prepaid card, which would flood the queue.
  • `Block if :card_country: != 'US' and :risk_level: = 'elevated'` instead of blocking all foreign cards, which blocks many legitimate payments.
  • `Request 3D Secure if :is_new_card_on_customer:` to authenticate a card the first time it appears on a customer.

Test before enforcing. The rule editor simulates a rule against recent charges, and traffic allocation lets a rule act on a percentage of matching payments; at 0% it runs in shadow mode and reports what it would have done. Then watch rule metrics: a review rule whose approved payments are later disputed should become a block rule, a block rule with a rising estimated false positive rate is costing you good customers, and a 3DS rule with high completion and few disputes may be adding friction for nothing. Radar also reviews and pauses payouts for platform accounts through account rules, which matters on Connect.

Does 3D Secure shift liability for fraudulent disputes?

Usually, but never guaranteed. When a payment is successfully authenticated with 3D Secure and later receives an eligible fraud dispute, liability typically shifts from you to the card issuer. Stripe's documentation is explicit that neither a successful payment, an `authenticated` result nor an Electronic Commerce Indicator guarantees the shift for any specific payment.

Stripe's 3D Secure guide sets out the limits. Liability shift applies to fraud disputes only; a product-not-received or subscription-canceled dispute follows the normal process. If a card does not support 3D Secure or authentication errors out, the payment proceeds and liability generally does not shift. Shift can be lost if your account is in a network fraud monitoring program, some industries are excluded by network rules, and it can be downgraded after authorization. Wallet payments with equivalent cryptograms, such as Apple Pay or Google Pay, can carry liability shift in some cases.

Two operational points follow. First, record `payment_method_details.card.three_d_secure.result` and the `electronic_commerce_indicator` from the charge when the payment succeeds, so your case system knows which disputes are likely covered. Stripe adds the ECI and the 3D Secure outcome to your evidence when you counter a dispute, but you should still add order details. Second, answer inquiries on authenticated payments. Stripe warns that ignoring one can lead to a no-reply chargeback that may invalidate the liability shift. Radar exposes `:is_3d_secure:`, `:is_3d_secure_authenticated:` and `:has_liability_shift:` so rules can act on the outcome, and you can request authentication yourself with `payment_method_options[card][request_three_d_secure]` set to `any` or `challenge`. Requesting it on every payment costs conversion, which is why risk-based rules are the default approach.

How do disputes work on Stripe Connect?

On Connect, the charge type decides who responds to a dispute and whose balance is debited. Direct charges are disputed on the connected account; destination charges and separate charges and transfers are disputed on the platform, with or without `on_behalf_of`.

For direct charges, Stripe debits the connected account's balance and you respond through the API with the connected account's ID in the `Stripe-Account` header; whether the platform or the connected account pays dispute fees depends on the account's fee configuration, and negative-balance responsibility decides who covers a shortfall. For destination charges and separate charges and transfers, Stripe debits the disputed amount and fees from the platform. The platform can recover funds by creating a transfer reversal, and Stripe's guidance on timing is specific: reverse immediately if you accept the dispute, but if you challenge it, wait for the outcome and reverse only if you lose. Reversing early and repaying after a win can fail on platform balance or cross-border transfer restrictions.

We compare the charge types and their default responsibilities in Stripe Connect direct charges vs destination charges, so here we focus on what changes in dispute code. Your case record needs the connected account ID and the charge type. Your `charge.dispute.closed` handler needs the transfer IDs for the disputed payment so it can reverse the right seller share when a dispute is lost. If connected accounts respond to their own disputes, Stripe's Connect embedded components include a disputes list and a disputes-for-a-payment component, which saves building a response UI. Seller onboarding is part of prevention too; our guide to Connect onboarding flows covers what to collect up front.

What internal workflow, automation and metrics should you set up?

Run disputes as cases: one record per dispute ID, one named owner, a deadline taken from `due_by`, evidence assembled by automation, reviewed by a person and submitted once, and a weekly review of dispute rate by charge date and dispute activity by dispute date.

Decide whether to accept or challenge

Not every dispute is worth fighting. Accept when the claim is valid: the goods never shipped, the refund was owed, or the card was clearly stolen and there was no 3D Secure authentication. Accepting through the Dashboard or the close endpoint (`POST /v1/disputes/{id}/close`) marks the dispute `lost` and is irreversible; Stripe notes that accepting an inquiry does not resolve it, so answer inquiries with evidence instead. Challenge when you have evidence that answers the specific reason. Stripe charges a dispute countered fee when you challenge, in addition to the fee for receiving the dispute, and returns the countered fee if you win; check the current amounts for your country on Stripe's pricing pages before you set a threshold. A customer can also withdraw a dispute, but you still have to submit evidence for it to close in your favor.

Automate gathering, not judgment

Good automation does the tedious part. On `charge.dispute.created`, look up the order, customer, delivery and usage records from your own systems; render receipts, policies and logs to PDF; map them to the evidence fields for the dispute's reason; and stage the packet with `submit: false`. Then notify the owner with a link to the staged packet and the deadline. Reminders should escalate at fixed points before `due_by`, and a dispute with no owner or no staged evidence two days before the deadline should page someone. Keep human review for submission until the packets are consistently right.

Which dispute metrics should you monitor?

Stripe's Dashboard shows two measures. Dispute activity is disputes divided by successful payments, counted by the date the dispute arrived; card network monitoring programs use this measure. Dispute rate counts disputes by the date of the original charge, which shows which sales, campaigns or attack windows actually caused disputes, and keeps changing for about 120 days as late disputes arrive. Track both, alongside early fraud warnings, which Visa counts toward its monitoring program.

  • Dispute activity and dispute rate by card network, product and, on Connect, by connected account.
  • Disputes by reason. A rise in `subscription_canceled` points at your cancellation flow; a rise in `fraudulent` points at Radar and 3D Secure.
  • Response coverage: the share of disputes answered before `due_by`, and the median days from creation to submission.
  • Win rate by reason, read with care, since a high win rate on a growing number of disputes is still a problem.
  • Early fraud warnings and how many became disputes, to tune your refund threshold.
  • Radar rule performance: disputes from approved reviews, disputes from allow-rule overrides, and estimated false positives on block rules.

Feed these from your case records rather than from email; Sigma and Data Pipeline can also query dispute and fraud data. For the broader payments foundations these numbers depend on, such as idempotent payment creation and complete customer data at checkout, see our Stripe integration guide.

From here, this article is about Cevrynt

How Cevrynt approaches Stripe dispute handling

Cevrynt, Inc. is a Stripe partner in the Stripe Partner Ecosystem. The certifications belong to the people who hold them: Arindam Pradhan, our founder, is a Stripe Certified Professional (Implementation Architect), and Anupam Pradhan and Subhadip Giri are Stripe Certified Professionals (Payments Architect). Our dispute work starts by mapping how disputes reach your team today, which charge types you use and where the evidence lives, then building the webhook handling, case records, evidence staging and ledger entries in your repository and your Stripe account, with refund, dispute and negative-balance edge cases tested before launch.

Every project is scoped after a technical review. You can see how we approach payments work on our Stripe payments services page, read about marketplace funds flows on the Stripe Connect service page, or tell us about your dispute process and we will reply with the questions we need answered first.