Disputes API Guide - Flex Documentation

Documentation Index

Fetch the complete documentation index at: /llms.txt

Use this file to discover all available pages before exploring further.

Disputes (chargebacks) occur when a cardholder challenges a charge with their bank. Flex automatically syncs disputes the moment they are filed and exposes a full API for listing, retrieving, submitting evidence, and closing disputes.


Table of Contents


Overview

Cardholder files dispute with their bank
       │
       ▼
charge.dispute.created → Flex syncs dispute
       │
       ▼
Partner receives webhook event: charge.dispute.created
       │
       ├─── Partner calls GET /v1/disputes/:id  (inspect dispute)
       │
       ├─── Partner calls POST /v1/disputes/:id (submit evidence)
       │         └─ submit: false  → save draft, do not submit yet
       │         └─ submit: true   → submit evidence immediately
       │
       └─── Partner calls POST /v1/disputes/:id/close (accept loss)

When a dispute is opened, Flex:

  1. Receives the charge.dispute.created event.
  2. Creates a Dispute record linked to your payment_intent_id and charge_id.
  3. Forwards a charge.dispute.created event to your registered webhook endpoint.

You have until evidence_details.due_by to respond. Submitting evidence with submit: true sends it immediately and increments submission_count. You may re-submit with updated evidence before the deadline. Alternatively, POST /v1/disputes/:id/close concedes the dispute immediately.


Authentication

All requests require a bearer token (API key).

Authorization: Bearer fsk_...

Your API key is scoped to your partner account. Disputes for other partners are never visible regardless of the ID used — they return 404.


The Dispute Object

{
  "dispute": {
    "dispute_id": "fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8",
    "payment_intent_id": "fpi_01jx3q7ab1c8d2e3f4g5h6j7k8",
    "charge_id": "fch_01jx3q6ab1c7d2e3f4g5h6j7k8",
    "amount": 4999,
    "currency": "usd",
    "status": "needs_response",
    "reason": "fraudulent",
    "is_charge_refundable": false,
    "metadata": {},
    "created_at": "2026-02-10T14:30:00Z",
    "test_mode": false,

"evidence_details": {
      "due_by": "2026-03-01T23:59:59Z",
      "has_evidence": false,
      "past_due": false,
      "submission_count": 0
    },

"balance_transactions": [
      {
        "balance_transaction_id": "fbt_01jx3qabc123",
        "amount": -4999,
        "fee": -1500,
        "net": -6499,
        "status": "available",
        "type": "adjustment",
        "created_at": "2026-02-10T15:00:00Z"
      }
    ],

"payment_method_details": {
      "type": "card",
      "card": {
        "brand": "visa",
        "network_reason_code": "10.4"
      }
    }
  }
}

Field Reference

Field Type Description
dispute_id string Flex dispute ID (fdsp_ prefix). Use this in all API calls.
payment_intent_id string Flex payment intent ID (fpi_) linked to this dispute.
charge_id string
null Flex charge ID (fch_) linked to this dispute.
amount integer Disputed amount in cents (e.g. 4999 = $49.99).
currency string Three-letter ISO currency code (e.g. "usd").
status string Current dispute status. See Dispute Status Lifecycle.
reason string Reason code provided by the card network. See Dispute Reason Codes.
is_charge_refundable boolean Whether the underlying charge can be refunded.
metadata object Arbitrary key-value metadata.
created_at string ISO 8601 timestamp when the dispute was created.
test_mode boolean true if this is a test-mode dispute.
evidence_details object
null Evidence submission deadline and status. See below.
balance_transactions array Balance transactions associated with this dispute (withdrawals and reinstatements). Only populated on single-dispute GET.
payment_method_details object
null Card details for the disputed payment. Only populated on single-dispute GET.

evidence_details Fields

Field Type Description
due_by string
null ISO 8601 deadline for submitting evidence. null if no deadline has been set yet.
has_evidence boolean true if any evidence has been saved (draft or submitted).
past_due boolean true if the submission deadline has passed.
submission_count integer Number of times evidence has been submitted.

balance_transactions Fields (per item)

Field Type Description
balance_transaction_id string Flex balance transaction ID (fbt_ prefix).
amount integer Amount in cents. Negative = debit from your balance.
fee integer Dispute fee in cents (charged by the card network).
net integer Net impact on your balance (amount + fee).
status string pending or available.
type string "adjustment" for dispute debit or reinstatement credit.
created_at string
null ISO 8601 timestamp.

payment_method_details.card Fields

Field Type Description
brand string
null Card brand: "visa", "mastercard", "amex", "discover", etc.
network_reason_code string
null Card network reason code (e.g. "10.4" for Visa, "4853" for Mastercard).

Dispute Status Lifecycle

needs_response ──submit evidence──►  under_review  ──►  won
      │                                                    │
      │                                                    │
      └─────────close (accept loss)──►  lost  ◄───────────┘
Status Description Can Submit Evidence Can Close
needs_response Dispute is open and awaiting your response. ✅ ✅
warning_needs_response Inquiry (pre-chargeback) needing response. ✅ ✅
under_review Evidence submitted; issuer reviewing. ❌ ❌
warning_under_review Inquiry under review. ❌ ❌
won Dispute resolved in your favour. ❌ ❌
lost Dispute resolved against you. Funds forfeited. ❌ ❌
warning_closed Inquiry closed without becoming a chargeback. ❌ ❌

Attempting to submit evidence or close a dispute in a non-actionable status returns 422.


Dispute Reason Codes

Value Description
fraudulent Customer claims they did not authorise the charge. Most common.
product_not_received Customer claims they did not receive the product/service.
product_unacceptable Customer claims the product/service was defective or not as described.
credit_not_processed Customer claims a refund they were promised was never processed.
duplicate Customer claims they were charged twice for the same transaction.
subscription_canceled Customer claims they were charged after cancelling a subscription.
unrecognized Customer does not recognise the charge.
bank_cannot_process Bank returned the charge for technical reasons.
debit_not_authorized Customer claims the debit was not authorised.
customer_initiated Customer-initiated dispute.
check_returned Check was returned.
incorrect_account_details Account details were incorrect.
insufficient_funds Insufficient funds.
general General / uncategorised reason.

Endpoints

List Disputes

GET /v1/disputes

Returns disputes for your account in reverse chronological order (newest first).Query Parameters

Parameter Type Description
limit integer Number of disputes to return. Default 20, max 100.
starting_after string Cursor for forward pagination. Pass the last dispute_id from the previous page.
ending_before string Cursor for backward pagination. Pass the first dispute_id from the current page.
charge string Filter by Flex charge ID (fch_...).
payment_intent string Filter by Flex payment intent ID (fpi_...).

Note: balance_transactions and payment_method_details are not populated in list responses. Use GET /v1/disputes/:id to get the full dispute.

Example — list all disputes

GET /v1/disputes?limit=20
Authorization: Bearer fsk_...

Response

{
  "disputes": [
    {
      "dispute_id": "fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8",
      "amount": 4999,
      "currency": "usd",
      "status": "needs_response",
      "reason": "fraudulent",
      "created_at": "2026-02-10T14:30:00Z",
      "evidence_details": {
        "due_by": "2026-03-01T23:59:59Z",
        "has_evidence": false,
        "past_due": false,
        "submission_count": 0
      },
      "balance_transactions": null,
      "payment_method_details": null,
      ...
    }
  ]
}

Example — filter by payment intent

GET /v1/disputes?payment_intent=fpi_01jx3q7ab1c8d2e3f4g5h6j7k8
Authorization: Bearer fsk_...

Example — paginate forward

GET /v1/disputes?limit=10&starting_after=fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8
Authorization: Bearer fsk_...

Get Dispute

GET /v1/disputes/:dispute_id

Retrieves a single dispute by its Flex ID. This is the only endpoint that populates balance_transactions and payment_method_details.Path Parameters

Parameter Description
dispute_id Flex dispute ID (fdsp_...).

Example

GET /v1/disputes/fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8
Authorization: Bearer fsk_...

Response

{
  "dispute": {
    "dispute_id": "fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8",
    "payment_intent_id": "fpi_01jx3q7ab1c8d2e3f4g5h6j7k8",
    "charge_id": "fch_01jx3q6ab1c7d2e3f4g5h6j7k8",
    "amount": 4999,
    "currency": "usd",
    "status": "needs_response",
    "reason": "fraudulent",
    "is_charge_refundable": false,
    "metadata": {},
    "created_at": "2026-02-10T14:30:00Z",
    "test_mode": false,
    "evidence_details": {
      "due_by": "2026-03-01T23:59:59Z",
      "has_evidence": false,
      "past_due": false,
      "submission_count": 0
    },
    "balance_transactions": [
      {
        "balance_transaction_id": "fbt_01jx3qabc123",
        "amount": -4999,
        "fee": -1500,
        "net": -6499,
        "status": "available",
        "type": "adjustment",
        "created_at": "2026-02-10T15:00:00Z"
      }
    ],
    "payment_method_details": {
      "type": "card",
      "card": {
        "brand": "visa",
        "network_reason_code": "10.4"
      }
    }
  }
}

Errors

Status When
404 Dispute not found or belongs to a different partner.

Update Evidence

POST /v1/disputes/:dispute_id

Saves evidence for a dispute. Set submit: true to submit evidence immediately. Set submit: false (or omit it) to save a draft without submitting.The dispute must be in needs_response or warning_needs_response status.Path Parameters

Parameter Description
dispute_id Flex dispute ID (fdsp_...).

Request Body

{
  "dispute": {
    "evidence": { ... },
    "submit": true
  }
}

Evidence Fields

All fields are optional. Include only what is relevant to your dispute reason.Text fields

Field Description
customer_name Full name of the customer as it appears in your records.
customer_email_address Email address of the customer.
customer_purchase_ip IP address the customer used when making the purchase.
billing_address Billing address as entered during checkout.
product_description Description of the product or service. Be specific.
service_date Date the service was provided (YYYY-MM-DD).
shipping_address Address where the product was shipped.
shipping_carrier Carrier name (e.g. "UPS", "FedEx").
shipping_date Date the product was shipped (YYYY-MM-DD).
shipping_tracking_number Tracking number for the shipment.
duplicate_charge_id The Flex charge ID of the original, legitimate charge (for duplicate disputes).
duplicate_charge_explanation Explanation of why the charges are different (for duplicate disputes).
cancellation_policy_disclosure Explanation of how the cancellation policy was disclosed to the customer.
cancellation_rebuttal Rebuttal to the customer’s cancellation claim.
refund_policy_disclosure Explanation of how the refund policy was disclosed.
refund_refusal_explanation Explanation of why a refund was not issued.
access_activity_log Log of customer access to the service (relevant for product_not_received).
uncategorized_text Any additional evidence or context.

File fields — use a Flex File ID (ffile_... from POST /v1/files)Files must be uploaded with purpose dispute_evidence before use. See Uploading Evidence Files.

Field Description
receipt Receipt or order confirmation.
customer_communication Email/chat correspondence with the customer.
customer_signature Signed delivery confirmation or service agreement.
cancellation_policy Copy of your cancellation policy.
refund_policy Copy of your refund policy.
duplicate_charge_documentation Documentation showing the original charge was legitimate.
service_documentation Proof the service was delivered.
shipping_documentation Shipping confirmation or carrier proof of delivery.
uncategorized_file Any additional supporting document.

Example — save a draft (no submission)

POST /v1/disputes/fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8
Authorization: Bearer fsk_...
Content-Type: application/json

{
  "dispute": {
    "evidence": {
      "customer_name": "Jane Doe",
      "customer_email_address": "jane@example.com",
      "product_description": "HSA-eligible blood pressure monitor, shipped 2026-01-15",
      "shipping_tracking_number": "1Z999AA10123456784",
      "shipping_carrier": "UPS",
      "shipping_date": "2026-01-15",
      "receipt": "ffile_01jx3qabc000000000000000001"
    },
    "submit": false
  }
}

Example — submit evidence

POST /v1/disputes/fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8
Authorization: Bearer fsk_...
Content-Type: application/json

{
  "dispute": {
    "evidence": {
      "customer_name": "Jane Doe",
      "customer_email_address": "jane@example.com",
      "product_description": "HSA-eligible blood pressure monitor, shipped 2026-01-15",
      "shipping_tracking_number": "1Z999AA10123456784",
      "shipping_carrier": "UPS",
      "shipping_date": "2026-01-15",
      "receipt": "ffile_01jx3qabc000000000000000001",
      "shipping_documentation": "ffile_01jx3qabc000000000000000002"
    },
    "submit": true
  }
}

ResponseReturns the updated Dispute object with refreshed evidence_details.submission_count.

{
  "dispute": {
    "dispute_id": "fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8",
    "status": "under_review",
    "evidence_details": {
      "due_by": "2026-03-01T23:59:59Z",
      "has_evidence": true,
      "past_due": false,
      "submission_count": 1
    },
    ...
  }
}

Errors

Status When
404 Dispute not found or belongs to a different partner.
422 Dispute is not in needs_response / warning_needs_response status.
404 A referenced file ID (ffile_...) does not exist.
422 A referenced file was not uploaded with dispute_evidence purpose.

Close Dispute

POST /v1/disputes/:dispute_id/close

Accepts the dispute and concedes the funds. Use this when you do not intend to contest the dispute. This action is irreversible.The dispute must be in needs_response or warning_needs_response status.Path Parameters

Request BodyNo body required.Example

POST /v1/disputes/fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8/close
Authorization: Bearer fsk_...

ResponseReturns the updated Dispute object. status will be lost.

{
  "dispute": {
    "dispute_id": "fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8",
    "status": "lost",
    ...
  }
}

Errors

Status When
404 Dispute not found or belongs to a different partner.
422 Dispute is not in needs_response / warning_needs_response status.

Uploading Evidence Files

Before referencing a file in an evidence submission, upload it using the Files API with purpose dispute_evidence.

POST /v1/files
Authorization: Bearer fsk_...
Content-Type: multipart/form-data

file=@receipt.pdf
purpose=dispute_evidence

Response

{
  "file": {
    "file_id": "ffile_01jx3qabc000000000000000001",
    "filename": "receipt.pdf",
    "purpose": "dispute_evidence",
    "size": 48320,
    "created_at": "2026-02-10T14:00:00Z"
  }
}

Use the returned file_id (ffile_...) in evidence file fields. Attempting to use a file with a different purpose (e.g. identity_document) will return a 422 with a clear error message indicating which field is invalid and what purpose was found vs expected.


Webhook Events

Configure a webhook endpoint in your Flex dashboard to receive dispute lifecycle events.

charge.dispute.created

Fired when a dispute is opened against one of your charges.

{
  "event_id": "fevt_01jx3q8kt1b9a7n2c4d5e6f7g8",
  "event_type": "charge.dispute.created",
  "event_dt": 1770000000,
  "object": {
    "dispute_id": "fdsp_01jx3q8kt1b9a7n2c4d5e6f7g8",
    "payment_intent_id": "fpi_01jx3q7ab1c8d2e3f4g5h6j7k8",
    "charge_id": "fch_01jx3q6ab1c7d2e3f4g5h6j7k8",
    "amount": 4999,
    "currency": "usd",
    "status": "needs_response",
    "reason": "fraudulent",
    "evidence_details": {
      "due_by": "2026-03-01T23:59:59Z",
      "has_evidence": false,
      "past_due": false,
      "submission_count": 0
    }
  }
}

Recommended action: Alert your team and begin gathering evidence.

charge.dispute.updated

Fired when a dispute’s status, evidence, or deadline changes (including when you submit evidence via the API).Recommended action: Refresh your local dispute record.

charge.dispute.closed

Fired when a dispute is resolved — either won, lost, or warning_closed.Recommended action: Update your records; if lost, the funds have been debited.

charge.dispute.funds_withdrawn

Fired when funds are withdrawn from your balance to cover the disputed amount. A balance transaction is added to the dispute.

charge.dispute.funds_reinstated

Fired when a previously disputed amount is returned to your balance (dispute won).All webhook payloads include a flex-signature header for verification. See the Webhooks documentation for signature verification details.


Error Reference

All errors follow a standard format:

{
  "code": "validation_error",
  "detail": "Cannot submit evidence for dispute in 'under_review' status"
}

Or for field-level validation errors:

{
  "detail": [
    {
      "loc": ["evidence", "receipt"],
      "msg": "File ffile_01xxx in receipt has purpose identity_document, expected dispute_evidence",
      "type": "value_error"
    }
  ]
}
HTTP Status code When
401 unauthorized Missing or invalid API key.
404 not_found Dispute or file not found (or belongs to another partner).
422 validation_error Invalid status transition or file validation failure.
500 server_error Internal error — contact support with the request ID.

End-to-End Testing Guide

Prerequisites

Step 1 — Trigger a dispute

Create a checkout session and complete payment using an auto-dispute test card:

Card Number Dispute Reason Notes
4000 0000 0000 0259 fraudulent Automatically disputed after charge
4000 0000 0000 2685 product_not_received Automatically disputed after charge

Use 4111 1111 1111 1111 as a valid non-disputing card for comparison.After completing payment:

Step 2 — Inspect the dispute

GET /v1/disputes
Authorization: Bearer fsk_...

Find the new dispute. Then fetch the full object:

GET /v1/disputes/fdsp_...
Authorization: Bearer fsk_...

Verify:

Step 3 — Submit evidence

POST /v1/disputes/fdsp_...
Authorization: Bearer fsk_...
Content-Type: application/json

{
  "dispute": {
    "evidence": {
      "customer_name": "Test Customer",
      "product_description": "HSA-eligible item, delivered",
      "uncategorized_text": "This charge is legitimate."
    },
    "submit": true
  }
}

Verify:

Step 4 — Close a second dispute

Create another charge with the auto-dispute card. Then:

POST /v1/disputes/fdsp_.../close
Authorization: Bearer fsk_...

Verify:

Step 5 — Verify multi-tenant isolation

Using a different partner’s API key, attempt to access the dispute IDs from Steps 2–4. All should return 404.

Step 6 — Test error cases

**Invalid status transition:**Try POST /v1/disputes/fdsp_.../close on a dispute that is already lost or under_review. Expected: 422 with message "Cannot close dispute in '...' status".

**File with wrong purpose:**Upload a file with purpose=identity_document, then reference it in an evidence field. Expected: 422 with message indicating which field has the wrong file purpose.

Non-existent dispute:

GET /v1/disputes/fdsp_doesnotexist

Expected: 404.

Flex Refund API Guide Early Fraud Warnings