---
title: "Refunds and disputes"
url: https://services.dev/docs/money/refunds-and-disputes
captured: [money-refunds.txt]
---
# Refunds and disputes

URL: https://services.dev/docs/money/refunds-and-disputes

> What happens to the money when a job is canceled, sent back, refunded or disputed. Each seller carries its own charge's losses, each line reverses on its own entry, accrued and held pay stand, and the record answers each dispute with the evidence its reason code asks for.

A refund or a dispute answers one question: whose money goes back? Each seller's charge is its own, so each one carries its own losses, and nothing reaches a member's accrued or held pay. Every refund appends an entry that names the one it reverses; nothing is edited.

## Before and after release

| When | What the buyer does | What happens |
|---|---|---|
| Before delivery | cancels | the hold voids; nothing is charged, and no processing is spent |
| After delivery, inside 7 days | sends it back with a note | the job redoes the work; the buyer pays $0 only if the redo is declined, and accrued pay stands either way |
| After the charge is final | asks for a refund | you refund from the owner console, the API or the CLI |
| Any time | disputes with their bank | the dispute opens on the seller's charge, with the record as evidence |

```txt
→ POST /jobs/job_7Hq2/cancel   # before delivery
```

```txt title="200 · job_7Hq2 · canceled · $0.00 · magic values: none"
  money   charge.held $49.00 → charge.voided · the authorization dropped, no processing
  ledger  +$0.00 · AI $0.42 · you −$0.42
```

A refund after the charge is final comes back from your account, the take returns with it, and the processing stays spent:

```txt
→ POST /owner/jobs/job_7Hs5/refund {"reason":"The registry rejected the format after it was final."}   # after the charge is final
```

```txt title="200 · rfd_7Hs5 · job_7Hs5 · $49.00 back to the buyer's card, from your account · magic values: none"
  money   charge.earned → charge.refunded $49.00 · le_7Hs5r appended, reverses le_7Hs5a
  ledger  +$49.00 −$49.00 · processing $1.72 stays spent · AI $0.84 · you −$2.56
  take    returned with the charge
```

## Two sellers

On a two-seller checkout, each seller refunds only its own charge. A refund of your line leaves the member's sale standing, and the member decides about their own:

```txt
→ POST /owner/matters/job_7Hr1/refund {"reason":"Goodwill, after a slow filing."}   # two sellers
```

```txt title="200 · rfd_7Hr1 · job_7Hr1 · $174.00 back, api.lawyer's line · magic values: none"
  money   chg_7Hr1 refunded $174.00 · chg_7Hr2, the attorney's $175.00, is the attorney's sale and stands
  member  mbr_7Hq3 refunds their own fee from the delivery portal, or keeps it
  ledger  +$175.00 · attorney's fee $175.00 (rail member) · processing $10.73 stays spent · AI $2.10 · coverage $4.00 · you −$16.83
```

| Line | On a refund of your charge |
|---|---|
| Your line | reverses, and its take with it, pro rata on a partial refund |
| A member's fee on their own rail | the member's sale; it stands unless they refund it |
| Pay you paid a member | stands: accrued pay is the member's |
| Held share pay | stands: only its own outcome going back reverses it |
| A pass-through | goes back only when the outside system returns it |
| Placement for a filled Task | stands |
| Processing | stays spent |
| AI spent | stays spent |

## Disputes

A chargeback opens on the seller's own charge, with the amount and the network's fee held until it closes. The record assembles the evidence the bank's reason code asks for, and sends it before the due date unless you send or change it first:

```txt
→ GET /owner/disputes/dsp_7Hq3   # 2 Dec 2026: the client's bank opened a dispute on chg_7Hq3
```

```txt title="200 · dsp_7Hq3 · chg_7Hq3 · api.lawyer's charge · $174.00 · needs_response · evidence due 18 Dec 2026 · magic values: none"
  reason    product_not_received
  held      $174.00 and Stripe's dispute fee, taken from your account until it closes
  evidence  from the record, for product_not_received
    delivery      response.pdf · sha256:4f1c…9a07 · delivered 9 Nov 2026, opened 9 Nov 2026
    receipt       rcp_7Hq3 · the USPTO's acceptance of the filing
    consent       ask_7Hq3 · signed by the client, 2 Nov 2026
    attestation   respond · mbr_7Hq3 · stamped and attested 6 Nov 2026
    thread        14 messages · the client's last, 9 Nov: "Got it, thanks."
  sends     by itself on 15 Dec, three days before it is due, unless you send or change it first
  member    mbr_7Hq3's $175.00 is untouched · a dispute on your charge never reaches a member's pay
```

| Reason code | The evidence the record sends |
|---|---|
| `product_not_received` | the delivery, its hash and when the buyer opened it; each outside receipt |
| `fraudulent` | the consent ask and the mandate, signed by the buyer, and the card's CVC and address checks |
| `subscription_canceled` | the cancel record, and the cycle it ran to |
| `credit_not_processed` | the refund entries, each with what it reversed |
| `product_unacceptable` | the send-back window and what it held, and each attestation |

A dispute moves through `warning_needs_response`, `needs_response` and `under_review` to `won` or `lost`. A won one returns the amount; the network's dispute fee stays spent either way:

```txt
→ GET /owner/disputes/dsp_7Hq3   # 9 Jan 2027
```

```txt title="200 · dsp_7Hq3 · won · $174.00 returned to your account · the dispute fee stays spent · magic values: none"
  money   dispute.opened → evidence sent 15 Dec → under_review → dispute.closed, won
```

On an outcome charge, the proof and the committed close are the evidence, and a held share stays held to its window whatever the bank rules:

```txt
→ GET /owner/disputes/dsp_7Ht4   # a seller's bank disputed a won deal's charge
```

```txt title="200 · dsp_7Ht4 · chg_7Ht4 · api.forsale's charge · $9,300.00 · needs_response · evidence due 4 Jan 2027 · magic values: none"
  reason    fraudulent · the seller says they never authorized it
  held      $9,300.00 from the deal's own hold, under job_7Ht4
  evidence  from the record, for fraudulent
    mandate       ask_7Ht4 · Fabrikam's consent to charge up to $10,000.00 at won, signed 3 Nov 2026 · card …0005
    proof         in_…T4 · $62,000.00 paid to Fabrikam on 21 Nov 2026, read from Fabrikam's own Stripe account
    close         committed by mbr_7Hq4 · closed_value $62,000.00 · contract.pdf sha256:9b2e…11c4
    thread        22 messages · Fabrikam's, 20 Nov: "Signed. Thanks for pushing it over the line."
  member    mbr_7Hq4's $6,200.00 stays held to its window, and is theirs at outcome.final whatever the bank rules
```

`Brand.disputes.prevent` refunds a charge under a set amount when an early fraud warning arrives, before a dispute and its fee can open:

```txt
→ GET /owner/disputes/dsp_7Hs6   # lingua: an early fraud warning on a $49.00 charge
```

```txt title="200 · dsp_7Hs6 · chg_7Hs6 · warning_needs_response · prevented · $49.00 refunded before a dispute opened · magic values: none"
  why       Brand.disputes.prevent refunds a warned charge under $100.00 · dispute.prevented
  ledger    +$49.00 −$49.00 · processing $1.72 stays spent · AI $0.84 · no dispute fee
```

A dispute on your charge never reaches a member's pay. A lost dispute on a member's own charge is yours to carry: services.dev recovers it from your account as a `make_whole` entry and never touches the member's transfer ([flow of funds](https://services.dev/docs/money/flow-of-funds.md#the-way-back)):

```txt
→ GET /owner/disputes?status=lost&period=2026
```

```txt title="200 · api.lawyer · 2026 · 4 disputes lost of 2,409 charges · $697.00 and $60.00 in fees, on your account · magic values: none"
  dsp_7Hm2   chg_7Hm2   $174.00   lost · no consent ask on record: ordered before the ask was added
  dsp_7Hn8   chg_7Hn8   $174.00   lost · the client never received the delivery email
  dsp_7Hn9   chg_7Hn9   $175.00   lost · an attorney's own charge · recovered from your account, never the attorney's
  dsp_7Hp1   chg_7Hp1   $174.00   lost · the bank ruled for the cardholder
  members   $0.00 · make_whole $175.00 on dsp_7Hn9 · every attorney's pay stands
```

## Outcome money

Outcome money reverses inside its window when the payment behind it goes back: the share, share pay and the take, while flat pay stands. A partial refund moves each share line pro rata. [Outcomes](https://services.dev/docs/money/outcomes.md#what-reverses) has every line.

## The objects

| Object | Holds |
|---|---|
| `rfd_` refund | the charge, the amount in cents, the reason, the ledger entries it appended and each one they reverse, and its Stripe refund and transfer reversal |
| `dsp_` dispute | the charge, the reason code, the amount held, the evidence and its due date, its status, how it closed, and its Stripe dispute |

| Event | Audience | Says |
|---|---|---|
| `charge.voided` | buyer, owner | a stopped job dropped its hold |
| `charge.refunded` | buyer, owner | money went back after collection, line by line |
| `dispute.prevented` | owner | an early fraud warning was refunded before a dispute opened |
| `dispute.opened` | owner | a bank opened a dispute on a charge; the evidence is ready and its due date set |
| `dispute.closed` | owner | the bank ruled: won returns the amount, lost keeps it out; the fee stays spent either way |

Worked in: [the sales example's money](https://services.dev/docs/examples/sales/money.md#when-it-reverses), each reversal on one deal.

## In each app

| App | What it shows |
|---|---|
| Customer portal | Cancel and send back on each job, each refund on its receipt, line by line |
| Delivery portal | Nothing changes in accrued or held pay; a held share shows its reversal only with its own outcome |
| Owner console | Refunds with their entries, open disputes with their evidence and due dates, and the year's losses, in [the support queue](https://services.dev/docs/operate/support.md) |

## Every form

| Key or call | Written | Does |
|---|---|---|
| `POST /jobs/:id/cancel` | before delivery | voids the hold; the buyer pays $0 |
| `POST /jobs/:id/send_back` | inside 7 days of delivery, with a note | redoes the work; $0 only if the redo is declined |
| `POST /owner/jobs/:id/refund` | `{ usd?, reason }` | refunds your line, in full unless `usd` says less |
| `Brand.disputes.evidence` | `'auto' \| 'review'` | `'auto'` sends the evidence three days before it is due; `'review'` waits for you |
| `Brand.disputes.prevent` | `{ refund_under?: number }` | refunds a warned charge under that amount, as `dispute.prevented` |

Charges run on behalf of each seller on every plan, so a refund or a chargeback lands on the seller of the charge.

---

- Up: [Money](https://services.dev/docs/money.md)
- Previous: [Flow of funds](https://services.dev/docs/money/flow-of-funds.md)
- Next: [Pay](https://services.dev/docs/money/pay.md)
