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 holds the same operations.
An operation or a parameter marked Preview works, but it is not locked yet and may change: what Preview means.
Lists dispute objects, newest first#
GET/v1/disputesrak_dispute_read
Only the disputes of this charge. Do not send it with payment_intent.
Filter on the creation time, a Unix timestamp: created=<t> for an exact match, or created[gt], created[gte], created[lt], created[lte].
Cursor: the id of the first object of the current page, to get the previous page. Do not send it with starting_after.
How many objects to return, from 1 to 100. Default 10.
Only the disputes of this PaymentIntent. Do not send it with charge.
Cursor: the id of the last object of the previous page, to get the next page. Do not send it with ending_before.
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 objects.
curl https://api.fluveo.com/v1/disputes \
-H "Authorization: Bearer sk_test_..."const response = await fetch("https://api.fluveo.com/v1/disputes", {
method: "GET",
headers: {
"Authorization": "Bearer sk_test_...",
},
});
console.log(await response.json());import requests
response = requests.get(
"https://api.fluveo.com/v1/disputes",
headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())<?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);{
"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}rak_dispute_write
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.
The dispute's id. Treat it as opaque: it starts du_.
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.
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.
Evidence for the bank, one key per field (evidence[uncategorized_text]=...). Text fields take plain text; the file fields take a file id (file_...).
26 child fields
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 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 object.
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"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());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
$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);{
"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}rak_dispute_read
The dispute's id. Treat it as opaque: it starts du_.
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 object.
curl https://api.fluveo.com/v1/disputes/du_123 \
-H "Authorization: Bearer sk_test_..."const response = await fetch("https://api.fluveo.com/v1/disputes/du_123", {
method: "GET",
headers: {
"Authorization": "Bearer sk_test_...",
},
});
console.log(await response.json());import requests
response = requests.get(
"https://api.fluveo.com/v1/disputes/du_123",
headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())<?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);{
"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}/closerak_dispute_write
Accepts the dispute: you give up the disputed amount. It takes no parameters.
The dispute's id. Treat it as opaque: it starts du_.
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.
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 object.
curl -X POST https://api.fluveo.com/v1/disputes/du_123/close \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: $(uuidgen)"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());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
$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);{
"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_.
amountintegerbalance_transactionsarraychargestringAn id by default; send
expand[]=chargeto get the full charge object.createdintegercurrencystringevidenceobjectevidence_detailsobjectidstringis_charge_refundablebooleanlivemodebooleanmetadataobjectobjectenumOne of:
dispute.An id by default; send
expand[]=payment_intentto get the full payment_intent object.payment_method_detailsobjectreasonstringstatusenumOne of:
lost,needs_response,prevented,under_review,warning_closed,warning_needs_response,warning_under_review,won.