# lema docs — API: check

The never-reopen surface: adjudicate one approach, or fetch the whole closed set and guard locally.

### POST /check-approach — Check an approach

Adjudicates one named approach against your own recorded deliberation. Exactly two verdicts can fire on this surface: ruled_out — a recorded rejection governs, with the cited why-not — or no_recorded_ruling, the honest unknown. When nothing was ruled out but the record holds the reasoning, the no_recorded_ruling carries a cited why: the recorded rationale, kept distinct from a ruling.

Auth: decisions:read
Quota: Counts as one MCP query (the ask side of the daily wallet).

Request body:
- `approach` (string, required) — The direction to check against your record.
- `workspace_ids` (string[]) — Optional workspace UUIDs to check against. Omitted, every workspace you can see.

Response:
- `repo` (string) — The scope label the check ran over.
- `approach` (string) — Your approach, echoed.
- `verdict` (string) — ruled_out or no_recorded_ruling — nothing else fires on this surface.
- `why_not` (string · optional) — On ruled_out only: the recorded why-not, summarized and cited. Never co-present with why.
- `why` (string · optional) — On no_recorded_ruling only, when the record holds reasoning: the recorded rationale — not a ruling, not approval.
- `sources` (object[]) — The citations behind the verdict: n, ref, type, text (summarized), plus optional url, binding_cosine (relevance, not confidence — measured per call), decision_id.
- `how` (object) — The forward pointer, when the record holds one: guidance and/or the sanctioned alternative, with its own citation when grounded.
- `note` (string · optional) — The honesty note telling the agent how to treat the verdict.
- `usage.atoms_tokens` (integer) — Tokens in the claims actually served.
- `usage.source_decisions` (integer) — How many source decisions those claims cite.
- `usage.source_tokens` (integer) — Tokens in the full source documents behind them — what you did not have to load.
- `usage.tokens_saved` (integer) — source_tokens minus atoms_tokens. Conservative: only sources lema can size are counted.
- `usage.compression_ratio` (number) — source_tokens over atoms_tokens, measured per call.
- `synthesis_tokens_in / synthesis_tokens_out` (integer) — The synthesis cost of this call.

Errors:
- 400 — approach missing, malformed workspace UUID, or an unknown JSON field.
- 403 — a requested workspace exists but is not yours to see.
- 429 — the daily MCP-query wallet is spent.
- 501 — this deployment is not configured to answer.

> Precision-safe by construction: an ungrounded match degrades to the honest no_recorded_ruling — never an ungrounded ruled_out over your record.
> why_not and why are mutually exclusive: a ruled_out never carries why; a no_recorded_ruling never carries why_not.
> A bare abstain returns sources: [] and the explicit note that absence is NOT approval.
> decision_url is never set over a private corpus — decision_id is the stable key you keep.

Example:

```
curl https://api.lema.sh/check-approach \
  -H "Authorization: Bearer $LEMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"approach": "re-merge the ledger service into the monolith"}'
```

```
{
  "repo": "<the workspaces checked>",
  "approach": "re-merge the ledger service into the monolith",
  "verdict": "ruled_out",
  "why_not": "<the recorded why-not — summarized, never a verbatim quote>",
  "sources": [
    {
      "n": 1,
      "ref": "ADR-0019",
      "type": "rejected",
      "text": "<the killed option, with why it was killed>",
      "decision_id": "<stable uuid>"
    }
  ],
  "how": { "guidance": "<the sanctioned alternative, when recorded>" },
  "note": "this approach touches a recorded rejection — surface the
           why-not and its citation, do not re-propose it",
  "usage": { "atoms_tokens": …, "source_decisions": …, "source_tokens": …,
             "tokens_saved": …, "compression_ratio": … },
  "synthesis_tokens_in": …,
  "synthesis_tokens_out": …
}
```

### GET /closed-atoms — The closed set

Your org's CLOSED no-go set: every rejected alternative of an accepted, non-superseded decision, as one atom each. This is what hosted check_decided adjudicates against locally — fetch it once per session and the never-reopen guard runs without a network call per edit.

Auth: decisions:read
Quota: Counts as one MCP query (the retrieve side of the daily wallet).

Query parameters:
- `workspace_ids` (string · repeatable or comma-joined) — ?workspace_ids=A&workspace_ids=B or ?workspace_ids=A,B. Absent, every workspace you can see.

Response:
- `atoms` (object[]) — The closed set, newest first, capped at 500.
- `atoms[].id` (string) — The atom id.
- `atoms[].type` (string) — The claim type — rejected, for this set.
- `atoms[].text` (string) — The recorded rejection, summarized.
- `atoms[].ref` (string) — The governing decision ref.
- `atoms[].locator` (string · optional) — A finer pointer into the source.
- `atoms[].closed` (boolean) — Always true here — this is the never-reopen set.
- `atoms[].closed_note` (string) — The human explanation of why the option is closed.
- `atoms[].match_key` (string) — The killed option's name — the ONLY text the guard keys on, so rationale prose can never false-fire a block.
- `atoms[].workspace` (string · optional) — Which workspace it came from.
- `truncated` (boolean · optional) — Present and true only when the 500-atom cap was hit — a partial no-go set is never silently passed off as complete.

Errors:
- 400 — a malformed workspace UUID.
- 403 — a requested workspace exists but is not yours to see.
- 429 — the daily MCP-query wallet is spent.

> A caller with no workspaces gets 200 with atoms: [] — an empty record is a state, not an error.

Example:

```
curl "https://api.lema.sh/closed-atoms" \
  -H "Authorization: Bearer $LEMA_API_TOKEN"
```

```
{
  "atoms": [
    {
      "id": "<atom id>",
      "type": "rejected",
      "text": "<the recorded rejection, summarized>",
      "ref": "ADR-0019",
      "closed": true,
      "closed_note": "<why this option is closed>",
      "match_key": "<the killed option's name>",
      "workspace": "payments-api"
    }
  ]
}
```
