Guides / Payments

Get Instant Alerts for Stripe Disputes

4 min read·Payments

Why Speed Matters

Stripe disputes have a response deadline — typically 7–21 days depending on the card network. Every hour you're unaware is an hour you can't spend gathering evidence. An immediate alert gives you maximum time.

Step 1: Register the Webhook in Stripe

In your Stripe Dashboard → Webhooks, add a new endpoint and enable:

  • charge.dispute.created
  • charge.dispute.updated
  • charge.dispute.closed

Step 2: Handle the Events

ts
// app/api/webhooks/stripe/route.ts
import Stripe from "stripe";
import { logit } from "@/lib/logit";

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

export async function POST(req: Request) {
  const body = await req.text();
  const sig = req.headers.get("stripe-signature")!;

  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch {
    return new Response("Invalid signature", { status: 400 });
  }

  if (event.type === "charge.dispute.created") {
    const dispute = event.data.object as Stripe.Dispute;
    const amount = (dispute.amount / 100).toFixed(2);
    const currency = dispute.currency.toUpperCase();

    await logit.now("disputes", {
      event: "Dispute opened",
      description: `${currency} ${amount}${dispute.reason} — respond by ${new Date(dispute.evidence_details.due_by! * 1000).toLocaleDateString()}`,
      icon: "⚖️",
      notify: true,     // always ping immediately for disputes
      tags: {
        reason: dispute.reason,
        status: dispute.status,
        currency: dispute.currency,
      },
      metadata: {
        disputeId: dispute.id,
        chargeId: dispute.charge,
        amount: dispute.amount,
        reason: dispute.reason,
        evidenceDueBy: dispute.evidence_details.due_by,
        stripeStatus: dispute.status,
      },
    });
  }

  if (event.type === "charge.dispute.closed") {
    const dispute = event.data.object as Stripe.Dispute;
    await logit.now("disputes", {
      event: `Dispute ${dispute.status}`,
      description: `${dispute.currency.toUpperCase()} ${(dispute.amount / 100).toFixed(2)} — outcome: ${dispute.status}`,
      icon: dispute.status === "won" ? "✅" : "❌",
      notify: true,
      tags: { reason: dispute.reason, outcome: dispute.status },
      metadata: { disputeId: dispute.id, chargeId: dispute.charge },
    });
  }

  return new Response("ok");
}

Step 3: Add a Smart Action for Dispute Spikes

Multiple disputes in a short window may indicate fraud:

FieldValue
NameDispute spike
Channeldisputes
Event nameDispute opened
Threshold3
Window24 hours
Cooldown24 hours
ActionPush notification + Slack webhook

Dispute Response Checklist

When the alert fires:

  1. Open Stripe Dashboard → Disputes
  2. Check the evidence due date in the event metadata
  3. Gather evidence: receipt, shipping confirmation, correspondence with customer
  4. Submit evidence before the deadline

Tips

  • Use notify: true on every dispute — they're too important to let threshold rules delay.
  • Log the evidenceDueBy timestamp in metadata so it's immediately visible in the LogIt event detail.
  • A dispute spike (3 in 24h) often signals a fraud wave — add the Smart Action to catch it early.

Try LogIt free

7-day trial. No credit card required.

Start free