api.lawyer

API REFERENCE

Every route, from one manifest.

This reference is generated from the same routes manifest the service ships — path, method, and request/response examples as JSON. The base path is this host; requests and responses are application/json.

API reference

Matters

The Matter is the unit of legal work — one Client, one legal objective, advanced through the six Stage Verbs (intake, prepare, examine, review, sign, file) to a terminal outcome. Stage is derived from the append-only journal; terminal is forever.

POST/matters

Mint a Matter and its Asset. A ratified matterType is required (400 otherwise) — jurisdiction derives from the Jurisdiction Rules table against the matter facts. The Asset carries lineage, docket, and a Deadline Ladder.

Request
{
  "tenant": "acme-ip-tools",
  "mint": "patent.click",
  "consumerPattern": "B2H2A",
  "offer": "provisional",
  "listFeeUsd": 1200,
  "client": { "name": "Ada Inventor" },
  "asset": { "kind": "patent_application", "title": "Distributed ledger for credential attestation" },
  "rail": "patent",
  "matterType": "patent_provisional",
  "clientState": "TX",
  "adverseParties": [],
  "exposureUsd": 25000,
  "clientMandates": { "settlementFloor": 50000 },
  "tenantGuardrails": { "maxFeeUsd": 300 }
}
Response
{
  "matter": {
    "id": "m_1",
    "stage": "intake",
    "status": "intake",
    "rail": "patent",
    "matterType": "patent_provisional",
    "jurisdiction": "*",
    "assetId": "a_1",
    "needsClientDirection": false
  }
}
GET/matters/:id

The Matter, its Asset, and the append-only journal of committed events.

Response
{
  "matter": { "id": "m_1", "stage": "review", "status": "waiting_on_signer", "pendingGigId": "g_7" },
  "asset": { "id": "a_1", "kind": "patent_application", "lineage": ["m_1"] },
  "journal": [
    { "kind": "stage", "verb": "intake", "by": "gate", "note": "matter opened" },
    { "kind": "stage", "verb": "prepare", "by": "agent", "note": "…" }
  ]
}
POST/matters/:id/advance

One turn of propose → gate → commit. 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.

Request
{
  // optional B2H2A override, merged over the agent's Proposal
  "merits": { "settlementUsd": 60000, "clientDirectionSeq": 1 }
}
Response
{
  "matter": { "id": "m_1", "stage": "prepare", "status": "prepare" },
  "committed": { "advanceTo": "prepare", "by": "agent" }
}

// or, when the next act is reserved:
{
  "matter": { "id": "m_1", "status": "waiting_on_signer", "pendingGigId": "g_7" },
  "escalated": { "reservedAct": "sign", "gigId": "g_7" }
}
POST/matters/:id/gig-resultsigned by a credential-holder

The gigs.lawyer callback. 'attested' commits the reserved act with the Signer's Attestation; 'send_back' returns the Matter to prepare with defects journaled; 'declined' journals the memo and the Matter needs the Client's direction — a Declination is final.

Request
{
  "gigId": "g_7",
  "outcome": "attested",
  "attestation": { "signedBy": "sg_1", "credentialRef": "uspto:71234", "artifactHash": "sha256:…" }
}
Response
{ "matter": { "id": "m_1", "stage": "sign", "status": "sign" } }
POST/matters/:id/client-direction

Journal the Client's instruction — the only event that reopens a Matter a Declination has stopped. Captured from the actual Client; a Tenant's agent cannot manufacture the Client's yes.

Request
{ "instruction": "proceed with a narrowed claim set", "authorizes": { "settlementUsd": 60000 } }
Response
{ "matter": { "id": "m_1", "status": "review", "needsClientDirection": false }, "directionSeq": 6 }
POST/matters/:id/consent

Journal the Client's Consent — the captured informed-consent artifact (limited scope, tenant data access). Required before a B2A2D Matter advances past intake. Executed by the actual Client: any other actor is refused with 422 — a Tenant's agent cannot consent on the Client's behalf.

Request
{ "actor": "client", "scope": "limited-scope engagement + tenant data access" }
Response
{ "matter": { "id": "m_1", "stage": "intake", "consented": true }, "consentSeq": 1 }
GET/matters/:id/settlement

The two-rail Settlement, only on a fulfilled Matter. Patent rail: platform as provider — flat Legal Fee to the Signer, Product Fee to the Customer. State rail: the Signer is merchant of record for the Legal Fee (recorded, never platform revenue). Both carry the per-act insurance line.

Response
{
  "settlement": {
    "rail": "patent",
    "merchantOfRecord": "platform",
    "productFeeToCustomerUsd": 1200,
    "legalFeeToSignerUsd": 300,
    "attestedActs": 2,
    "perActInsuranceUsd": 40,
    "insuranceTotalUsd": 80,
    "platformNetUsd": 820
  }
}
API reference

Orders

The one-shot Mint surface: a Mint (patent.click) turns a purchase into a Matter and drives it until it waits on a Signer or terminates.

POST/orders

Create a patent-rail Matter and advance it until waiting_on_signer or terminal.

Request
{
  "mint": "patent.click",
  "offer": "provisional",
  "title": "Distributed ledger for credential attestation",
  "inventor": "Ada Inventor",
  "listFeeUsd": 99
}
Response
{ "matterId": "m_2", "status": "waiting_on_signer" }
API reference

Deadline Ladder

Clock-driven safety machinery on the Asset — deterministic (an explicit tick drives the clock, never wall time), closed only by evidence, daily human acknowledgment inside the final week, and an enumerated terminal rung.

GET/deadlines/:assetId

The ladder's state: days remaining, alarm (nominal | vacant), vacancy, the Monitoring Engagement holder, ack window, resolution — and on recurring obligations (franchise tax) the next occurrence and past-occurrence history.

Response
{
  "assetId": "a_1",
  "daysUntilDeadline": 45,
  "alarm": "nominal",
  "vacant": false,
  "monitoring": { "assetId": "a_1", "signerId": "sg-1" },
  "ackRequiredToday": false,
  "terminalOutcomes": ["clientNotice", "extensionPurchase", "proSeHandoff", "outsideCounselHandoff"],
  "occurrences": [],
  "acks": [],
  "resolution": null
}
POST/deadlines/:assetId/ack

A named human acknowledges today’s rung — required daily inside t-7..t-1.

Request
{ "actor": "ops@tenant" }
Response
{ "acked": true, "atDaysRemaining": 6 }
POST/deadlines/:assetId/tick

Advance the deterministic clock by N days; returns the rung events that fired.

Request
{ "days": 15 }
Response
{
  "daysUntilDeadline": 30,
  "alarm": "nominal",
  "events": [{ "rung": "notice", "atDaysRemaining": 30 }]
}
POST/deadlines/:assetId/monitor

Name who holds the Monitoring Engagement on this Asset — a Signer id, or 'platform' for open watch machinery. Naming an owner clears vacancy: dead air has a NAMED owner, never an anonymous one.

Request
{ "signerId": "sg-1" }
Response
{ "monitoring": { "assetId": "a_1", "signerId": "sg-1" }, "vacant": false, "alarm": "nominal" }
POST/deadlines/:assetId/satisfy

Close the deadline by evidence — the receipt. On a one-shot ladder this resolves it forever; on a RECURRING obligation (DE franchise tax) it resolves the current occurrence and rolls nextOccurrence forward one cadence — the obligation never closes this way, it recurs.

Request
{ "evidenceRef": "de/franchise-tax/receipt/2027" }
Response
{
  "assetId": "a_1",
  "recurEveryDays": 365,
  "nextOccurrenceInDays": 365,
  "occurrences": [{ "kind": "satisfied", "evidenceRef": "de/franchise-tax/receipt/2027" }],
  "resolution": null
}
POST/deadlines/:assetId/resolve

Choose the enumerated terminal-rung outcome at t-0 — clientNotice, extensionPurchase, proSeHandoff, or outsideCounselHandoff. Nothing else is a legal answer to t-0; 409 off the terminal rung. On a recurring obligation this is the ONLY permanent closure — a handoff ending the platform’s watch.

Request
{ "outcome": "outsideCounselHandoff" }
Response
{ "resolved": { "kind": "terminal_rung", "outcome": "outsideCounselHandoff" } }
API reference

Practice files

The families a practice-management client runs on: the append-only journal, the Attestation view, the three exit terminals, the Asset register, document metadata, and the per-practitioner Conflicts Ledger — everything derived from the kernel, never authored beside it.

GET/matters/:id/journal

The append-only Event journal. Stage derives from it; terminals are inserts, never mutations.

Response
{ "type": "OK", "matterId": "m_1", "stage": "prepare", "appendOnly": true, "events": [ … ] }
GET/matters/:id/attestations

Every Attestation on the journal — reserved stage acts and utterance-class acts. A typed EMPTY until one lands.

Response
{ "type": "OK", "results": [{ "seq": 4, "act": "sign", "attestation": { "signedBy": "sg-1", … } }] }
POST/matters/:id/terminal

One of the three exit terminals, forever: withdrawn requires byClient (the Client terminates, nobody else); lapsed requires confirmedUnresponsiveTwice; referred_out requires referredTo. 'fulfilled' commits only off a filed receipt via the lifecycle.

Request
{ "outcome": "referred_out", "referredTo": "outside counsel of record", "note": "outside platform scope" }
Response
{ "matter": { "id": "m_1", "stage": "referred_out", … }, "terminal": "referred_out" }
GET/assets/:id

The perpetual noun: insert-only status history, the continuous docket, the lineage of Matters, the external registry id once one exists.

Response
{ "asset": { "id": "a_1", "kind": "patent_application", "statusHistory": [ … ], "docket": [ … ], "lineage": ["m_1"] } }
POST/matters/:id/documents

Record document metadata — { name, kind, sha256 }. The hash is the caller-held content; no blob door exists on this surface, and none is declared.

Request
{ "name": "spec-v1.pdf", "kind": "draft", "sha256": "9f…64 hex…" }
Response
{ "document": { "id": "doc_1", "matterId": "m_1", "name": "spec-v1.pdf", "kind": "draft", "sha256": "9f…" } }
POST/conflicts/:practitionerId/entries

Append one hashed party name to the practitioner’s Conflicts Ledger — hashes at rest, plaintext held by the practitioner, never pooled across practitioners. There is no update and no delete door. On the top-level surface this family rides a verified practitioner credential and answers a typed BLOCKED naming the upgrade path until id.org.ai license verification is live; inside a sandbox workspace it is open on synthetic state.

Request
{ "nameHash": "3b…64 hex…" }
Response
{ "entry": { "seq": 0, "nameHash": "3b…", "recordedAt": "…" }, "appendOnly": true }
API reference

Event tail

The per-grain event tail and its delivery wire — derived from the grain journal by replay, never stored beside it. Three feeds over one tail: timeline (everything, in order), reminders (reminder.* events fired by Deadline Ladder rungs, each addressed to the Monitoring owner — or carrying the vacancy alarm), and time (events carrying a named actor: acks, consents, attestation outcomes, client directions).

GET/events

The tail — typed OK | EMPTY, append-only, cursored with ?after={seq}, filtered with ?feed=timeline|reminders|time, bounded with ?limit (default 100, max 500).

Response
{
  "type": "OK",
  "appendOnly": true,
  "latestSeq": 4,
  "results": [{ "seq": 3, "at": "…", "kind": "reminder.notice", "subject": "a_1",
                "summary": "t-30 notice — Asset a_1 is 30 days from its statutory deadline",
                "deliverTo": "sg-1", "data": { "atDaysRemaining": 30 } }]
}
GET/events/stream

The live wire (Server-Sent Events, under Accept: text/event-stream — what EventSource sends): backlog first, then every new event as it lands — frames named 'grain-event' with the seq as SSE id, so Last-Event-ID resumes a dropped connection. A plain GET answers a typed descriptor naming the subscription contract. A reminder reaching a subscriber here IS the delivery the ladder promises.

Response
id: 3
event: grain-event
data: { "seq": 3, "kind": "reminder.notice", "subject": "a_1", "deliverTo": "sg-1", … }
API reference

Sandbox grains

Anonymous self-serve provisioning — the no-ask-zone applied to tenancy. A workspace is the practice-management grain (the buyer there is a Customer); a tenant is the demand-substrate integrator grain. Both are keyless, synthetic, TTL-bound, and every envelope under them carries provenance { environment: "sandbox", simulated: true }.

POST/workspaces

Provision an ephemeral workspace with synthetic seed state. The FULL practice contract mounts under /workspaces/:id — same routes, same envelopes, no consequences: Gig escalations are answered in-process and you play the Signer via the workspace’s own gig-result door. Anonymous grains expire on a TTL; creation is bounded per caller (429, typed).

Response
{
  "type": "OK",
  "workspace": { "id": "ws_1…", "tier": "anonymous", "environment": "sandbox", "expiresAt": "…",
                 "graduation": { "tier": "identified", "how": "sign in via id.org.ai …" } },
  "provenance": { "environment": "sandbox", "simulated": true, "workspace": "ws_1…" }
}
GET/workspaces/:id/fixtures

The magic fixtures — named, deterministic, contract-tested identities and scenarios every workspace honors (a Signer that always verifies eligible, one that always fails standing, one whose Coverage has lapsed since verification, an always-conflicting party, an always-ack-due Asset, a notice-eve Asset whose first tick fires the t-30 reminder onto the event tail, and the two Clients: the Declination path and the clean walk to fulfilled). Script against these instead of poking at seed state.

Response
{ "type": "OK", "fixtures": [{ "name": "eligible Signer", "use": { "field": "attestation.signedBy", "value": "sg_fixture_eligible" }, "promise": "always verifies eligible — …" }, … ] }
POST/tenants

Provision an ephemeral Tenant: declare Offers (flat-priced — the schema cannot express a percent-of-outcome price) and routing mechanics (Tenant Guardrails only; a merits-bearing field is a 422 before it is a policy question), then mint Matters against them.

Response
{
  "type": "OK",
  "tenant": { "id": "tn_1…", "tier": "anonymous", "environment": "sandbox", "offers": [], "routing": { "maxFeeUsd": 400 } },
  "provenance": { "environment": "sandbox", "simulated": true, "tenant": "tn_1…" }
}
POST/firms

Provision an ephemeral FIRM grain — the envelope of ADR 0014, never an actor: it holds member-executed Enrollment grants (the caller plays the member, enrolling from the named fixture bench at GET /firms/:id/fixtures) and reads GET /firms/:id/roster — a projection computed per read from the eligibility lattice’s binary facts (credential live, standing fresh, coverage in force). No Register is consulted, nothing is scored, and a revoked Enrollment blanks the row. The production family rides the same grain: PUT /firms/:id/routing (mechanics only — a merits key is a 422 type error; opt-ins member-countersigned), GET /firms/:id/remittances (a rollup view that aggregates — the money mechanics stay individual), and the envelope conflicts posture ledger with its Clearance pre-screen (GET /firms/:id/conflicts, POST /firms/:id/conflicts/entries, POST /firms/:id/conflicts/prescreen).

Response
{
  "type": "OK",
  "firm": { "id": "fm_1…", "tier": "anonymous", "environment": "sandbox", "enrollments": { "enrolled": 0, "revoked": 0 } },
  "provenance": { "environment": "sandbox", "simulated": true, "firm": "fm_1…" }
}
API reference

Catalog and verification

The data-product rails (empty-but-real until a SKU is live) and the published test suite — proof artifacts, not adjectives.

GET/catalog

The data-SKU catalog. Zero listed today — a typed EMPTY that carries the declaration contract (posted flat price, fee line, Merchant of Record, meter grain, and the data ProofPredicate: "no record, no charge"). A SKU appears only when it is live, priced, and metered.

Response
{ "type": "EMPTY", "skus": [], "message": "no data SKU is listed — …", "meterPromise": { "promise": "No record, no charge." }, "skuContract": { … } }
GET/verify/suite.json

The digest-pinned public-contract suite (api.qa/suite@1): declarative GET rows over the live doors, runnable by anyone — the autonomous-qa verifier against the live origin, the @law/api verify export, or plain curl. The pin is printed on /verify.

Response
{ "$type": "Suite", "name": "api-lawyer", "version": "1", "requirements": [ … ] }

How the service refuses

Refusals are typed and worded: 404 for a Matter or Asset that does not exist, 409 when the Matter is waiting_on_signer, terminal, or stopped by a Declination, 422 when the Gate refuses a Proposal (mechanics, merits, or a missing Attestation), and 502 when gigs.lawyer refuses a Gig post. The Gate speaks only to refuse — a 2xx is the journal committing.