Refusals and cures
When the rules say no, the answer names the act, the row it cites and the way to yes, and nothing is charged. The same refusal reaches a buyer, an agent and a person.
A refusal answers one question: what does the caller do instead? Every refusal the rules bring names the act, cites the row and its version, and carries a cure, in next: the route where the act is performed, the waitlist for a region, or the person to hand a link to. A refusal never charges.
The legal example refuses litigation in a shared row, with a route. Ordering it answers the same way on every surface:
npx services.dev litigatelitigate is never performed here: Litigation is retained directly, never ordered here.
cites rules 2026-10 · shared.litigate
next hand https://referrals.example.test/litigation to the person it asks
docs https://api.lawyer/docs/errors/act_refused→ POST /matters {"service":"litigate","max_usd":500,"key":"sue-initech"}{
"type": "BLOCKED",
"category": "blocked",
"code": "act_refused",
"reason": "litigate is never performed here: Litigation is retained directly, never ordered here.",
"param": "service",
"act": "litigate",
"rule": {
"version": "2026-10",
"at": "shared.litigate",
"position": "Litigation is retained directly, never ordered here."
},
"cure": {
"route": "https://referrals.example.test/litigation"
},
"next": {
"do": "hand_to_person",
"url": "https://referrals.example.test/litigation",
"arguments": {},
"tool": null,
→ tools/call order_litigate {"max_usd":500}← isError: true
act_refused: litigate is never performed here: Litigation is retained directly, never ordered here.
next hand https://referrals.example.test/litigation to the person it asks
docs https://api.lawyer/docs/errors/act_refusedThe SDK calls the API and throws its answer as a BlockedError, with the same code and next. Its types name every code an order can answer:
npx services.dev sdk "service.order": "input_invalid" | "policy_invalid" | "key_reused" | "not_found" | "not_live" | "act_refused" | "region_not_ratified" | "brand_unverified" | "period_taken" | "key_missing" | "key_invalid" | "body_invalid" | "version_unknown" | "api_error"
/** Order a job, or get the offer: the price and the exact next call. A Service whose price has every answers a subscription, sbn_…. `POST /jobs` with service: "translation"; refused with ServicesError<'service.order'>. */Nothing was ordered and nothing was charged: the brand's feed has no matter.ordered (the legal brand's word for job.ordered), and the ledger reads $0.
| Code | Who hears it | It carries | The cure in next |
|---|---|---|---|
act_refused | the buyer or their agent, at the order | the act, the row and its position, cure.route | hand_to_person, with the route |
region_not_ratified | the buyer, at the order | the act, the order's region, the ratified regions, the row | waitlist |
brand_unverified | the buyer, at the order | the act, the brand credentials it needs, the row | waitlist |
reserved | a person who attests without the credential | the act, who it is reserved to, the row | a person who holds it attests |
needs_a_person | a key or a tool that tries a person's act | the act | hand_to_person, with the person's app |
needs_the_party | anyone but the party an ask waits on | the ask and the party | hand_to_person, with the party's link |
needs_direction | anyone but the client, after a decline | the step | the client's direct ask |
The order-time refusals run before anything is held, one step at a time, in the order the Service writes its steps: a refused act first, then a reserved act whose region the order doesn't name, which answers input_invalid with the input to send, then a region outside the row's regions, then a stale brand credential. A step the client performs themselves, under posture: 'self', is their own act, so its row's regions and credentials don't bind it. A region outside the row's regions hears the waitlist, and joining it is one call:
→ POST /contract-review/waitlist {"email":"ada@example.com"} # the next the refusal named← 200 {"object":"waitlist","service":"contract-review","status":"live","position":1,"simulated":true}A person's act is theirs, and no key or tool can take it. The member CLI without the member's own session answers:
npx services.dev reviews listOnly a signer, in their own session at http://localhost:8787/attorneys, does this. A key never can.
next hand http://localhost:8787/attorneys to the person it asks
docs https://api.lawyer/docs/errors/needs_a_personAn ask waits on its party, and a member who tries to attest past it hears who to hand the link to:
→ POST /reviews/tsk_7Hq2/attest # as mbr_7Hq2, before the reply← 403 needs_the_party: advise waits on Ada Lovelace to answer. Only they can, through their own link. Hand them the link in next.These refusals are the boundary working. AI delivers by default, people come in where you want or need them, and every caller hears exactly who acts next.
A refusal is the Service keeping to its rules, so it costs the buyer nothing, and the brand keeps nothing. An order refused at the door holds no money. A person's act refused at the gate leaves their Task open, so they can perform it again with the credential or the input it needs. A decline is different: it is a person's act, paid, and Declines and direction has it.
check catches the rest before a Service takes an order: act_unruled, reserved_act_unstaffed, rail_conflict and brand_credential_missing. check's gates lists every one.