# Dispute

URL: /business/nouns/dispute · Id: `noun:dispute`

> A Dispute is a contested payment or delivery, such as a chargeback, a rejected Service outcome, or a buyer’s claim against a Supplier.

A Dispute is a contested payment or delivery, such as a chargeback, a rejected [Service](/business/nouns/service) outcome, or a buyer’s claim against a [Supplier](/business/nouns/supplier).

## Properties
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `string` | Yes | One of `chargeback`, `outcome`, or `marketplace`. |
| `status` | `string` | Yes | One of `open`, `answered`, `decided`, `appealed`, or `closed`. |
| `held` | `money` | Yes | The amount held from the seller until the Dispute closes. |
| `reason` | `text` | Yes | What the buyer says fell short, in their words. |
| `answerBy` | `datetime` | No | When the seller’s answer is due; a missed deadline decides for the buyer. |
| `decision` | `string` | No | One of `buyer`, `seller`, or `split`, with the amounts each side keeps. |
| `decidedBy` | `string` | Yes | `issuer` (the cardholder’s bank) for a `chargeback`; `worker`, uninvolved in the Order, for an `outcome` or `marketplace` Dispute. |
| `fee` | `money` | No | The card network’s fee on a `chargeback`, debited with the held amount when it opens. |

## Object
```json
{
  "$id": "https://api.sb/tutorloop.com/disputes/dispute_1",
  "$type": "Dispute",
  "kind": "chargeback",
  "status": "open",
  "held": {
    "amount": 60,
    "currency": "USD"
  },
  "reason": "Example reason",
  "answerBy": "2026-10-22T12:00:00Z",
  "decision": "buyer",
  "decidedBy": "issuer",
  "fee": {
    "amount": 60,
    "currency": "USD"
  }
}
```

## Relationships
- <a id="rel-cites"></a>A Dispute **cites** many pieces of [Evidence](/business/nouns/evidence): the [Evidence](/business/nouns/evidence) each side submits on the Dispute, frozen once submitted. `cites`, inverse [`citedBy`](/business/nouns/evidence#rel-cited-by).
- <a id="rel-contests"></a>A Dispute **contests** one [Order](/business/nouns/order): the [Order](/business/nouns/order) whose delivery or charge the Dispute contests. `contests`, inverse [`disputes`](/business/nouns/order#rel-disputes).

## Verbs
| Verb | Description | Subject | Event | Class |
| --- | --- | --- | --- | --- |
| [Dispute.accept](/business/nouns/dispute/accept) | Accept concedes an open Dispute without contest, so the buyer keeps the held amount and the Dispute closes. | [Billing Specialist](/agents/finance/billing-specialist) | [`Dispute.accepted`](/business/events/Dispute.accepted) | One-way (money): `send`, `do` |
| [Dispute.answer](/business/nouns/dispute/answer) | Answer submits the seller’s Evidence on an open Dispute before its deadline: the contract, the delivered work, and its verdicts. | [Customer Lead](/agents/customer/customer-lead) | [`Dispute.answered`](/business/events/Dispute.answered) | One-way (money): `send`, `do` |
| [Dispute.appeal](/business/nouns/dispute/appeal) | Appeal asks for one review of a decided Dispute by a Worker who took no part in the first decision. | [Billing Specialist](/agents/finance/billing-specialist) | [`Dispute.appealed`](/business/events/Dispute.appealed) | One-way (money): `send`, `do` |
| [Dispute.decide](/business/nouns/dispute/decide) | Decide rules on an answered Dispute for the buyer, the seller, or a split, and releases or returns the held amount. | [Billing Specialist](/agents/finance/billing-specialist) | [`Dispute.decided`](/business/events/Dispute.decided) | One-way (money, customer): `send`, `do` |
| <a id="get"></a>[Dispute.get](/business/nouns/dispute/get) | Returns one Dispute by id. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="list"></a>[Dispute.list](/business/nouns/dispute/list) | Returns Disputes, newest first, one page at a time. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |

A Dispute is kept forever and never edited: it has no `update` or `delete`.

## Events
Events no Verb emits. Each Verb’s Event is in the Verbs table above; every Event is delivered in the same shape.

### Dispute.opened
A card issuer opened a Dispute against a charge, and the rail holds the disputed amount. Raised by the rail.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | Yes | `Dispute.opened`. |
| `at` | `datetime` | Yes | When it happened. |
| `seq` | `integer` | No | Its position in the namespace, one more than the Event before; any seq is a cursor into the history. |
| `data` | `json` | Yes | The Thing as the Action left it. |
| `data.dispute` | [Dispute](/business/nouns/dispute) | Yes | The Dispute as the change left it. |
| `data.dispute.kind` | `string` | Yes | One of `chargeback`, `outcome`, or `marketplace`. |
| `data.dispute.status` | `string` | Yes | One of `open`, `answered`, `decided`, `appealed`, or `closed`. |
| `data.dispute.held` | `money` | Yes | The amount held from the seller until the Dispute closes. |
| `data.dispute.reason` | `text` | Yes | What the buyer says fell short, in their words. |
| `data.dispute.answerBy` | `datetime` | No | When the seller’s answer is due; a missed deadline decides for the buyer. |
| `data.dispute.decision` | `string` | No | One of `buyer`, `seller`, or `split`, with the amounts each side keeps. |
| `data.dispute.decidedBy` | `string` | Yes | `issuer` (the cardholder’s bank) for a `chargeback`; `worker`, uninvolved in the Order, for an `outcome` or `marketplace` Dispute. |
| `data.dispute.fee` | `money` | No | The card network’s fee on a `chargeback`, debited with the held amount when it opens. |
| `data.transaction` | [Transaction](/business/nouns/transaction) | Yes | The disputed charge. |

```json
{
  "$id": "https://api.sb/tutorloop.com/events/event_1",
  "$type": "Event",
  "name": "Dispute.opened",
  "at": "2026-10-22T12:00:00Z",
  "data": {
    "dispute": {
      "$id": "https://api.sb/tutorloop.com/disputes/dispute_1",
      "$type": "Dispute",
      "kind": "chargeback",
      "status": "open",
      "held": {
        "amount": 60,
        "currency": "USD"
      },
      "reason": "Example reason",
      "answerBy": "2026-10-22T12:00:00Z",
      "decision": "buyer",
      "decidedBy": "issuer",
      "fee": {
        "amount": 60,
        "currency": "USD"
      }
    },
    "transaction": {
      "$id": "https://api.sb/tutorloop.com/transactions/transaction_1",
      "$type": "Transaction",
      "kind": "charge",
      "amount": {
        "amount": 60,
        "currency": "USD"
      }
    }
  },
  "seq": 1
}
```

## Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.

| Operation | `owner` | `admin` | `member` | `viewer` |
| --- | --- | --- | --- | --- |
| `Dispute.get` | Yes | Yes | Yes | Yes |
| `Dispute.list` | Yes | Yes | Yes | Yes |
| `Dispute.accept` | Yes | No | No | No |
| `Dispute.answer` | Yes | No | No | No |
| `Dispute.appeal` | Yes | No | No | No |
| `Dispute.decide` | Yes | No | No | No |

## Stripe
**Dispute** (`dispute`), for a Dispute whose `kind` is `chargeback`. The source of truth is Stripe: db.sb mirrors it, written only from Stripe’s webhooks.

| Property | Stripe field | Source of truth |
| --- | --- | --- |
| `held` | `amount` | Stripe |
| `reason` | `reason` | Stripe |
| `status` | `status` | Stripe |
| `answerBy` | `evidence_details.due_by` | Stripe |
| `fee` | `balance_transactions[].fee` | Stripe |

## Call it

**Ask**

```text
Answer the latest dispute with evidence.
```

**API**

```bash
curl -X POST https://api.sb/kilnbakery.com/disputes/dispute_1/answer \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"evidence":["evidence_1"]}'
```

```json
{
  "api": "https://api.sb/kilnbakery.com",
  "dispute": {
    "$id": "https://api.sb/kilnbakery.com/disputes/dispute_1",
    "$type": "Dispute",
    "kind": "chargeback",
    "status": "answered",
    "held": {
      "amount": 9,
      "currency": "USD"
    },
    "reason": "Example reason"
  },
  "action": {
    "$id": "https://api.sb/kilnbakery.com/actions/action_1",
    "operation": "Dispute.answer",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/kilnbakery.com/actions/action_1"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const dispute = await $.Dispute.answer({ dispute: 'dispute_1', evidence: ['evidence_1'] })
```

**CLI**

```bash
npx startup-builder do Dispute.answer --json '{"dispute":"dispute_1","evidence":["evidence_1"]}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Dispute.answer({ dispute: 'dispute_1', evidence: ['evidence_1'] })"
  }
}
```

## Example
A store disputes a catalog cleanup [Order](/business/nouns/order) because 40 products landed in the wrong category. The Dispute holds the fee, the fix runs, and the store confirms the redelivery. The Dispute closes with no decision, and the fee is earned.