disputes

Stable. Describes behaviour that is built and covered by tests in this repository.

API reference

GET /v1/disputes #

The cases customers opened with their banks about a payment, newest first

A dispute (a chargeback, or an inquiry that may become one) is kept as the provider last reported it: outcome is open while it is undecided (for a chargeback the provider has taken the money back until it decides), won when it ended in your favour or never became a chargeback, lost when the cardholder won. Each step is also sent as the payment.disputed webhook event. A dispute of a payment Payrudder did not make has no invoice_id. Stripe's and Paddle's own words are in provider_status; UNVERIFIED against a live account. 503 when the payment service does not answer.

Parameters

NameInTypeDescription
invoice_idquerystring · uuidOnly the disputes of this invoice
outcomequeryopen | won | lost
limitqueryinteger

Responses

StatusMeaningBody
200OKListDisputesResponse
400Error envelopeError
503Error envelopeError
Example response
{
  "disputes": [
    {
      "id": "string",
      "provider": "stripe",
      "dispute_id": "string",
      "payment_ref": "string",
      "amount_minor": "string",
      "currency": "string",
      "outcome": "open",
      "formal": false,
      "provider_status": "string",
      "opened_time": "2026-11-03T10:15:00Z",
      "handled": false,
      "note_count": 1
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/disputes \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/disputes/{id}/history #

The notes and marks you made on a dispute, newest first

What you did with the case inside Payrudder: each note, and each time it was marked as handled or the mark was lifted. id is the case's id as GET /v1/disputes gives it. actor (who) is left out for a viewer of the account, as in the audit log. At most the newest 200. 404 payments.dispute.not_found for a case that is not yours.

Parameters

NameInTypeDescription
id requiredpathstring

Responses

StatusMeaningBody
200OKListDisputeEventsResponse
400Error envelopeError
404Error envelopeError
503Error envelopeError
Example response
{
  "events": [
    {
      "id": "string",
      "action": "note",
      "create_time": "2026-11-03T10:15:00Z"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/disputes/<id>/history \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/disputes/{id}/notes #

Write a note on a dispute

A note is for you and your colleagues: what you found out, what you answered at the provider, where the evidence is. It is kept with the case, one to 2000 characters, and recorded in the audit log without its text. Payrudder sends it nowhere: the case is answered at the provider, Payrudder submits no evidence.

Parameters

NameInTypeDescription
id requiredpathstring

Request body

FieldTypeDescription
note requiredstring
Example
{
  "note": "string"
}

Responses

StatusMeaningBody
201CreatedDisputeEvent
400Error envelopeError
404Error envelopeError
503Error envelopeError
Example response
{
  "id": "string",
  "action": "note",
  "create_time": "2026-11-03T10:15:00Z"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/disputes/<id>/notes \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"note":"string"}'

PUT /v1/disputes/{id}/handled #

Mark a dispute as handled in Payrudder, or lift the mark

Your own mark: it says you have dealt with the case (answered it at the provider, accepted it, refunded it). It changes nothing at the provider, in the payment ledger, or in what was announced (payment.disputed). Setting what is already set is not an error and writes nothing. Recorded in the audit log. The answer is the case as it stands.

Parameters

NameInTypeDescription
id requiredpathstring

Request body

FieldTypeDescription
handled requiredboolean
Example
{
  "handled": false
}

Responses

StatusMeaningBody
200OKDispute
400Error envelopeError
404Error envelopeError
503Error envelopeError
Example response
{
  "id": "string",
  "provider": "stripe",
  "dispute_id": "string",
  "payment_ref": "string",
  "amount_minor": "string",
  "currency": "string",
  "outcome": "open",
  "formal": false,
  "provider_status": "string",
  "opened_time": "2026-11-03T10:15:00Z",
  "handled": false,
  "note_count": 1
}
curl
curl -X PUT https://YOUR-API-ADDRESS/v1/disputes/<id>/handled \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"handled":false}'

Models

AddDisputeNoteRequest

FieldTypeDescription
note requiredstring

Dispute

FieldTypeDescription
id requiredstringPayrudder's id of the case
provider requiredstring
dispute_id requiredstringThe provider's reference of the case (Stripe's du_..., Paddle's cb_ or cbw_ and the transaction)
payment_ref requiredstringThe payment that is disputed, as the provider calls it (a Stripe PaymentIntent id, a Paddle transaction id)
invoice_idstring · uuidThe invoice of that payment; absent for a payment Payrudder did not make
amount_minor requiredstring · int64The disputed amount
currency requiredstring
outcome requiredopen | won | lost
formal requiredbooleanA chargeback proper, which takes the money back; false is an inquiry or a warning ahead of one
provider_status requiredstringThe provider's own word (Stripe's needs_response, under_review, won, lost, warning_closed ...)
reasonstringThe cardholder's reason in the provider's words (Stripe's fraudulent, product_not_received ...); absent when the provider does not say
evidence_due_timestring · date-timeWhen your evidence is due; absent when the provider does not say
opened_time requiredstring · date-time
closed_timestring · date-timeAbsent while the case is open
fee_minorstring · int64What the provider took for the case, when a notification said (in `fee_currency`, which is the currency the provider settles in and can differ from `currency`). It is also a row of the payment ledger of its own. Stripe's is read from the balance transactions of the dispute and Paddle's from the chargeback fee of the payout totals; UNVERIFIED against a live account. Absent when no notification named a fee
fee_currencystringThe currency of `fee_minor`
handled requiredbooleanYour own mark that you dealt with the case (`PUT /v1/disputes/{id}/handled`). It changes nothing at the provider
handled_timestring · date-timeAbsent when the case is not marked
handled_bystringWho marked it; absent when it is not marked, for a viewer of the account, and for an API key that holds no audit:read (the read_only and billing roles)
note_count requiredinteger · int64How many notes you wrote (`GET /v1/disputes/{id}/history`)

DisputeEvent

FieldTypeDescription
id requiredstring
action requirednote | handled | unhandledA note, the mark that the case was handled, or that mark lifted
notestringThe text of a note; absent for a mark
actorstringWho: the person's address, or the API key as `api_key:<id>`; absent for a viewer of the account
create_time requiredstring · date-time

Error

FieldTypeDescription
error requiredobject

ListDisputeEventsResponse

FieldTypeDescription
events requiredarray of DisputeEvent

ListDisputesResponse

FieldTypeDescription
disputes requiredarray of Dispute

SetDisputeHandledRequest

FieldTypeDescription
handled requiredboolean