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.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
kind | string | required | One of chargeback, outcome, or marketplace. |
status | string | required | One of open, answered, decided, appealed, or closed. |
held | money | required | The amount held from the seller until the Dispute closes. |
reason | text | required | What the buyer says fell short, in their words. |
answerBy | datetime | When the seller’s answer is due; a missed deadline decides for the buyer. | |
decision | string | One of buyer, seller, or split, with the amounts each side keeps. | |
decidedBy | string | required | issuer (the cardholder’s bank) for a chargeback; worker, uninvolved in the Order, for an outcome or marketplace Dispute. |
fee | money | The card network’s fee on a chargeback, debited with the held amount when it opens. |
Object
{
"$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 Dispute cites many pieces of Evidence: the Evidence each side submits on the Dispute, frozen once submitted.
cites, inversecitedBy. - A Dispute contests one Order: the Order whose delivery or charge the Dispute contests.
contests, inversedisputes.
Verbs
getReturns one Dispute by id.GET /{namespace}/disputes/{id}listReturns Disputes, newest first, one page at a time.GET /{namespace}/disputesacceptAccept concedes an open Dispute without contest, so the buyer keeps the held amount and the Dispute closes.POST /{namespace}/disputes/{dispute}/acceptanswerAnswer submits the seller’s Evidence on an open Dispute before its deadline: the contract, the delivered work, and its verdicts.POST /{namespace}/disputes/{dispute}/answerappealAppeal asks for one review of a decided Dispute by a Worker who took no part in the first decision.POST /{namespace}/disputes/{dispute}/appealdecideDecide rules on an answered Dispute for the buyer, the seller, or a split, and releases or returns the held amount.POST /{namespace}/disputes/{dispute}/decide
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 | required | Dispute.opened. |
at | datetime | required | When it happened. |
seq | integer | Its position in the namespace, one more than the Event before; any seq is a cursor into the history. | |
data | json | required | The Thing as the Action left it. |
data.dispute | Dispute | required | The Dispute as the change left it. |
data.dispute.kind | string | required | One of chargeback, outcome, or marketplace. |
data.dispute.status | string | required | One of open, answered, decided, appealed, or closed. |
data.dispute.held | money | required | The amount held from the seller until the Dispute closes. |
data.dispute.reason | text | required | What the buyer says fell short, in their words. |
data.dispute.answerBy | datetime | When the seller’s answer is due; a missed deadline decides for the buyer. | |
data.dispute.decision | string | One of buyer, seller, or split, with the amounts each side keeps. | |
data.dispute.decidedBy | string | required | issuer (the cardholder’s bank) for a chargeback; worker, uninvolved in the Order, for an outcome or marketplace Dispute. |
data.dispute.fee | money | The card network’s fee on a chargeback, debited with the held amount when it opens. | |
data.transaction | Transaction | required | The disputed charge. |
{
"$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
Answer the latest dispute with evidence.Example
A store disputes a catalog cleanup 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.