{"openapi":"3.1.0","info":{"title":"api.lawyer","version":"0.3.0","summary":"Legal work as an API: everything the law leaves open runs agent-native, and the reserved acts route to independent licensed professionals who sign under their own credential.","description":"The demand side of legal work for agents. Open work runs agent-native; acts in the Reserved-Act Catalog escalate to an independent licensed Signer via gigs.lawyer. Typed outcomes throughout: OK | EMPTY | BLOCKED (AXP Appendix A.1). Machine surfaces: /.well-known/agents.json (capability card + probe manifest), /llms.txt, /pricing, and this contract."},"servers":[{"url":"https://api.lawyer"}],"paths":{"/matters":{"get":{"operationId":"listMatters","summary":"List Matters — keyless, typed, and branching on its query","description":"Plain GET answers 200 OK with the Matter list. ?filter= or ?tag= narrow the list and answer 200 EMPTY when nothing matches (a truthful empty set, never a bare array faking data). ?scope=admin or ?scope=internal answer 403 BLOCKED — those scopes are reserved to the platform.","parameters":[{"name":"filter","in":"query","required":false,"schema":{"type":"string"},"description":"narrow by offer — a value matching no Matter answers a typed EMPTY"},{"name":"tag","in":"query","required":false,"schema":{"type":"string"},"description":"narrow by matterType — a value matching no Matter answers a typed EMPTY"},{"name":"scope","in":"query","required":false,"schema":{"type":"string","enum":["admin","internal"]},"description":"platform-reserved scopes — always answered 403 BLOCKED for callers"}],"responses":{"200":{"description":"OK (results) or EMPTY (truthful empty set) envelope","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/MattersOk"},{"$ref":"#/components/schemas/Empty"}]}}}},"403":{"description":"BLOCKED — the requested scope is reserved to the platform","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Blocked"}}}}}},"post":{"operationId":"createMatter","summary":"Mint a Matter and its Asset","description":"A ratified matterType is required (400 otherwise) — jurisdiction derives from the Jurisdiction Rules table against the matter facts, never ad hoc.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMatter"}}}},"responses":{"201":{"description":"the minted Matter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatterEnvelope"}}}},"400":{"description":"a ratified matterType is required"},"422":{"description":"malformed Matter body or matter facts"}}}},"/matters/{id}":{"get":{"operationId":"getMatter","summary":"The Matter, its Asset, and the append-only journal","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the Matter with Asset and journal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatterEnvelope"}}}},"404":{"description":"no such Matter"}}}},"/matters/{id}/advance":{"post":{"operationId":"advanceMatter","summary":"One propose → gate → commit turn — or the reserved-act escalation","description":"If the next act is in the Reserved-Act Catalog the service escalates instead: it posts a Gig to gigs.lawyer and the Matter waits on a Signer.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"merits":{"type":"object"},"note":{"type":"string"}}}}}},"responses":{"200":{"description":"committed (the journal advanced) or escalated (waiting on a Signer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatterEnvelope"}}}},"404":{"description":"no such Matter"},"409":{"description":"waiting_on_signer, terminal, or stopped by a Declination"},"422":{"description":"the Gate refuses the Proposal"},"502":{"description":"gigs.lawyer refused the Gig post"}}}},"/matters/{id}/consent":{"post":{"operationId":"recordConsent","summary":"Journal the Client's Consent — executed by the actual Client","description":"A B2A2D Matter cannot advance past intake until the actual Client's informed consent is journaled. actor must be 'client' — a Tenant's agent cannot consent on the Client's behalf (422).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actor","scope"],"properties":{"actor":{"type":"string"},"scope":{"type":"string"}}}}}},"responses":{"200":{"description":"Consent journaled; consentSeq cites the journal position"},"404":{"description":"no such Matter"},"409":{"description":"terminal Matter — the journal is closed"},"422":{"description":"actor is not the Client, or the scope is missing"}}}},"/matters/{id}/gig-result":{"post":{"operationId":"reportGigResult","summary":"The supply network reports a completed Review","description":"The gigs.lawyer callback: outcome 'attested' (with the Signer's Attestation), 'send_back' (completed paid review, defects noted), or 'declined' (the paid, final Declination with its memo). The Gate checks the Attestation and can never author it.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["gigId","outcome"],"properties":{"gigId":{"type":"string"},"outcome":{"type":"string","enum":["attested","send_back","declined"]},"attestation":{"$ref":"#/components/schemas/Attestation"},"memo":{"type":"string"}}}}}},"responses":{"200":{"description":"the Matter after the result lands"},"404":{"description":"no such Matter"},"409":{"description":"no pending Gig, or a Gig id mismatch"},"422":{"description":"a required Attestation or memo is missing"}}}},"/matters/{id}/client-direction":{"post":{"operationId":"recordClientDirection","summary":"The Client's instruction, journaled with provenance","description":"The only key that reopens a Matter a Signer's Declination has stopped — the Client's yes is sovereign, and it is journaled, never assumed.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["instruction"],"properties":{"instruction":{"type":"string"},"authorizes":{"type":"object"}}}}}},"responses":{"200":{"description":"direction journaled; directionSeq cites the journal position"},"404":{"description":"no such Matter"},"409":{"description":"terminal Matter"},"422":{"description":"the instruction is missing"}}}},"/matters/{id}/settlement":{"get":{"operationId":"getSettlement","summary":"The two-rail Settlement — releases only on fulfilled","description":"No filing, no charge: the Meter releases only on the 'fulfilled' terminal (the receipt on file). Any other stage answers 409.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the Settlement"},"404":{"description":"no such Matter"},"409":{"description":"the Matter is not fulfilled — nothing releases"}}}},"/matters/{id}/journal":{"get":{"operationId":"getJournal","summary":"The append-only Event journal — Stage derives from it","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"typed OK with the events; appendOnly: true is a statement, not a flag"},"404":{"description":"no such Matter"}}}},"/matters/{id}/attestations":{"get":{"operationId":"listAttestations","summary":"Every Attestation on the journal — a derived view","description":"Reserved stage acts and utterance-class acts carry the Signer’s frozen, signed Attestation. Answers a typed EMPTY until one lands.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK (attestation rows) or EMPTY","content":{"application/json":{"schema":{"oneOf":[{"type":"object"},{"$ref":"#/components/schemas/Empty"}]}}}},"404":{"description":"no such Matter"}}}},"/matters/{id}/terminal":{"post":{"operationId":"recordTerminal","summary":"One of the three exit terminals — forever","description":"withdrawn (Client-terminated; byClient: true required) | lapsed (confirmedUnresponsiveTwice: true required) | referred_out (referredTo required). 'fulfilled' is never reachable here — it commits only off a filed receipt via the lifecycle. Terminal is forever (409 on a terminal Matter).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["outcome"],"properties":{"outcome":{"type":"string","enum":["withdrawn","lapsed","referred_out"]},"byClient":{"type":"boolean"},"confirmedUnresponsiveTwice":{"type":"boolean"},"referredTo":{"type":"string"},"note":{"type":"string"}}}}}},"responses":{"200":{"description":"the terminal journaled"},"404":{"description":"no such Matter"},"409":{"description":"already terminal, or waiting on a Signer"},"422":{"description":"the outcome’s required predicate is missing"}}}},"/matters/{id}/documents":{"get":{"operationId":"listDocuments","summary":"Matter document metadata (OK | EMPTY)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK (document records) or a typed EMPTY"},"404":{"description":"no such Matter"}}},"post":{"operationId":"recordDocument","summary":"Record document metadata — blob storage stubbed behind the contract","description":"Metadata only: { name, kind, sha256 }. The sha256 (64 hex) is the caller-held content hash; no blob door exists on this surface, and none is declared (presence-when-true).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","kind","sha256"],"properties":{"name":{"type":"string"},"kind":{"type":"string"},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"}}}}}},"responses":{"201":{"description":"the document record"},"404":{"description":"no such Matter"},"409":{"description":"the Matter is terminal — its Client File is closed on this surface"},"422":{"description":"name, kind, or a well-formed sha256 is missing"}}}},"/orders":{"post":{"operationId":"placeOrder","summary":"The Mint's one-shot: mint a Matter and advance until it waits or terminates","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mint","offer","title","inventor","listFeeUsd"],"properties":{"mint":{"type":"string"},"offer":{"type":"string"},"title":{"type":"string"},"inventor":{"type":"string"},"listFeeUsd":{"type":"number"},"clientState":{"type":"string"}}}}}},"responses":{"201":{"description":"matterId + the status the Matter rests at"},"422":{"description":"the order body is malformed"},"502":{"description":"gigs.lawyer refused the Gig post"}}}},"/deadlines/{assetId}":{"get":{"operationId":"getDeadlineLadder","summary":"The Asset's Deadline Ladder — vacancy, alarm, occurrence history","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the ladder view"},"404":{"description":"no ladder for that Asset"}}}},"/deadlines/{assetId}/ack":{"post":{"operationId":"ackDeadline","summary":"The daily human acknowledgment inside the final week","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"acknowledged at the current rung"},"404":{"description":"no ladder for that Asset"},"409":{"description":"the ladder refuses (resolved, or no ack due)"},"422":{"description":"a named actor is required"}}}},"/deadlines/{assetId}/tick":{"post":{"operationId":"tickDeadline","summary":"Advance the deterministic clock N days","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"daysUntilDeadline, alarm state, fired events"},"404":{"description":"no ladder for that Asset"},"409":{"description":"the ladder refuses the tick"}}}},"/deadlines/{assetId}/monitor":{"post":{"operationId":"assignMonitoring","summary":"Name who holds the Monitoring Engagement — dead air has an owner","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"monitoring assignment; vacancy cleared"},"404":{"description":"no ladder for that Asset"},"422":{"description":"a signerId is required"}}}},"/deadlines/{assetId}/satisfy":{"post":{"operationId":"satisfyDeadline","summary":"Close the deadline by evidence — never by assertion","description":"One-shot ladders resolve forever; a recurring obligation resolves the current occurrence and rolls the next one forward. 422 without evidence; 409 after closure.","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the ladder view after the evidence lands"},"404":{"description":"no ladder for that Asset"},"409":{"description":"already closed"},"422":{"description":"an evidenceRef is required"}}}},"/deadlines/{assetId}/resolve":{"post":{"operationId":"resolveTerminalRung","summary":"The enumerated terminal-rung outcome at t-0","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the resolution"},"404":{"description":"no ladder for that Asset"},"409":{"description":"not at the terminal rung"},"422":{"description":"the outcome is off the enum"}}}},"/events":{"get":{"operationId":"getEventTail","summary":"The per-grain event tail — timeline, reminders, named-actor residue (OK | EMPTY)","description":"Append-only, derived from the grain journal by replay. Three feeds over one tail: timeline (everything), reminders (the delivery wire — reminder.* events fired by Deadline Ladder rungs, each addressed to the Monitoring owner or carrying the vacancy alarm), time (events carrying a named actor). Cursor with ?after=.","parameters":[{"name":"after","in":"query","required":false,"schema":{"type":"integer"},"description":"return events with seq greater than this"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"at most this many events (default 100, max 500)"},{"name":"feed","in":"query","required":false,"schema":{"type":"string","enum":["timeline","reminders","time"]}}],"responses":{"200":{"description":"OK (events, latestSeq) or a typed EMPTY"},"422":{"description":"the feed is off the enum"}}}},"/events/stream":{"get":{"operationId":"streamEventTail","summary":"The live wire — the same tail as Server-Sent Events (backlog, then live)","description":"With Accept: text/event-stream (what EventSource sends): each frame is named 'grain-event' with the seq as its SSE id, so Last-Event-ID resumes the tail where a dropped connection left it; keep-alive pings every 15s. Without that Accept header the door answers a typed JSON descriptor naming the subscription contract — never a hung request. A reminder reaching a subscriber on this wire IS the delivery the Deadline Ladder promises.","parameters":[{"name":"after","in":"query","required":false,"schema":{"type":"integer"}},{"name":"feed","in":"query","required":false,"schema":{"type":"string","enum":["timeline","reminders","time"]}}],"responses":{"200":{"description":"the SSE stream (text/event-stream)"},"422":{"description":"the feed is off the enum"}}}},"/assets":{"get":{"operationId":"listAssets","summary":"The Asset register (OK | EMPTY)","description":"Assets are the perpetual nouns: insert-only status history, the continuous docket, the lineage of Matters they accumulate.","responses":{"200":{"description":"OK (assets) or a typed EMPTY"}}}},"/assets/{id}":{"get":{"operationId":"getAsset","summary":"One Asset: status history, docket, lineage, external id","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the Asset"},"404":{"description":"no such Asset"}}}},"/conflicts":{"get":{"operationId":"conflictsIndex","summary":"Typed BLOCKED — Conflicts Ledgers are per-practitioner, never pooled","responses":{"403":{"description":"BLOCKED, naming the per-practitioner address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Blocked"}}}}}}},"/conflicts/{practitionerId}":{"get":{"operationId":"getConflictsLedger","summary":"One practitioner's Conflicts Ledger — hashes only, append-forever","description":"The platform stores sha256 hashes of party names; the practitioner holds the plaintext. On the top-level surface this door rides a verified practitioner credential (id.org.ai, fresh at act-class) and answers a typed 403 BLOCKED naming the upgrade path until that verification is live; the sandbox grain (/workspaces/{id}/conflicts/…) carries the same contract open on synthetic state.","parameters":[{"name":"practitionerId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK (entries) or a typed EMPTY"},"403":{"description":"BLOCKED with the tier upgrade path named (top-level surface today)"}}}},"/conflicts/{practitionerId}/entries":{"post":{"operationId":"appendConflictsEntry","summary":"Append one hashed party name — there is no update and no delete door","parameters":[{"name":"practitionerId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nameHash"],"properties":{"nameHash":{"type":"string","pattern":"^[0-9a-f]{64}$"}}}}}},"responses":{"201":{"description":"the appended entry"},"403":{"description":"BLOCKED with the tier upgrade path named (top-level surface today)"},"422":{"description":"nameHash must be sha256 hex — the platform never holds plaintext"}}}},"/workspaces":{"post":{"operationId":"provisionWorkspace","summary":"Anonymous self-serve workspace — keyless, synthetic, TTL-bound","description":"The no-ask-zone applied to tenancy: a keyless POST provisions an ephemeral sandbox workspace with synthetic seed state; the FULL practice contract (every /matters, /deadlines, /assets, /documents, /conflicts door above) mounts under /workspaces/{id} against that state, and every envelope carries provenance { environment: \"sandbox\", simulated: true }. Anonymous workspaces expire on a TTL; an id.org.ai sign-in binds the workspace to your principal instead. Creation is bounded per caller (429, typed).","responses":{"201":{"description":"the workspace, its TTL, and the graduation path"},"429":{"description":"BLOCKED — per-caller creation bound"}}}},"/workspaces/{id}":{"get":{"operationId":"getWorkspace","summary":"Workspace metadata: tier, TTL, counts, graduation path","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the workspace"},"404":{"description":"unknown or expired — a typed EMPTY naming the fix (POST /workspaces)"}}}},"/workspaces/{id}/matters":{"get":{"operationId":"listWorkspaceMatters","summary":"The workspace’s typed Matter list — the same /matters contract, sandbox state","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK | EMPTY envelope, provenance-stamped"},"403":{"description":"BLOCKED on the reserved scopes, exactly as at the top level"},"404":{"description":"unknown or expired workspace"}}},"post":{"operationId":"createWorkspaceMatter","summary":"Mint a Matter inside the workspace — same contract, no consequences","description":"Escalations to gigs.lawyer never leave the building: a deterministic in-process counterparty answers the Gig post, and the caller plays the Signer via the workspace’s own /matters/{id}/gig-result door. Every practice door above mounts identically under this prefix.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the minted Matter, provenance-stamped"},"404":{"description":"unknown or expired workspace"}}}},"/workspaces/{id}/fixtures":{"get":{"operationId":"listWorkspaceFixtures","summary":"The MAGIC FIXTURES — named deterministic identities with promised outcomes","description":"Well-known synthetic values every sandbox workspace honors (the Stripe-4242 pattern): a Signer that always verifies eligible, one that always fails standing, an adverse party that always produces a conflict hit, an Asset title prefix whose Deadline Ladder always sits inside the final week, a Client whose Matter always takes the Declination path, and one that walks clean to fulfilled. Each promise is contract-tested; script against these instead of poking at seed state.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the fixtures listing — name, magic value, promised outcome"},"404":{"description":"unknown or expired workspace"}}}},"/tenants":{"post":{"operationId":"provisionTenant","summary":"Anonymous self-serve Tenant — the integrator grain, keyless","description":"The Tenant is the demand-side integrator (a Mint or B2A2D embedder) — never a law firm, never the Client, and never the practice-management Customer (that grain is /workspaces). Synthetic state, TTL, per-caller creation bounds; declare Offers and routing mechanics, then mint Matters against them.","responses":{"201":{"description":"the tenant, its TTL, and the graduation path"},"429":{"description":"BLOCKED — per-caller creation bound"}}}},"/tenants/{id}":{"get":{"operationId":"getTenant","summary":"Tenant metadata: Offers, routing, tier, TTL","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the tenant"},"404":{"description":"unknown or expired — a typed EMPTY naming the fix (POST /tenants)"}}}},"/tenants/{id}/offers":{"put":{"operationId":"setTenantOffers","summary":"Declare the Tenant’s Offers — flat-priced, meter-released","description":"Each Offer is { id, title, priceUsd, matterType?, proofPredicate? }. The schema cannot express a percent-of-outcome price: a percentage member is refused (422) before it is a policy question.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the declared Offers"},"404":{"description":"unknown or expired tenant"},"422":{"description":"malformed Offers, or a percentage member"}}}},"/tenants/{id}/routing":{"put":{"operationId":"setTenantRouting","summary":"Tenant Guardrails — platform mechanics only","description":"Accepts { maxFeeUsd }. A merits-bearing field (settlement, disclosure) is structurally not expressible from the Tenant seat and answers 422 — mirroring the kernel’s provenance-partitioned predicates.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the routing mechanics"},"404":{"description":"unknown or expired tenant"},"422":{"description":"a merits-bearing field, or a malformed cap"}}}},"/tenants/{id}/matters":{"get":{"operationId":"listTenantMatters","summary":"The tenant’s minted Matters (OK | EMPTY)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK or EMPTY"},"404":{"description":"unknown or expired tenant"}}},"post":{"operationId":"createTenantMatter","summary":"Mint a Matter against one of the tenant’s declared Offers","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the minted Matter"},"404":{"description":"unknown or expired tenant"},"422":{"description":"no such Offer declared, or a malformed body"}}}},"/firms":{"post":{"operationId":"provisionFirm","summary":"Anonymous self-serve firm grain — the envelope, keyless (sandbox twin)","description":"The Firm is an ENVELOPE, never a marketplace actor (ADR 0014): it holds member-executed Enrollment grants, never accounts — no door on this grain signs, claims, attests, or scores. Synthetic state, TTL, per-caller creation bounds; the bench is the named fixture members (GET /firms/{id}/fixtures) and the caller plays the member.","responses":{"201":{"description":"the firm grain, its TTL, and the graduation path"},"429":{"description":"BLOCKED — per-caller creation bound"}}}},"/firms/{id}":{"get":{"operationId":"getFirm","summary":"Firm grain metadata: enrollment counts, tier, TTL","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the firm grain"},"404":{"description":"unknown or expired — a typed EMPTY naming the fix (POST /firms)"}}}},"/firms/{id}/enrollments":{"get":{"operationId":"listFirmEnrollments","summary":"The grant ledger — current Enrollment standing per member (OK | EMPTY)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK or EMPTY"},"404":{"description":"unknown or expired firm"}}},"post":{"operationId":"executeEnrollment","summary":"Member-executed Enrollment — the firm can invite; only the member can enroll","description":"Body: { memberId, grants? } where memberId names a fixture member and grants enumerates the derived projections the member permits (this grain serves [\"roster\", \"remittance\", \"routing\"]). Append-only lifecycle enrolled → amended → revoked; in the sandbox the caller plays the member, exactly as it plays the Signer on the workspace grain. Every grant records its CEREMONY — a journaled instrument naming what was granted and who executed it, graduating to a member-executed e-sign artifact when the envelope family (G9) lands.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the executed grant"},"404":{"description":"unknown or expired firm"},"422":{"description":"unknown member, or an unknown grant"}}}},"/firms/{id}/enrollments/{memberId}/revoke":{"post":{"operationId":"revokeEnrollment","summary":"Revocation — the member’s unilateral act","description":"Blanks the member’s row from the next roster read: derivation, not deletion. A fresh Enrollment is the member’s own new act.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"memberId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the revocation record"},"404":{"description":"unknown firm, or a member with no Enrollment to revoke"},"409":{"description":"already revoked — a fresh Enrollment is a new member act"},"422":{"description":"unknown member"}}}},"/firms/{id}/roster":{"get":{"operationId":"getFirmRoster","summary":"The roster — a lattice-derived projection, computed per read","description":"Each enrolled member’s row derives from Enrollment status plus the eligibility lattice’s binary facts — credential elements live, standing fresh, coverage in force — via the kernel’s own lattice() walk against a DECLARED synthetic probe act (echoed on the envelope). No Register is consulted (ADR 0010 keeps Register visibility routing-internal), nothing is scored, and a revoked Enrollment blanks the row.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK (derived rows + the probe) or EMPTY (no enrolled members, fix named)"},"404":{"description":"unknown or expired firm"}}}},"/firms/{id}/fixtures":{"get":{"operationId":"listFirmFixtures","summary":"The fixture members — the synthetic bench, promises contract-tested","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the fixture-member listing — name, memberId, promised outcome"},"404":{"description":"unknown or expired firm"}}}},"/firms/{id}/routing":{"get":{"operationId":"getFirmRouting","summary":"Declared routing preferences (OK | EMPTY)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK or EMPTY"},"404":{"description":"unknown or expired firm"}}},"put":{"operationId":"putFirmRouting","summary":"Firm routing preferences — mechanics only, member-countersigned","description":"Body: { participationWindows?, memberOptIns?, remittanceDesignation? }. A merits-bearing key (settlement, disclosure) is a 422 type error before it is a policy question, exactly as Tenant Guardrails are validated; a claim/substitution/Reservation key is refused the same way — a firm preference can never claim a Gig, never select a substitute mid-Gig, never touch Reservation. Every memberOptIns entry must be countersigned inside that member’s own live Enrollment (the 'routing' grant). remittanceDesignation accepts only 'individual': the Merchant-of-Record designation on the State Rail is an open counsel item (ADR 0014), answered as a typed BLOCKED.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the declared preferences"},"403":{"description":"BLOCKED — remittanceDesignation 'firm' names the open counsel item"},"404":{"description":"unknown or expired firm"},"422":{"description":"merits-bearing or actor-shaped key, malformed mechanics, or an uncountersigned opt-in"}}}},"/firms/{id}/remittances":{"get":{"operationId":"getFirmRemittances","summary":"The consolidated Remittance rollup — a view that aggregates; money stays individual","description":"Aggregates member Remittance lines for enrolled members whose Enrollment carries the 'remittance' grant — Rule 5.4’s one native exemption (fee sharing among lawyers in the same firm) is what makes intra-firm consolidation lawful. The Merchant-of-Record on every line stays the platform (Patent Rail) or the individual Signer (State Rail), never the firm: the designation is an open counsel item (ADR 0014) and this view never invoices.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK (per-member lines + totals) or EMPTY (no member granted the projection, fix named)"},"404":{"description":"unknown or expired firm"}}}},"/firms/{id}/conflicts":{"get":{"operationId":"getFirmConflicts","summary":"The envelope’s own conflicts posture ledger (OK | EMPTY)","description":"One envelope, one imputed conflicts surface: append-forever, sha256 hashes at rest (the firm holds the plaintext), never pooled across firms. There is no update door and no delete door — append-only is the absence of those doors.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK or EMPTY"},"404":{"description":"unknown or expired firm"}}}},"/firms/{id}/conflicts/entries":{"post":{"operationId":"appendFirmConflictsEntry","summary":"Append one hashed party name to the envelope ledger","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the appended entry"},"404":{"description":"unknown or expired firm"},"422":{"description":"nameHash must be 64-char sha256 hex — hashes only, never plaintext"}}}},"/firms/{id}/conflicts/prescreen":{"post":{"operationId":"firmConflictsPrescreen","summary":"The Clearance pre-screen — a Conflicts Sheet’s party hashes against the envelope ledger","description":"The firm-grain pre-screen ahead of the member-grain Clearance (ADR 0014 §2): body { nameHashes: [sha256 hex, …] } answers hit-or-clear against this envelope’s posture ledger. An earlier screen for enrolled members’ claims — never a substitute for the Signer’s own full-book check with Rule 1.10 imputation, which still fires on every claim.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the pre-screen verdict — { hit, matched, checked, ledgerEntries }"},"404":{"description":"unknown or expired firm"},"422":{"description":"nameHashes must be a non-empty array of sha256 hex"}}}},"/catalog":{"get":{"operationId":"getCatalog","summary":"The data-product catalog — empty-but-real until a SKU is live","description":"Lists the data SKUs sold on this surface. Zero are listed today, and the envelope says so as a typed EMPTY carrying the declaration contract a SKU must satisfy: name, what-you-get, posted flat price, gate kind, fee line, Merchant of Record, meter grain, and the data ProofPredicate (\"no record, no charge\" — the served record releases the meter; an empty result inside a stated scope is a record; a not-found, an unreachable upstream, and a retried failed call never meter). A SKU appears only when it is live, priced, and metered — never pre-announced.","responses":{"200":{"description":"EMPTY (the truthful zero-SKU catalog) or OK (listed SKUs)"}}}},"/catalog/{sku}":{"get":{"operationId":"getCatalogSku","summary":"One data SKU — typed EMPTY until it exists","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the SKU row"},"404":{"description":"a typed EMPTY — the catalog lists zero SKUs today"}}}},"/icp.json":{"get":{"operationId":"getIcp","summary":"Self-classification: agent_classes + the access-tier ladder","responses":{"200":{"description":"the ICP document"}}}},"/verify":{"get":{"operationId":"getVerifyPage","summary":"Run our tests — the published-verification page (three faces)","responses":{"200":{"description":"HTML, JSON, or markdown face per the conneg law"}}}},"/verify/suite.json":{"get":{"operationId":"getVerifySuite","summary":"The digest-pinned public-contract suite (api.qa/suite@1)","description":"Declarative GET rows over the live doors, runnable by anyone: npx autonomous-qa verify https://api.lawyer, the @law/api `verify` export, or plain curl. The digest on /verify pins these exact bytes.","responses":{"200":{"description":"the Suite document"}}}},"/pricing":{"get":{"operationId":"getPricing","summary":"The AXP Pricing Document","description":"Answers 200 with the closed pricing model (AXP Appendix A.2). This surface declares \"free\": preview build, keyless reads — the metering clauses are not applicable.","responses":{"200":{"description":"the Pricing Document","content":{"application/json":{"schema":{"type":"object","required":["model"],"properties":{"model":{"type":"string","enum":["free","metered"]}}}}}}}}},"/healthz":{"get":{"operationId":"getHealth","summary":"Liveness — typed OK","responses":{"200":{"description":"service is up","content":{"application/json":{"schema":{"type":"object","required":["type","ok","service"],"properties":{"type":{"const":"OK"},"ok":{"type":"boolean"},"service":{"type":"string"},"waitlist":{"type":"integer"}}}}}}}}},"/keys":{"post":{"operationId":"requestKey","summary":"Request an API key (email capture; keys provision by email)","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string"},"building":{"type":"string"}}}}}},"responses":{"200":{"description":"key request recorded (HTML confirmation page)"},"400":{"description":"the email did not parse"}}}}},"components":{"schemas":{"MatterSummary":{"type":"object","required":["id","stage","status"],"properties":{"id":{"type":"string"},"stage":{"type":"string"},"status":{"type":"string"},"tenant":{"type":"string"},"offer":{"type":"string"},"consumerPattern":{"type":"string","enum":["B2A","B2H2A","B2A2D"]},"rail":{"type":"string","enum":["patent","state"]},"matterType":{"type":"string"},"jurisdiction":{"type":"string"},"listFeeUsd":{"type":"number"},"assetId":{"type":"string"},"assetTitle":{"type":"string"}}},"MattersOk":{"type":"object","required":["type","results"],"properties":{"type":{"const":"OK"},"results":{"type":"array","items":{"$ref":"#/components/schemas/MatterSummary"}}}},"Empty":{"type":"object","required":["type","results"],"properties":{"type":{"const":"EMPTY"},"results":{"type":"array","maxItems":0},"message":{"type":"string"}}},"Blocked":{"type":"object","required":["type","reason"],"properties":{"type":{"const":"BLOCKED"},"reason":{"type":"string"}}},"Attestation":{"type":"object","required":["signedBy","credentialRef","artifactHash"],"description":"The Signer's frozen, signed authorization of a reserved act under their own credential — a co-predicate the Gate checks but can never author.","properties":{"signedBy":{"type":"string"},"credentialRef":{"type":"string"},"artifactHash":{"type":"string"}}},"BlockedWithUpgrade":{"type":"object","required":["type","reason"],"description":"A tier-boundary refusal: BLOCKED with the upgrade path named — never a silent 404. upgrade.tier names the rung; upgrade.how names the door to it.","properties":{"type":{"const":"BLOCKED"},"reason":{"type":"string"},"upgrade":{"type":"object","properties":{"tier":{"type":"string"},"how":{"type":"string"}}}}},"SandboxProvenance":{"type":"object","required":["environment","simulated"],"description":"Carried on every envelope under a sandbox grain (/workspaces/{id}, /tenants/{id}) — a simulated payload always says so.","properties":{"environment":{"const":"sandbox"},"simulated":{"const":true},"workspace":{"type":"string"},"tenant":{"type":"string"}}},"MatterEnvelope":{"type":"object","required":["matter"],"properties":{"matter":{"$ref":"#/components/schemas/MatterSummary"}}},"CreateMatter":{"type":"object","required":["tenant","consumerPattern","offer","listFeeUsd","client","asset","rail","matterType"],"properties":{"tenant":{"type":"string"},"mint":{"type":"string"},"consumerPattern":{"type":"string","enum":["B2A","B2H2A","B2A2D"]},"offer":{"type":"string"},"listFeeUsd":{"type":"number"},"client":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}},"asset":{"type":"object","required":["kind","title"],"properties":{"kind":{"type":"string","enum":["patent_application","trademark_registration","trademark","contract","entity","estate"]},"title":{"type":"string"}}},"rail":{"type":"string","enum":["patent","state"]},"matterType":{"type":"string"},"clientState":{"type":"string"},"governingLawState":{"type":"string"},"recipientState":{"type":"string"},"adverseParties":{"type":"array","items":{"type":"string"}},"exposureUsd":{"type":"number"}}}}}}