For MerchantsAutomation

Automate a chargeback dispute

Receive a dispute.created webhook for any processor, fetch the dispute, attach evidence and notes, and let Chargeflow build and submit the representment for you.

This recipe is a full, copy-paste-runnable flow for responding to a chargeback with Chargeflow, whatever processor it came from. You receive the dispute.created webhook, fetch the dispute, attach supporting evidence and notes, and Chargeflow builds and submits the representment on your behalf, ahead of the deadline.

The flow is identical for every processor. Each dispute carries a source field (stripe, paypal, shopify_payments, braintree, and so on) telling you where it originated, but you call the same Chargeflow endpoints regardless. See Integrations for how each processor connection ingests disputes.

Chargeflow submits, you don't

You never call a submit endpoint or talk to the processor's dispute API. Once a dispute is in needs_response and your evidence is attached, Chargeflow assembles the evidence package and files the representment for you. This recipe is about feeding Chargeflow the context, not about submitting.

Before you start

  • Connect at least one payment processor to Chargeflow. After that, new disputes from that processor are ingested automatically. See Integrations.
  • Get your API Access Key. Every request sends it in the x-api-key header. See Authentication.
  • Register a webhook endpoint for dispute.created. See Subscribe to Webhook Events.

All requests go to https://api.chargeflow.io under the /public/2025-04-01/ path.

Steps

Register for the dispute.created event

Register a webhook endpoint so Chargeflow notifies you when a new dispute is ingested, from any connected processor.

Terminal
curl -X POST https://api.chargeflow.io/public/2025-04-01/webhooks \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "event": "dispute.created"
  }'

Receive the dispute.created webhook

When a cardholder disputes a payment, Chargeflow ingests the dispute, runs its first enrichment pass, and posts a dispute.created event to your endpoint.

Note

The dispute.created event arrives wrapped in the delivery envelope: the dispute fields are inside data, and data.id is the Chargeflow dispute ID you use in the rest of this recipe.

Example payload (the source here is stripe, but it could be any connected processor):

Webhook payload
{
  "type": "dispute.created",
  "webhookId": "123e4567-e89b-12d3-a456-426614174000",
  "creationDate": "2025-01-27T10:00:00Z",
  "data": {
    "id": "dis_12345",
    "source_id": "du_123456789",
    "account_id": "act_112233",
    "created_at": "2025-01-27T09:00:00Z",
    "reason": "fraud",
    "due_by": "2025-02-10T10:00:00Z",
    "source": "stripe",
    "amount": 150,
    "currency": "USD",
    "status": "needs_response",
    "stage": "Chargeback",
    "closed_at": null,
    "transaction": "tx_1234567890",
    "subscription": null,
    "order": "ord_1234567890",
    "customerCommunication": null
  }
}

Your endpoint must return a 2XX status promptly, then process the event asynchronously. Acknowledge first, work after.

Node.js
const express = require('express');
const app = express();

app.post('/webhook', express.json(), (req, res) => {
  const event = req.body;

  // Acknowledge receipt immediately so Chargeflow does not retry
  res.json({ received: true });

  if (event.type === 'dispute.created') {
    // Same flow for every processor - branch on event.data.source only if you
    // need processor-specific handling on your side.
    handleDispute(event.data.id);
  }
});

app.listen(8000, () => console.log('Webhook handler running on port 8000'));

Tip

Verify the X-Chargeflow-Hmac-Sha256 signature on every webhook in production. See Webhooks for the verification code.

Fetch the dispute

Use the Chargeflow dispute ID from data.id to read the full, current dispute record. Check the status, the due_by deadline, and which linked objects (transaction, order, subscription) Chargeflow has already matched.

Terminal
curl -X GET https://api.chargeflow.io/public/2025-04-01/disputes/dis_12345 \
  -H "x-api-key: YOUR_API_KEY"

Example response:

Response
{
  "id": "dis_12345",
  "source": "stripe",
  "source_id": "du_123456789",
  "account_id": "act_112233",
  "created_at": "2025-01-27T09:00:00Z",
  "reason": "fraud",
  "due_by": "2025-02-10T10:00:00Z",
  "amount": 150,
  "currency": "USD",
  "status": "needs_response",
  "stage": "Chargeback",
  "closed_at": null,
  "transaction": "tx_1234567890",
  "subscription": null,
  "order": "ord_1234567890"
}

Respond only while status is needs_response. Once the response is filed with the issuer, the status moves to under_review.

Add evidence

Attach the files that refute the claim. This is a multipart/form-data request. The dispute ID is the path parameter, and each call uploads one file with an evidenceUploadCategory.

For a fraud chargeback, proof of delivery and customer communication are the strongest artifacts.

Terminal
curl -X POST https://api.chargeflow.io/public/2025-04-01/disputes/dis_12345/evidence \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@/path/to/delivery-confirmation.pdf" \
  -F "evidenceUploadCategory=tracking_information"

A successful upload returns the evidence ID:

Response
{
  "requestId": "req_abc123def456",
  "evidenceId": "evd_789xyz"
}

Supported categories are tracking_information, customer_communication, invoice, additional_evidence, and additional_notes. Files can be PNG, JPG, or PDF, up to 5 MB. See Upload Evidence for the full reference.

Add notes

Notes are uploaded through the same endpoint using the additional_notes category. This category requires a content field with your free-form text, alongside the file.

Terminal
curl -X POST https://api.chargeflow.io/public/2025-04-01/disputes/dis_12345/evidence \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@/path/to/account-history.pdf" \
  -F "evidenceUploadCategory=additional_notes" \
  -F "content=Customer has 4 prior undisputed orders to the same address. IP and billing ZIP match the order on file."

Tip

Add evidence as early as possible after dispute.created. Chargeflow folds your uploads into the evidence package before submission, so the sooner they arrive, the more the AI can build around them.

Let Chargeflow submit the response

You do not call a separate submit endpoint, and you never touch the processor's own dispute API. Once a dispute is in needs_response and your evidence is attached, Chargeflow assembles the evidence package and submits the representment to the issuer ahead of the due_by deadline.

Track progress by reading the dispute again. The status moves from needs_response to under_review after submission, and to won or lost once the issuer decides.

Terminal
curl -X GET https://api.chargeflow.io/public/2025-04-01/disputes/dis_12345 \
  -H "x-api-key: YOUR_API_KEY"

Next steps

Was this page helpful?

On this page