---
title: "Disputes"
description: "4 operations, with parameters and examples."
url: https://docs.fluveo.com/api/disputes/
---

> Index of every page: https://docs.fluveo.com/llms.txt. Integrating Fluveo? Read https://docs.fluveo.com/agents/integrate.md first.

> Too long? This page with code in one language only: https://docs.fluveo.com/api/disputes.curl.md (cURL), https://docs.fluveo.com/api/disputes.node.md (Node.js), https://docs.fluveo.com/api/disputes.python.md (Python), https://docs.fluveo.com/api/disputes.php.md (PHP).

# Disputes

4 operations, with parameters and examples.

A Dispute is a buyer's challenge of a payment with their bank. Read disputes, send evidence and submit it, or close a dispute to accept it. Every dispute id starts with du_, and list limit takes 1 to 100. An update or a close sent again with the same Idempotency-Key and the same body returns the first dispute with Idempotent-Replayed: true. A refused request, such as an invalid evidence field or an unknown dispute, is not kept, so you may fix it and send it again with the same key. To send a document, upload it with POST /v1/files (purpose dispute_evidence), then name its file_ id in an evidence field such as evidence[receipt] or evidence[uncategorized_file] on POST /v1/disputes/{dispute}, with submit=true to send the evidence to the bank. Every evidence field you save is kept and sent, with the same limits: each document a PDF, JPEG or PNG of up to 5 MB, the documents together up to 4.5 MB and fewer than 50 pages, and each text up to its field's length.

Send every request to `https://api.fluveo.com/v1` with your secret key as a Bearer token. This page lists only what Fluveo serves; [the OpenAPI file](https://docs.fluveo.com/openapi.json) holds the same operations.

An operation or a parameter marked **Preview** works, but it is not locked yet and may change: [what Preview means](https://docs.fluveo.com/api.md#preview).

> **For AI agents:**
>
> Use plain HTTP; install no payments SDK. Send `Authorization: Bearer sk_test_...` and a fresh `Idempotency-Key` on every POST; reuse the same key only when you retry that same POST. Send only the documented parameters. An operation or a parameter marked Preview may change: prefer the ones without the mark.

## Lists dispute objects, newest first

`GET /v1/disputes`

Permission: [`rak_dispute_read`](https://docs.fluveo.com/permissions.md#dispute).

**Query parameters**

- `charge` (string, optional): Only the disputes of this charge. Do not send it with payment_intent.
- `created` (object, optional, preview): Filter on the creation time, a Unix timestamp: created=<t> for an exact match, or created[gt], created[gte], created[lt], created[lte].
- `ending_before` (string, optional, preview): Cursor: the id of the first object of the current page, to get the previous page. Do not send it with starting_after.
- `limit` (integer, optional): How many objects to return, from 1 to 100. Default 10.
- `payment_intent` (string, optional, preview): Only the disputes of this PaymentIntent. Do not send it with charge.
- `starting_after` (string, optional, preview): Cursor: the id of the last object of the previous page, to get the next page. Do not send it with ending_before.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Returns a list of [`dispute`](https://docs.fluveo.com/api/disputes.md#object-dispute) objects.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/disputes \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/disputes", {
  method: "GET",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/disputes",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/disputes");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "count": 1,
  "data": [
    {
      "amount": 2000,
      "charge": "ch_123",
      "created": 1767225600,
      "currency": "usd",
      "id": "du_123",
      "livemode": false,
      "metadata": {},
      "object": "dispute",
      "payment_intent": "pi_123",
      "reason": "fraudulent",
      "status": "needs_response"
    }
  ],
  "has_more": false,
  "object": "list",
  "url": "/v1/disputes"
}
```

## Updates a dispute

`POST /v1/disputes/{dispute}`

Permission: [`rak_dispute_write`](https://docs.fluveo.com/permissions.md#dispute).

Evidence text and file ids are saved as you send them; submit=true sends the evidence to the bank, after which it can no longer change.

**Path parameters**

- `dispute` (string, required): The dispute's id. Treat it as opaque: it starts du_.

**Headers**

- `Idempotency-Key` (string, optional): Send a unique key, 1 to 255 characters, on every POST. A retry with the same key and the same body returns the first answer instead of acting twice (with Idempotent-Replayed: true); the same key with another body is refused. Keys are kept 7 days, except on POST /v1/checkout/sessions, POST /v1/checkout/sessions/{session} and POST /v1/webhook_endpoints, where a key holds 24 hours; webhook endpoint keys are also capped at the newest 32 per store and mode. Do not rely on a replay after that window. GET and DELETE ignore it.
- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

**Body parameters**

- `evidence` (object, optional): Evidence for the bank, one key per field (evidence[uncategorized_text]=...). Text fields take plain text; the file fields take a file id (file_...).
  - `access_activity_log` (string, optional)
  - `billing_address` (string, optional)
  - `cancellation_policy` (string, optional)
  - `cancellation_policy_disclosure` (string, optional)
  - `cancellation_rebuttal` (string, optional)
  - `customer_communication` (string, optional)
  - `customer_email_address` (string, optional)
  - `customer_name` (string, optional)
  - `customer_purchase_ip` (string, optional)
  - `customer_signature` (string, optional)
  - `duplicate_charge_documentation` (string, optional)
  - `duplicate_charge_explanation` (string, optional)
  - `product_description` (string, optional)
  - `receipt` (string, optional)
  - `refund_policy` (string, optional)
  - `refund_policy_disclosure` (string, optional)
  - `refund_refusal_explanation` (string, optional)
  - `service_date` (string, optional)
  - `service_documentation` (string, optional)
  - `shipping_address` (string, optional)
  - `shipping_carrier` (string, optional)
  - `shipping_date` (string, optional)
  - `shipping_documentation` (string, optional)
  - `shipping_tracking_number` (string, optional)
  - `uncategorized_file` (string, optional)
  - `uncategorized_text` (string, optional)
- `metadata` (object, optional): Up to 50 key-value pairs of your own, sent as metadata[key]=value: keys of 1 to 40 characters, values up to 500. Keys starting with fluveo_ are reserved. On an update, an empty value removes a key.
- `submit` (boolean, optional): submit defaults to true and sends the evidence to the bank. Send submit=false explicitly to save a draft without submitting.

Use only the documented body parameters. Send a new `Idempotency-Key` with each new request, and the same key again only to retry that request.

Returns a [`dispute`](https://docs.fluveo.com/api/disputes.md#object-dispute) object.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/disputes/du_123 \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  --data-urlencode "evidence[uncategorized_text]=The buyer received the order on 2026-11-02." \
  --data-urlencode "submit=false"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/disputes/du_123", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_test_...",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: new URLSearchParams([["evidence[uncategorized_text]","The buyer received the order on 2026-11-02."],["submit","false"]]),
});
console.log(await response.json());
```

Python:

```python
import uuid

import requests

response = requests.post(
    "https://api.fluveo.com/v1/disputes/du_123",
    headers={"Authorization": "Bearer sk_test_...", "Idempotency-Key": str(uuid.uuid4())},
    data=[("evidence[uncategorized_text]", "The buyer received the order on 2026-11-02."), ("submit", "false")],
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/disputes/du_123");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: " . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => "evidence%5Buncategorized_text%5D=The%20buyer%20received%20the%20order%20on%202026-11-02.&submit=false",
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "amount": 2000,
  "charge": "ch_123",
  "created": 1767225600,
  "currency": "usd",
  "id": "du_123",
  "livemode": false,
  "metadata": {},
  "object": "dispute",
  "payment_intent": "pi_123",
  "reason": "fraudulent",
  "status": "needs_response"
}
```

## Retrieves a dispute

`GET /v1/disputes/{dispute}`

Permission: [`rak_dispute_read`](https://docs.fluveo.com/permissions.md#dispute).

**Path parameters**

- `dispute` (string, required): The dispute's id. Treat it as opaque: it starts du_.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Returns a [`dispute`](https://docs.fluveo.com/api/disputes.md#object-dispute) object.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/disputes/du_123 \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/disputes/du_123", {
  method: "GET",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/disputes/du_123",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/disputes/du_123");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "amount": 2000,
  "charge": "ch_123",
  "created": 1767225600,
  "currency": "usd",
  "id": "du_123",
  "livemode": false,
  "metadata": {},
  "object": "dispute",
  "payment_intent": "pi_123",
  "reason": "fraudulent",
  "status": "needs_response"
}
```

## Closes a dispute

`POST /v1/disputes/{dispute}/close`

Permission: [`rak_dispute_write`](https://docs.fluveo.com/permissions.md#dispute).

Accepts the dispute: you give up the disputed amount. It takes no parameters.

**Path parameters**

- `dispute` (string, required): The dispute's id. Treat it as opaque: it starts du_.

**Headers**

- `Idempotency-Key` (string, optional): Send a unique key, 1 to 255 characters, on every POST. A retry with the same key and the same body returns the first answer instead of acting twice (with Idempotent-Replayed: true); the same key with another body is refused. Keys are kept 7 days, except on POST /v1/checkout/sessions, POST /v1/checkout/sessions/{session} and POST /v1/webhook_endpoints, where a key holds 24 hours; webhook endpoint keys are also capped at the newest 32 per store and mode. Do not rely on a replay after that window. GET and DELETE ignore it.
- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Use only the documented body parameters. Send a new `Idempotency-Key` with each new request, and the same key again only to retry that request.

Returns a [`dispute`](https://docs.fluveo.com/api/disputes.md#object-dispute) object.

**Request**

cURL:

```bash
curl -X POST https://api.fluveo.com/v1/disputes/du_123/close \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/disputes/du_123/close", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_test_...",
    "Idempotency-Key": crypto.randomUUID(),
  },
});
console.log(await response.json());
```

Python:

```python
import uuid

import requests

response = requests.post(
    "https://api.fluveo.com/v1/disputes/du_123/close",
    headers={"Authorization": "Bearer sk_test_...", "Idempotency-Key": str(uuid.uuid4())},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/disputes/du_123/close");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: " . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => "",
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "amount": 2000,
  "charge": "ch_123",
  "created": 1767225600,
  "currency": "usd",
  "id": "du_123",
  "livemode": false,
  "metadata": {},
  "object": "dispute",
  "payment_intent": "pi_123",
  "reason": "fraudulent",
  "status": "needs_response"
}
```

## The dispute object

Ids start with `du_`.

**Attributes**

- `amount` (integer)
- `balance_transactions` (array)
- `charge` (string, expandable): An id by default; send `expand[]=charge` to get the full charge object.
- `created` (integer)
- `currency` (string)
- `enhanced_eligibility_types` (array)
- `evidence` (object)
- `evidence_details` (object)
- `id` (string)
- `is_charge_refundable` (boolean)
- `livemode` (boolean)
- `metadata` (object)
- `network_reason_code` (string, nullable)
- `object` (enum): One of: `dispute`.
- `payment_intent` (string, nullable, expandable): An id by default; send `expand[]=payment_intent` to get the full payment_intent object.
- `payment_method_details` (object)
- `reason` (string)
- `status` (enum): One of: `lost`, `needs_response`, `prevented`, `under_review`, `warning_closed`, `warning_needs_response`, `warning_under_review`, `won`.
