Skip to content
Start a Dossier
Developers

API reference

Every endpoint, with its parameters. Base URL https://api.codossier.com. All responses are JSON; refusals are problem documents with a code. The generated OpenAPI 3.1 at /v1/openapi.json is the exact contract.

Conventions

AuthAuthorization: Bearer cdk_live_<id>_<secret> or HTTP Basic (cdk_<id> / secret). Sandbox keys: cdk_test_….
WaitPrefer: wait=N (0–60 s) on POST /requests, GET /requests/{id} and GET /events: hold the call until the request changes.
IdempotencyIdempotency-Key: <string ≤128> on POST /requests: a retry returns the same request.
Status codes200 done or waiting on you · 202 accepted, still working · 400 validation · 401 no key · 403 scope or matter set · 404 not yours · 409 conflict or not ready · 422 the material · 429 allowance or rate · 5xx us.
Problems{schema: codossier.problem/1.0, code, title, detail, status, correlation_id, retry?}

The work door

POST /v1/requests · key

One envelope for every intent. JSON, or multipart with a request part (the JSON) and file parts.

FieldWhereRequiredMeaning
intentbodynoopen_matter · reply · supplement · search · inquire · apply · add_node · deliver · import. Absent: read from the shape; ambiguous shapes come back needs_input with a choice.
matterbodynoThe matter's id. Required by every intent except open_matter and reply.
matter_external_idbodynoThe matter by your reference instead of its id.
scopebodyno"lode:<id>" — the lode to work (inquire).
textbodynoThe ask or the material, in words.
documents[]bodyno{part} · {url, filename?, sha256?} · {text, title?} · {dms}.
intakebodynoA codossier.intake/1 record (direct submission).
optionsbodynoThe intent's own fields (below).
estimatebodynotrue: price and plan, run nothing.
in_reply_tobodynoThe request being answered (reply).
answersbodynoAnswers by question id (reply).

Returns a request (codossier.request/1.1): id, understood_as, status, stage, eta, result, questions[], failure, cost, next.poll_after_seconds, links, created_at, updated_at.

GET /v1/requests/{id} · key

One request. With Prefer: wait=N, holds until it changes.

Returns the request.

GET /v1/requests · key

Requests of this office, newest first.

FieldWhereRequiredMeaning
matterquerynoOnly this matter.
scopequerynoOnly this scope, e.g. "lode:<id>".
openquerynotrue: only queued, working and needs_input.
limitqueryno1–200, default 50.

Returns {requests: [...]}

Intents and their options

IntentScopeRunsOptions (in `options`, unless noted)
open_mattermatters:writelongname, external_id, language (en|es|auto), simulate (sandbox: ready|needs_input|not_covered|failed|quota|quote). Material: top-level text, documents or intake. Result: a matter; the dossier via links.dossier.
replymatters:writequickTop-level in_reply_to, plus text and/or answers by question id. To a COMPLETED extend: answers keyed by entry token — each settles that fact in the engine's ledger too and lifts conditional off the proposals resting on it (MCP: settle_fact).
searchsearchquicktop (1–25, default 10). The query is top-level text. Result: hits with node id, kind, path, relevance 0–100, quote.
inquireinquirelongmodel (sonnet|opus). The lode is scope: "lode:<id>"; the ask is text. Result: answer with cites, additions (pending proposals), dropped.
extendinquirelongnamespaces[] (corpus ids, primary first; empty = the matter's own), direction (crest|rift|child|adversary|procedure|free text). The lode is scope: "lode:<id>"; the guidance is text (optional). Result: [email protected] — the seam hash back unmodified, the plan, proposals (crest | rift | proposition_append | child_lode — each the same codossier.proposal object list_proposals and apply return, with posture, verdict, bench, factors, basis and its decision state), entries (open facts, answer with reply by token), steward decisions, the bill. Follow it by its request id: it keeps running if you disconnect. $1–3 by the engine's judgment.
reportinquirelongkind (client_brief|counsel_packet|lead_scoring), fresh (default false), email (default false); the matter is top-level matter and must be completed. Without fresh, the latest report of that kind already on the matter answers at once; with fresh, or when there is none yet, one is generated (the brief and the packet a few minutes; Lead Scoring about a minute of paid model calls) and mailed to you. Result: [email protected] — kind, the report id, fresh, document (the report as data: crift.report-document/1.x — header, numbered sections, typed blocks; the same content the PDF prints), file name, bytes, sha256, download (GET it with the same key) and emailed_to. Reports stay on the matter.
lead_scoringinquirelongThe same as report with kind lead_scoring and fresh, kept for callers that use it (no MCP tool: use report). No options; the matter is top-level matter and must be completed. Result: [email protected] — the report id, file name, bytes, sha256 and download (GET it with the same key). A two-page PDF for co-counsel: the party's questions tested against what is likely, next moves and what not to do, gates and clocks, case value and the other side, provenance, missing pieces, authorities with their status. Each request is a fresh analysis, about a minute; the report stays on the matter.
applymatters:writequickaccept[] and/or discard[] (proposal ids). Result: the decided proposals; an accepted one carries created_node.
add_nodematters:writequickkind (crest|rift|child_lode), parent (a lode id), title; text is the body. Result: the node.
supplementmatters:writelongmode (smart|rebuild). Material as for open_matter. Result: the touched lodes and their proposals.
deliverdocumentsquick (pdf: long)format (json|md|pdf), document (brief|counsel, for pdf), to.upload_url (https, host on the office's delivery list). Result: delivered_to, sha256, bytes, receipt.
importdocumentslongTop-level documents[] with url (+ filename, sha256). Result: each document's id, media type, bytes, sha256, or error.
cancelmatters:writequickTop-level in_reply_to (the request to stop), optional text (why). Queued and waiting requests of any intent stop at once; a working extend stops its engine run first. Refused request_not_cancelable for a finished request or a working dossier build. Result: the stopped request; stopping one already stopped answers the same.

Matters

GET /v1/matters · key

The office's matters (sandbox keys: simulated ones), newest first.

FieldWhereRequiredMeaning
external_idquerynoFind one matter by your reference.
limitqueryno1–200, default 50.

Returns {matters: [...]}: id, external_id, name, status (received, reading, interviewing, building, ready, needs_input, not_covered, failed, deleted), stage, eta, links.

GET /v1/matters/{id} · key

One matter, with its open requests and, when ready, the tree's counts.

Returns a matter.

GET /v1/matters/{id}/dossier · key

The dossier as JSON (codossier.case-export/1): conversation, lodes, crests, rifts, levies. 409 not_ready until built.

GET /v1/matters/{id}/nodes/{nodeId} · key

One lode, crest or rift, with its path and children.

GET /v1/matters/{id}/reports/{reportId} · key

A generated report banked on the matter, as a file: the download link a report (or lead_scoring) result carries. 404 until it is ready.

GET /v1/matters/{id}/proposals · key

The Lode CLI's proposals on a matter.

FieldWhereRequiredMeaning
statusquerynopending (default), accepted, discarded, stale.
lodequerynoOnly this lode.

Returns {proposals: [...]}

DELETE /v1/matters/{id} · key · matters:write

Deletes the matter and its content; the billing record and content-free ledger rows stay. Answers 204.

The intake door

GET /v1/ping · key

Your key works and which office it acts for.

POST /v1/intakes · key

Create a case from any body: multipart files, a form post, raw text/PDF, or JSON. See Create a case.

FieldWhereRequiredMeaning
external_idquery/formno≤128; dedupes and lets you look the intake up.
namequery/formno≤200.
languagequery/formnoen | es | auto.
check_onlyquery/formnotrue: validate only.
simulatequery/formnoready | not_processable | not_covered | failed.

Returns 202 with the intake (id, external_id, status, reason, attempt, missing[], case_url, export_url, status_url); 200 when external_id already exists with the same content.

GET /v1/intakes/{id} · key

The intake's status.

Returns the intake; with the interview once read.

GET /v1/intakes · key

Look an intake up.

FieldWhereRequiredMeaning
external_idqueryyesYour reference.

GET /v1/intakes/{id}/export · key

The dossier as JSON once ready (same document as /matters/{id}/dossier).

DELETE /v1/intakes/{id} · key

Deletes the case.

Events

GET /v1/events · key

Everything that changed for this office since a cursor, in order. With Prefer: wait=N, holds until there is something.

FieldWhereRequiredMeaning
afterquerynoThe cursor from the last page (or from a request's links.events). Omit for the beginning.
limitqueryno1–200, default 100.

Returns {events: [{cursor, request, intent, matter, status, stage, updated_at, link}], cursor, next: {poll_after_seconds}} — pass cursor back as after.

Upload links

GET /v1/uploads/{token} · the token

What the link's page may show: the matter's name, whether it still works, when it expires.

POST /v1/uploads/{token} · the token

multipart/form-data files for the request the link belongs to. One use, 30 minutes. The request continues on its own.

Discovery

GET /v1/openapi.json · none

OpenAPI 3.1 of this door, generated from the contracts.

GET /v1/schemas · none

Every contract and its versions.

GET /v1/schemas/{name}/{version} · none

One JSON Schema, e.g. codossier.intake/1.0.

GET /llms.txt · none

The index for AI assistants; /llms-full.txt is the whole thing.

GET /.well-known/agent-card.json · none

The A2A 1.0 agent card, signed.

GET /.well-known/jwks.json · none

The keys that verify the agent card signature.

GET /.well-known/ucp · none

The UCP profile.

GET /.well-known/oauth-protected-resource · none

RFC 9728 metadata (keys only until OAuth lands).

Other doors

  • /mcp — MCP, Streamable HTTP; see Agents.
  • /a2a — A2A 1.0 JSON-RPC (A2A-Version: 1.0): SendMessage, GetTask, ListTasks, CancelTask.

Reason codes

unauthorized insufficient_scope key_expired key_revoked outside_matter_set sandbox_only forbidden validation invalid_option intent_unknown intent_ambiguous matter_required matter_not_found not_ready nothing_to_do scope_invalid request_not_found request_not_waiting request_not_cancelable idempotency_conflict intake_invalid intake_too_thin external_id_conflict proposal_not_found proposal_decided proposal_stale parent_not_found delivery_host_not_allowed delivery_off document_unreachable document_too_large daily_cap_reached quota_exceeded rate_limited billing_not_enabled quote_required quote_expired conflict not_found internal engine_unavailable engine_failed

Questions

A needs_input request carries questions[]: id, kind (text, choice with options[], upload with upload_url and expires_at, quote with the price), and prompt. Answer with reply: in_reply_to the request, answers keyed by question id (a choice's value, true for an upload done, the quote id to accept) and/or text.