Propose, then the gate commits

The running jobjob_7Hq2 · working · $49.00 held

AI, your code, a person and an outside system all propose. One gate runs seven checks in order and commits or refuses, the same way for everyone.

The gate answers one question: may this output become part of the job?

No step writes to a job directly. Every doer hands the gate a proposal: the AI, your code, a URL, a nested Service, a person and an outside system's answer. The gate is the only code that writes a step's output to the record. On a pass it writes step.committed. On a refusal it writes step.refused with a code, and the doer's work stays open.

#CheckHolds whenOn the recordA person getsExplained in
1terminalThe job is not at an end.job_endedjob_endedThe job lifecycle
2directionNo decline awaits the client's direction.needs_directionneeds_directionDeclines and direction
3graphThe graph allows the step, and any grant it needs is live.step_not_ready, needs_grantnot_waiting, needs_grantThe step graph
4mechanicsEvery policy value within its bounds. A client's decide is never undone here (5b).policy_invalidpolicy_invalidPolicy
5meritsMerits come only from the client, and the guard checks the output against them.merits_not_yours, guard_refusedmerits_not_yours, guard_refusedGuards
6authorityThe rules row, never the sign line, under the job's pinned version. A reserved act commits only from a holder of its credential, in the region it derives and where the row is ratified, with every brand credential it names verified. It carries its attestation, every checklist item affirmed, over the output as it stands now.unattested, reserved, region_not_ratified, brand_unverifiedneeds_a_person, reserved, region_not_ratified, brand_unverifiedRules
7proofAn end that charges has its release fact on the record.release_unprovenend_not_allowedHow a job ends

Any check can refuse, and the first that does decides the answer.

1. Terminal. An end is forever. Ending the job again answers end_not_allowed, and any other act on it answers job_ended:

Call
→ POST /jobs/job_7Hq2/end {"end":"withdrawn"}
409 · magic values: none
  "code": "end_not_allowed",
  "reason": "The job already ended at withdrawn, and an end is forever.",
Call
→ POST /jobs/job_7Hq2/messages {"text":"Can you still change page 2?"}
409 · magic values: none
  "code": "job_ended",
  "reason": "The job has ended, for good.",

2. Direction. A member's decline holds the job until the client directs it, to another member or to an end. While it waits, nothing else moves the job, from your console or the buyer's key:

Call
→ POST /owner/matters/job_7Hq2/approve   # the owner console
magic values: none
← 409 needs_direction: A signer declined, and only the client's direction moves the matter on.

Declines and direction walks the decline, the client's link and the member they name.

3. The graph. A step runs only when every step and ask it follows is on the record. Translation waits for the buyer's glossary before it starts:

Call
→ POST /translation {"document":"lease.pdf","to":"fr-CA","max_usd":49,"key":"glossary"}
magic values: none
← job_7Hq2 · waiting · on party · $49.00 held
  waiting {"on":"party","since_at":"2026-10-25T14:02:11.000Z","step":"glossary","clock":"paused"}

4. Mechanics. Every policy value sits inside its bounds. An order that breaks them hears every violation at once:

Call
→ POST /checked-translation {"document":"manual.pdf","to":"de","policy":{"match":1.5,"turnaround":"fast"},"max_usd":79,"key":"bad"}
422 · magic values: none
  "code": "policy_invalid",
  "reason": "The policy breaks 2 bounds: match must be at most 1; turnaround must be a number.",

5a. Merits. Merits come only from the client, and a step's guard checks the output against them. The brand can't set one, and an output below one is refused, whoever proposed it:

Call
→ POST /owner/jobs/job_7Hq2/policy {"policy":{"match":0.7}}
403 · magic values: none
  "code": "merits_not_yours",
  "reason": "match binds the merits, and only the client sets them.",

5b. A client's committed decision is never undone on mechanics.

6. Authority. The rules row the job pinned decides, never the sign line. Where the row isn't ratified, the order is refused at $0:

Call
→ POST /matters {"service":"contract-review","contract":{"url":"data:text/plain,A%20lease.","name":"lease.pdf"},"client":{"name":"Ada Lovelace","email":"ada@example.com"},"client_region":"US-FL","max_usd":99,"key":"lease-fl"}
403 · magic values: none
  "code": "region_not_ratified",
  "reason": "advise is ratified in US-CA, US-NY, US-TX under rules 2026-10, and this order's region is US-FL.",

So is an act under a brand credential that isn't verified and fresh:

Call
→ POST /jobs {"service":"extension","year":2025,"max_usd":25,"key":"stale","metadata":{"sandbox":{"brand":"stale"}}}
403 · magic values: metadata.sandbox.brand = verified, metadata.sandbox.brand = stale
  "code": "brand_unverified",
  "reason": "file is performed under the brand's efin, which isn't verified and fresh.",

7. Proof. An end that charges has its release fact on the record. With no receipt, the filing that fell back to the buyer charges nothing when they accept it:

Call
→ POST /jobs/job_7Hq2/accept
magic values: metadata.sandbox.outside = never
← job_7Hq2 · accepted · lease.fr-CA.pdf · $0
  ledger  AI $0.84 · you −$0.84

The AI's refused proposal is redone inside its budget. A person's gets 422 guard_refused, and the step stays theirs:

Call
→ POST /proofreaders/jobs/job_7Hq2/approve {"output":{"match":0.8}}
422 · magic values: metadata.sandbox.propose = { match: [0.7, 0.95] }
  "code": "guard_refused",
  "reason": "match: below the match the client set (0.8 < 0.9)",

The gate holds both: a member's decline at check 2, and the client's yes on their merits at 5a and 5b. Two sovereigns explains why neither can be overridden.

OpAPICLIMCPSDKSiteDocsApp
job.approvePOST /:portal/jobs/:id/approve, POST /jobs/:id/approve, POST /jobs/:id/sign, POST /owner/jobs/:id/approvejobs approve <job>never: a person actsnever: a person acts8 error pagesowner console, delivery portal

Events: step.refused, job.paused.

Operations has every column of every row.

CodeCategoryHTTPWhy
job_endedconflict409The job has ended, for good.
needs_directionconflict409A member declined, and only the client's direction moves the job on: another member, or an end.
not_waitingconflict409The job isn't waiting on a person.
needs_grantblocked403A grant this step needs isn't live: not given yet, expired or revoked.
policy_invalidinvalid_request422A policy value is out of bounds; the reason lists each.