> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rinne.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Disputes

> How Rinne tracks provider-initiated challenges against settled payments and reports them through the dispute.created and dispute.status-changed webhooks

A **dispute** is a challenge against a payment your merchant has already received, where a third party asks for those funds to be returned. Rinne ingests the challenge from the payment provider, tracks it through a single lifecycle, and reports every step to your organization through the `dispute.created` and `dispute.status-changed` webhooks.

<Info>
  Rinne is never the claimant. It receives the provider's report and relays it, so a dispute always originates outside Rinne—there is no way for your organization or Rinne to open one.
</Info>

## Where disputes come from

Disputes arrive from the provider that processed the original transaction. Each rail has its own vocabulary for the same idea, and Rinne absorbs that difference at the provider boundary: event names and payload fields describe Rinne's lifecycle, not the provider's. You integrate once, regardless of which rail a dispute came from.

The `type` field names the rail:

| `type`           | Rail                                                         |
| ---------------- | ------------------------------------------------------------ |
| `PIX_INFRACTION` | A PIX MED 2.0 infraction report raised against a PIX payment |
| `CHARGEBACK`     | A chargeback raised by a cardholder through the card network |

<Note>
  Today disputes reach you from the PIX rail (`PIX_INFRACTION`). Card chargebacks will arrive through these same two events, so handling both types now needs no later change.
</Note>

There are no dispute endpoints to call. Disputes are delivered to you exclusively as webhooks.

## Lifecycle

A dispute tracks **progress** and **verdict** on two separate fields: `status` says how far the claim has got, and `outcome` says how it ended.

`status` is an ordered progression and a dispute only ever moves forward. `CLOSED` is the single terminal status, so a finished dispute is exactly `status == "CLOSED"`.

```mermaid theme={null}
stateDiagram-v2
    [*] --> OPEN: Claim received
    OPEN --> UNDER_REVIEW: Analysis starts
    OPEN --> CLOSED: Verdict reached
    UNDER_REVIEW --> CLOSED: Verdict reached
    CLOSED --> [*]
```

| `status`       | Meaning                                                           |
| -------------- | ----------------------------------------------------------------- |
| `OPEN`         | The claim has been received. Nothing has been analyzed yet.       |
| `UNDER_REVIEW` | The claim is under analysis.                                      |
| `CLOSED`       | The claim is finished. Terminal, and always carries an `outcome`. |

`outcome` is `null` for the entire life of the dispute until it closes, and is always set once it has:

| `outcome`   | Meaning                                                                        |
| ----------- | ------------------------------------------------------------------------------ |
| `UPHELD`    | The claim succeeded. Funds are returned.                                       |
| `DISMISSED` | The claim failed. No financial movement.                                       |
| `WITHDRAWN` | The claim ceased to exist before a verdict was reached. No financial movement. |

A partially resolved claim is `UPHELD` with a smaller `amount`, not a separate outcome.

<Warning>
  A dispute never regresses and never reopens. If you receive an event whose `new_status` is not ahead of the status you have stored, treat it as a duplicate and ignore it—`old_status` is always strictly earlier than `new_status`, so a stale or repeated provider delivery produces no event at all.
</Warning>

## Disputes and the disputed transaction

Every dispute is linked to the transaction it challenges through `transaction_id`. Use that field to reconcile a dispute against the payment in your own system.

The dispute lifecycle is tracked separately from the transaction lifecycle: a `dispute.created` or `dispute.status-changed` event reports the claim's progress and is not a transaction status change. You receive dispute events independently of any [`transaction.status-changed`](/guides/webhooks) event for the same payment.

A single transaction can accumulate more than one dispute. Key your own records on `dispute_id`, not on `transaction_id`.

## The dispute webhooks

Both events use the same envelope, Svix signatures, and retry behavior as every other Rinne event—see [Webhooks](/guides/webhooks). In the envelope, `source` is `dispute-service` and `company_id` is the **merchant** the claim is filed against.

Both payloads carry an `entity` field holding the full dispute object as it stood when the event was emitted, so you never need a follow-up request to act on a dispute. The API reference lists every field it contains; two of them are easy to misread:

* `amount` is the amount **at risk**, in cents—an upper bound rather than an exact figure, currently derived from the disputed transaction. Rails that report an amount of their own will carry it here instead.
* `dispute_details` is free text describing the claim, and its author is not recorded—it may be the payer in their own words, a risk analyst at the reporting institution, or a generated message. Display it and read it, but never parse it or branch your logic on its contents.

### `dispute.created`

Emitted when Rinne records a dispute for the first time.

```json theme={null}
{
  "id": "e6925e51-7157-47b7-9d09-ab1b711d70b0",
  "type": "dispute.created",
  "version": 1,
  "timestamp": "2026-08-05T15:16:57.000Z",
  "source": "dispute-service",
  "company_id": "6834d397-ab3c-4862-a148-d6216042e9bc",
  "correlation_id": "e6925e51-7157-47b7-9d09-ab1b711d70b0",
  "payload": {
    "dispute_id": "056213b4-bb9b-45d0-8872-c8a33f050367",
    "transaction_id": "33c83e9a-405e-4648-a6b8-3f241274fff9",
    "organization_id": "29add8ce-c317-4db8-b2b5-2854892cd4d4",
    "type": "PIX_INFRACTION",
    "status": "OPEN",
    "outcome": null,
    "amount": 9500,
    "entity": {
      "id": "056213b4-bb9b-45d0-8872-c8a33f050367",
      "transaction_id": "33c83e9a-405e-4648-a6b8-3f241274fff9",
      "company_id": "6834d397-ab3c-4862-a148-d6216042e9bc",
      "organization_id": "29add8ce-c317-4db8-b2b5-2854892cd4d4",
      "provider": "CELCOIN",
      "type": "PIX_INFRACTION",
      "status": "OPEN",
      "outcome": null,
      "amount": 9500,
      "provider_dispute_id": "9edf80da-0d09-40c9-a79d-3794cbef552c",
      "dispute_details": "the reporting institution describes an unauthorised transfer",
      "deadline_at": null,
      "concluded_at": null,
      "created_at": "2026-08-05T15:16:57.000Z",
      "updated_at": "2026-08-05T15:16:57.000Z"
    }
  }
}
```

A dispute is usually created at `OPEN`. A dispute whose first notification to reach Rinne already describes a finished claim is created at `CLOSED`, with `outcome` already set—so handle a created event that is already closed rather than assuming a status change will follow.

### `dispute.status-changed`

Emitted when the dispute moves forward: `OPEN` to `UNDER_REVIEW`, or either of those to `CLOSED` with a verdict in `outcome`.

```json theme={null}
{
  "id": "04c9ab9c-16b2-41dd-b386-8b8ffdbcc6ca",
  "type": "dispute.status-changed",
  "version": 1,
  "timestamp": "2026-08-09T11:02:14.000Z",
  "source": "dispute-service",
  "company_id": "6834d397-ab3c-4862-a148-d6216042e9bc",
  "correlation_id": "04c9ab9c-16b2-41dd-b386-8b8ffdbcc6ca",
  "payload": {
    "dispute_id": "056213b4-bb9b-45d0-8872-c8a33f050367",
    "transaction_id": "33c83e9a-405e-4648-a6b8-3f241274fff9",
    "old_status": "UNDER_REVIEW",
    "new_status": "CLOSED",
    "outcome": "UPHELD",
    "entity": {
      "id": "056213b4-bb9b-45d0-8872-c8a33f050367",
      "transaction_id": "33c83e9a-405e-4648-a6b8-3f241274fff9",
      "company_id": "6834d397-ab3c-4862-a148-d6216042e9bc",
      "organization_id": "29add8ce-c317-4db8-b2b5-2854892cd4d4",
      "provider": "CELCOIN",
      "type": "PIX_INFRACTION",
      "status": "CLOSED",
      "outcome": "UPHELD",
      "amount": 9500,
      "provider_dispute_id": "9edf80da-0d09-40c9-a79d-3794cbef552c",
      "dispute_details": "the reporting institution describes an unauthorised transfer",
      "deadline_at": null,
      "concluded_at": "2026-08-09T11:02:14.000Z",
      "created_at": "2026-08-05T15:16:57.000Z",
      "updated_at": "2026-08-09T11:02:14.000Z"
    }
  }
}
```

<Tip>
  Group the events belonging to one dispute by `dispute_id`. Unlike some Rinne events, `correlation_id` here is unique per event rather than shared across a dispute's lifetime, so it identifies the delivery, not the claim.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Endpoint setup, Svix signature verification, and retries
  </Card>

  <Card title="Transactions" icon="credit-card" href="/concepts/transactions">
    The payment lifecycle a dispute challenges
  </Card>

  <Card title="Ledger entries" icon="book" href="/concepts/ledger-entries">
    How settled amounts are recorded against a merchant
  </Card>

  <Card title="API reference" icon="code" href="/api-reference/introduction">
    Full dispute event schemas under Webhooks → Events
  </Card>
</CardGroup>
