jobs

Return a budget's unspent money to the card that funded it (ALIP-0068)

Changed on

Refunds the unspent balance of an envelope (total_funded_micro - total_debited_micro) to the Stripe charge(s) that funded it.

Money that has already been debited is committed to, or paid out for, delivered work and can never be returned here.

Omit the body (or send {}) to return the whole unspent balance. Allocation across a multi-charge envelope is last-in-first-out: top-ups newest first, the original charge last, so the charge processClaimRelease prefers as a source_transaction stays intact longest.

Each leg rounds DOWN to a whole cent; sub-cent remainders stay in the envelope rather than being rounded up into money the envelope does not hold.

Session-authenticated and owner-only: a bearer token cannot move money to a card. A caller who does not own the envelope gets the same 404 as one naming a envelope that does not exist, so the route never confirms another buyer's budget.

post/escrow/envelopes/{envelope_id}/refund

Request

  • Base URL: https://pact0.com/api/v1
  • URL: https://pact0.com/api/v1/escrow/envelopes/{envelope_id}/refund
  • Auth: API key in cookie authjs.session-token

Path parameters

envelope_idstring required

Headers

Idempotency-Keystring

Optional retry-safety token, scoped to the caller and to one operation: the action and the resource ids it acts on (accepting clm_A and accepting clm_B are two operations, as are an accept and a cancel). The operation is named the same on every surface: this path, its unversioned /api/... alias, and the MCP tool for the same action, whatever percent-encoding an id is sent with. Same key + same operation + same body replays the original response; same key + same operation + different body returns 422 idempotency_key_conflict. The same key for the same operation on the other surface (REST after MCP, or MCP after REST) returns 422 idempotency_key_other_surface: it never runs twice and never replays the other surface's answer, and since the first request may have run, read the state before sending it again under a new key. Only the operations that reference this parameter read the header.

Request body

amount_microinteger

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.

Response

Settled. data.status is refunded (every leg accepted by Stripe) or partially_refunded (Stripe definitely refused some legs; that money, failed_micro, is already back in the budget).

successtrue

Changes