---
title: "The payer withdraws its dispute through the manage key (ALIP-0075)"
method: POST
path: "/manage/{job_id}/disputes/{dispute_id}/withdraw"
tags: ["disputes"]
---

# The payer withdraws its dispute through the manage key (ALIP-0075)

`POST /manage/{job_id}/disputes/{dispute_id}/withdraw`

## Request

- Base URL: `https://pact0.com/api/v1`
- URL: `https://pact0.com/api/v1/manage/{job_id}/disputes/{dispute_id}/withdraw`
- Auth: none required

The same withdraw as PATCH /disputes/{dispute_id}
`{"action":"withdraw"}`, as the job's guest actor. The dispute must
be on a claim of the key's job; only its raiser may withdraw it.
A stake hold not yet confirmed is let go; a confirmed one is
forfeited to the treasury (`stake_forfeited`), as for any raiser.
Send no body, `{}`, or `{"action":"withdraw"}`, optionally with
`accept_stake_forfeit`: the manage page sends its read's
`withdraw_forfeits_stake`, so a hold confirmed after that read is
refused (`409 stake_forfeit_not_accepted`, nothing changed) rather
than forfeited.

## Headers

- `X-Pact0-Manage-Key` string, required
- `Idempotency-Key` string

## Request body

- object
  - `action` 'withdraw'
  - `accept_stake_forfeit` boolean: false: refuse (409 stake_forfeit_not_accepted) rather than forfeit a stake hold confirmed since the page last read the job.

## Response `200`

Withdrawn (a second withdraw, or one sent at the same moment, answers the same withdrawal; while its stake capture is in flight it says `stake_forfeit_pending` instead of `stake_forfeited`). An answer with `stake_forfeit_pending` true is not stored under the Idempotency-Key, so a retry answers what has happened since.

- object
  - `success` true, required
  - `data` object, required
    - `id` string
    - `claim_id` string
    - `state` 'withdrawn'
    - `claim_reverted_to` string
    - `stake_minor` integer: Money in **micro-units**. 1 USD = 1,000,000 micro. **WARNING — do not confuse with cents.** Every `*_minor` field in this spec uses this convention, NOT the Stripe-style minor-currency-unit (cents) convention. Misreading the suffix as cents and multiplying by 100 yields a 10,000× misposting. Conversion examples: - `$0.05` (the test-pool fixture price) is `5_000_000 ÷ 100 = 50_000` - `$1.00` (the minimum buyer envelope) is `1_000_000` - `$5.00` (the dispute stake floor) is `5_000_000` - `$50.00` (the dispute stake ceiling) is `50_000_000` - `$5,000.00` (the maximum buyer envelope) is `5_000_000_000` Conversion to Stripe's cents (÷10,000, round down) happens only at the API boundary in the platform's wallet-backend implementations. Clients never invert this. **The `_usd` twin.** On job, claim and balance responses every `<base>_minor` / `<base>_micro` field has a `<base>_usd` sibling: the same amount in US dollars, as a number (`amount_minor: 50000` → `amount_usd: 0.05`). Read the twin; do arithmetic on the integer. Trio v8 §4.2 (2026-05-26): added because a REST-only buyer reading bare `{ type: integer, format: int64 }` schemas could plausibly assume cents. The bounds on POST /escrow/envelopes (`minimum: 1000000, maximum: 5000000000`) cap the worst-case misposting but do not close the confusion vector; surfacing the convention at every field site does.
    - `stake_forfeited` boolean: true once the confirmed stake hold this withdrawal forfeits has been captured to the treasury.
    - `stake_forfeit_pending` boolean: true while the withdrawal forfeits a confirmed stake hold that is not captured yet and can still be (its capture did not finish, or a concurrent withdraw is making it); the stake reconcile retries it hourly. From 108 hours after the authorization (a merchant-initiated hold can lapse before Stripe's seven days) it is true only while Stripe reports the hold still capturable. false once captured, or once Stripe reports the hold released (the stake was not kept), or when that cannot be confirmed.
    - `withdrawn_at` string, date-time

## Other responses

- `400`: `validation_failed` — the body asked for something other than a withdraw; `invalid_json`.
- `401`: `manage_key_invalid` — the manage key is missing, malformed, unknown, or belongs to another job (one answer for all four). `manage_key_revoked` — the job was attached to a pact0 account, which switched the link off; manage it signed in. Every refused key counts against the caller's IP; past the limit the answer is `429 rate_limited`.
- `403`: `not_dispute_raiser` — the worker opened this dispute; only its raiser withdraws it.
- `404`: `dispute_not_found` — no such dispute, a malformed id, or it is not on this job (the same answer for all three).
- `409`: `dispute_not_withdrawable` (already ruled on), `dispute_state_changed` (ruled on at the same moment), `stake_forfeit_not_accepted` (sent `accept_stake_forfeit: false`, and the deposit hold was confirmed since the page read the job; nothing changed).
- `422`: `idempotency_key_conflict` — this Idempotency-Key was used with a different body.
- `429`: Too many requests

## Changes

- **2026-10-20** `e48bb10d300f`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/pact0/apis/pact0-api/changes/manage/:job_id/disputes/:dispute_id/withdraw/post.md)

---

[API](https://skmtc.dev/pact0/apis/pact0-api.md) · [All operations](https://skmtc.dev/pact0/apis/pact0-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pact0/apis/pact0-api/revisions/e48bb10d300f?raw)
