Docs

Lawpatch for engineers

Everything you need to scan a repository, read the report, let your coding agent fix what it finds, and build on the API.

Quickstart

Two ways in. The first is faster and is what most teams use: install the MCP server in the agent you already code with, hand it a pairing code, and let it do the setup and the first scan itself.

  1. Sign in and create a project.
  2. Add Lawpatch to your agent from /app/connect — one click for Cursor and VS Code, one line for Claude Code and Codex.
  3. Copy the pairing code the same page shows and paste the sentence next to it into your agent.
  4. The agent calls setup_project, which writes lawpatch.config.json (safe to commit) and stores the key in ~/.lawpatch/credentials.json, outside the repository. Then ask it to scan.

A pairing code lasts ten minutes and works once. Redeeming it is what mints the API key, so the code is worthless both before and after — which is why it is safe to paste into a chat, unlike a key.

Or set the repository up from the terminal:

# with a pairing code from the dashboard
npx @lawpatch/sdk pair ABCD-EFGH-JKMN

# or answer the questions yourself
npx @lawpatch/sdk init

npx @lawpatch/sdk scan --dry-run   # print exactly what would be sent
npx @lawpatch/sdk scan

The SDK needs Node.js 20 or later. Every command needs a Lawpatch account; there is no anonymous mode.

How the analysis works

Nine steps, seven of them agents. Each one is instrumented, so every report carries a trace of what ran, for how long and at what token cost.

CartographerBuilds the processing model from your structure: activities, journeys, AI systems, transfers — plus the questions static analysis cannot answer.
Triage counselTurns signals into distinct legal issues, each with a neutral statement of facts and fact-pattern tags.
Precedent researcherHybrid retrieval: embeddings, fact-pattern overlap, statute overlap, authority weight (does it bind your markets?) and recency.
Precedent analystWalks each candidate decision element by element against your facts: met / likely-met / unclear / not-met, binding or persuasive, and what would distinguish you.
CounselScenarios with a sourced exposure figure, and an engineering fix plan that names your files.
Reviewing counselRed-teams the draft: unsupported premises, missed mitigations, disproportionate severity. Then argues the claim in each forum it can reach — an EU data protection authority, a US class action, the KVKK Board, up to three chosen by code from the finding's markets and laws — with standing, both sides on each element, and what each forum could order. Can downgrade or dismiss.
StrategistLooks across findings: how they compound into one incident, and the order to fix them in.
GuardrailsDeterministic: citations must exist, exposure figures must have a source, evidence caps severity.
ReconciliationMatches findings to your last scan so your triage decisions survive, and flags regressions.

The corpus is not prose: every decision carries its holding, the elements of the legal test as applied, the facts that would distinguish it, where it binds and whether it is still good law. That is what lets the analyst reason by analogy instead of by keyword.

CLI reference

Run it with npx @lawpatch/sdk <command>, or install @lawpatch/sdk and use the lawpatch binary. It looks for lawpatch.config.json from the current directory upwards, stopping at the repository root.

CommandWhat it does
lawpatch pair <code>Pair this repository using a pairing code from the dashboard. Writes the config and stores the key; the project's markets and business model come with it.
lawpatch initPair interactively with an API key instead, answering the context questions yourself.
lawpatch scanScan locally, send the derived signals, wait for the analysis and print the findings.
lawpatch findingsList findings from the latest scan. Defaults to open; --status open | in_progress | resolved | accepted_risk | false_positive.
lawpatch mark <id> <status>Set a finding's status: in_progress, resolved or open. When the finding is wrong, not-an-issue --note "why": the reason is required and goes to the audit log, and the finding stays dismissed while its evidence is unchanged.
lawpatch baselineRecord the latest scan's findings in lawpatch.baseline.json (fingerprints, not files). Commit it; --fail-on then counts only new findings.
lawpatch fix <id>Print the fix brief for a finding, or hand it off with --tool cursor | claude | codex.
lawpatch connect <tool>Install the MCP server into cursor, vscode, claude or codex.
lawpatch mcpRun the MCP server over stdio. Coding agents start this; you rarely run it yourself.
lawpatch agentsAdd a marked Lawpatch section to AGENTS.md (Codex and others) and CLAUDE.md (Claude Code): what it is, what it sends, how to use the tools. Asks first; --yes to skip the question. The MCP tool add_agent_instructions does the same.
lawpatch whoamiShow the project the current key belongs to, and its dashboard link.
lawpatch --versionPrint the SDK version (also -v). --help or -h prints usage.

Masked scanning

Source code never leaves the machine in any mode, but the naming around it did: repository paths, route addresses, table and column names, the branch. Set "privacy": "masked" in the config (or LAWPATCH_PRIVACY=masked) and each of those is replaced with a stable pseudonym before the payload is submitted — src/lib/user.ts leaves as 430c9bf141/22076fe566.ts. The mapping stays in ~/.lawpatch/masks/ at mode 0600; lawpatch fix and the MCP server translate the report back, so your agent still reads real paths. The dashboard shows the pseudonyms and says the project is masked.

What stays is the legal substance: data classes, the vendors data reaches, route tags, detector messages and the dependency list — a controlled vocabulary, not anything internal. "strict" goes further and also drops line numbers, identifiers the manifest does not already name as public packages, and dependency versions — at the cost of findings that are harder to act on. A masked scan covers the whole project, because an area-scoped scan is checked server-side by path; set LAWPATCH_MASK_SALT as a CI secret if CI and laptops should agree on which file is which.

scan options

FlagEffect
--dry-runPrint the complete payload that would be sent and stop — after the secret guard, so it is exactly what would leave. Nothing is submitted; the key is still verified first.
--jsonMachine-readable output: the scan and its findings as JSON.
--share-snippetsAttach a masked one-line snippet to each signal for this run (same as shareSnippets in the config).
--fail-on <severity>Exit with code 1 when a finding at or above critical | high | medium | low exists. Overrides failOn in the config. Findings in lawpatch.baseline.json are not counted.
--no-baselineCount every finding for --fail-on, including those recorded in lawpatch.baseline.json.
--no-waitSubmit and exit without waiting for the analysis; prints the report link.
--forceRe-analyse even if an identical payload was analysed in the last 24 hours.
--area <id>Scan only one named project area. Repeat the flag to include several areas; personal keys default to their granted areas.
--changedIn CI, scan only the areas touched by the branch. Use --base <ref> to choose the comparison base.
--commentIn GitHub Actions: create or update the sticky pull-request comment.
--no-annotationsIn GitHub Actions: skip inline annotations on the offending lines.

init options

--key, --markets EU,UK,US,TR, --model b2b|b2c|both, --sector, --description, --api-url (self-hosted deployments) and --yes (accept defaults without prompting).

Exit codes

0 when the command succeeded and no finding reached the --fail-on threshold; 1 for a threshold breach, a failed analysis, a missing or invalid key, or any other error.

Environment variables

VariableUsed for
LAWPATCH_API_KEYThe API key. Takes precedence over the stored credentials — use it in CI.
LAWPATCH_API_URLThe API origin for self-hosted deployments. A key is only sent to the origin it was issued by, the hosted API, localhost, or this origin.
LAWPATCH_PROJECT_ROOTFor the MCP server when it is started outside the repository: the repository's absolute path.
GITHUB_TOKEN / GH_TOKENLets --comment post the pull-request comment. Needs pull-requests: write.
NO_COLORDisables coloured output.

lawpatch.config.json

Written by pair or init at the repository root. It holds no secrets and is meant to be committed.

{
  "$schema": "https://www.lawpatch.studio/schema/lawpatch.config.json",
  "projectId": "…",
  "context": {
    "markets": ["EU", "TR", "US"],
    "businessModel": "b2c",
    "sector": "fitness",
    "description": "Coaching app with AI-generated training plans",
    "audienceIncludesChildren": false
  },
  "exclude": ["legacy", "scripts/fixtures", "**/*.generated.ts"],
  "shareSnippets": false,
  "failOn": "critical"
}
FieldMeaning
projectIdThe project this repository is paired with. A key for a different project is refused before anything is scanned.
apiUrlOptional. The API origin for self-hosted deployments. Because the file is committed, it is not trusted with your key on its own — see API keys below.
context.marketsOne or more of EU, UK, US, TR. Decides which law applies and which precedents bind you.
context.businessModelb2b, b2c or both.
context.sectorFree text, up to 80 characters, or null.
context.descriptionOne line on what the product does, up to 400 characters, or null.
context.audienceIncludesChildrentrue, false or null. Children in the audience changes the analysis of consent, profiling and design.
excludePaths to skip. A bare name (legacy) matches that folder anywhere; a path with a slash (scripts/fixtures) matches from the root; * ? and ** are globs.
shareSnippetsAttach a masked one-line snippet to each signal. Strings, numbers, e-mails and URLs are replaced before sending. Off by default.
failOncritical, high, medium, low or never. The default --fail-on for scan; without either, scan never fails the build.

context matters: the same code can be low-risk for a B2B tool and high-risk for a consumer app used by teenagers. Dependency folders, build output and names listed in .gitignore are skipped without being listed here.

Roles, access and project areas

Owners and admins manage the workspace. Developers can be limited to named projects and codebase areas; lawyers work from legal matters without seeing code; auditors have read-only access. Legacy members retain whole-project developer access.

RoleWorkspace access
ownerEverything. Only the owner can manage admins, workspace deletion and the two-step verification policy.
adminAll projects, members, areas and keys.
developerOnly granted projects and areas. May pair a personal key whose scope follows that grant.
lawyerGranted projects through the legal workspace; no source paths, findings or scan reports.
auditorRead-only access to projects, reports, legal outcomes and the audit log.

Project areas are repository-relative globs such as src/payments/**. Define them on the project page or paste a CODEOWNERS file to preview one area per owner before saving. Findings outside every area remain visible only to whole-project readers.

lawpatch areas
lawpatch areas suggest
lawpatch scan --area payments --area billing
lawpatch scan --changed --base origin/main

A scoped scan sends only signals from those areas and is labelled with its scope in scan history. It updates each area's latest scan, but does not overwrite the whole-project risk score or data map.

Each recurring finding becomes a legal matter that survives re-scans. Three agents write its brief in turn: a technical translator states what the software does as conduct and what each fact means in law, a legal analyst writes the memo — facts, issue, rules, application per jurisdiction, defences, conclusion, exposure, precedents and open questions, all without code — and reviewing counsel red-teams it. Counsel can take the matter, set a deadline, write structured requirements and either publish or send the solution for senior review, and can export the whole thing as a page to print or as Markdown, with every source credited.

SeniorityIndependent authority
TraineeDrafts solutions; every draft needs approval.
AssociatePublishes and accepts risk through medium severity.
SeniorPublishes and accepts risk through high severity; approves junior drafts.
PartnerPublishes, approves and accepts risk at every severity.

When counsel publishes a solution, Lawpatch translates it into an engineering brief. The related finding and get_fix_brief then use that approved brief instead of the scanner's original remediation. Privileged drafts and internal notes never leave the legal workspace.

Governance, outside counsel and notifications

The audit log records membership, key, scan and legal-decision events without recording the text of privileged notes. Owners, admins and auditors see the full log; partners see entries for their projects. The dashboard can export the filtered view as CSV.

Outside counsel must have a project grant and an end date no more than 180 days away. Owners, admins and an in-house partner on those projects can invite or renew them. Expired access is refused by every API until it is deliberately renewed.

The notification bell is the in-app channel for assignments, approvals, published solutions, urgent or overdue matters and expiring access. Lawpatch does not send these notifications by e-mail. Owners can require two-step verification by role from Workspace security; enabling it requires an owner session that already used a second factor, preventing an accidental lockout.

API keys and credentials

MCP server

The SDK includes an MCP server, so an agent can pair the repository, scan it, read the plan, check a precedent before making a change, implement a fix, mark it and re-scan to verify. The server starts whether or not the repository is paired yet: an unpaired repository answers every tool with the instruction to call setup_project instead of failing. Install it from Connect agents or:

npx @lawpatch/sdk connect cursor      # or vscode, claude, codex

# or by hand
claude mcp add lawpatch -- npx -y @lawpatch/sdk mcp
npx @lawpatch/sdk agents               # tell future agent sessions about Lawpatch
codex mcp add lawpatch --env LAWPATCH_PROJECT_ROOT=/path/to/repo -- npx -y @lawpatch/sdk mcp
ToolInputsWhat it does
setup_projectcodeRedeem a pairing code: writes lawpatch.config.json and stores the key outside the repository.
select_projectpath?Point the server at another repository on this machine. Without a path, lists the workspace folders the client shares and which are paired.
check_connection—Verify the key and report which project and folder the server is connected to. Does not scan.
check_filespathsRun the detectors on these files or folders, on this machine only. Nothing is sent; no pairing needed.
scan_projectforce?, areas?, paths?Scan locally and submit. paths scans the areas those files lie in. Returns a scan id straight away; force defaults to true so a verification scan is fresh.
get_scan_statusscan_idStatus of a submitted scan without waiting: queued, analyzing, completed or failed.
list_findingsstatus?, scan_id?Findings from the latest scan, or from the scan given. Status defaults to open.
get_fix_brieffinding_idFiles, lines, step-by-step changes, acceptance criteria and the legal basis, with source credits.
get_legal_feedbackfinding_id?, status?What counsel decided: approved solutions and their requirements, accepted risks, matters under review, questions for engineering.
get_plan—Overall posture, how findings compound, and the order to fix them in.
search_precedentsquery, jurisdictions?Search the corpus by fact pattern in plain English. Returns holdings, elements, what distinguishes a case, and credits.
mark_findingfinding_id, status, note?Set a finding to in_progress or resolved with a short note. A manual resolved is not proof of a fix — re-scan. false_positive (not an issue) needs a note saying why.
add_agent_instructions—Write a marked Lawpatch section into AGENTS.md and CLAUDE.md so later agent sessions know the tool. Edits files; the agent asks you first.

Typical loop: check_connection → scan_project → get_scan_status until completed → get_plan → get_fix_brief (which carries counsel's decision, if there is one) → change the code → check_files → mark_finding → scan_project with the changed paths.

Two prompts come with the server and show up as commands in clients that support them (in Claude Code, /mcp__lawpatch__legal_review and /mcp__lawpatch__fix_top_risk): a full review that ends in the fix order, and fixing the first finding through to a re-scan. Agents can also read /llms.txt, which states what the SDK reads, sends and stores.

CI / GitHub Actions

On a pull request Lawpatch leaves three things behind: inline annotations on the exact lines, a sticky comment that is edited in place on every push rather than piling up, and a job summary. Add --fail-on high and the build goes red before the merge.

# .github/workflows/lawpatch.yml
name: Lawpatch
on: [pull_request]
permissions:
  contents: read
  pull-requests: write        # needed for the sticky comment
jobs:
  legal-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with: { node-version: 22 }
      - run: npx -y @lawpatch/sdk scan --comment --fail-on high
        env:
          LAWPATCH_API_KEY: ${{ secrets.LAWPATCH_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The comment carries the risk score, what changed since the last scan (new / regressed / fixed), the top findings with the precedent behind each one and its source credit, and the recommended fix order. Without a pull request or a token, --comment is skipped and the job summary is still written. Annotations can be turned off with --no-annotations. Other CI systems work the same way with LAWPATCH_API_KEY and --fail-on; use --json to consume the result.

Programmatic API

@lawpatch/sdk is an ES module with TypeScript types. analyze() is the one-call path: it finds the config, verifies the key, scans locally, submits, waits and returns the findings.

import { analyze } from "@lawpatch/sdk";

const { scan, findings, riskScore } = await analyze({
  root: process.cwd(),                  // optional; defaults to the nearest config or repo root
  apiKey: process.env.LAWPATCH_API_KEY, // optional; defaults to the stored credentials
});

const blocking = findings.filter((f) => f.severity === "critical" && f.status === "open");
if (blocking.length) process.exit(1);

It throws when there is no config or key, when the key belongs to another project, or when the analysis fails. For finer control, use the client against the REST API directly:

import { LawpatchClient, LawpatchApiError } from "@lawpatch/sdk";

const client = new LawpatchClient(process.env.LAWPATCH_API_KEY!, "https://www.lawpatch.studio");

const me = await client.whoami();                        // { orgId, projectId, projectName, dashboardUrl }
const { findings } = await client.listFindings({ status: "open" });
const brief = await client.getFinding(findings[0].id);   // the full finding, including agentPrompt
await client.updateFinding(brief.id, "in_progress", "Moving the pixel behind consent");

const { results } = await client.searchPrecedents("session replay before consent", { jurisdictions: ["US"] });

try {
  await client.getScan("unknown");
} catch (e) {
  if (e instanceof LawpatchApiError) console.log(e.status, e.message); // 404 Scan not found
}
ExportPurpose
analyze(opts?)Scan and analyse in one call; returns { scan, findings, riskScore }.
LawpatchClientwhoami, submitScan, getScan, waitForScan, listFindings, getFinding, updateFinding, searchPrecedents.
LawpatchApiErrorThrown for non-2xx responses; carries status and the server's error message.
loadConfig / saveConfigRead and write lawpatch.config.json.
resolveConnectionThe key and API URL a repository would use, with the origin check applied.
redactLineThe masking applied to snippets before they leave the machine.
buildReportMarkdownThe pull-request comment as Markdown, for other CI systems.
SDK_VERSIONThe installed version.

REST API

The SDK is a thin client over a JSON API you can call from anything. Base URL https://www.lawpatch.studio; every endpoint except pairing takes the project's key as a bearer token and acts only on that project.

curl https://www.lawpatch.studio/api/v1/whoami \
  -H "Authorization: Bearer $LAWPATCH_API_KEY"

Errors and limits

Errors are JSON with a human-readable message, { "error": "Scan not found" }, and the HTTP status says what kind: 400 invalid input, 401 missing, malformed or revoked key, 404 not found or not in this project, 413 payload too large, 426 the SDK making the call is a version Lawpatch no longer supports (update @lawpatch/sdk), 429 a limit was reached, 500 our fault. Limits apply per key:

BucketLimitApplies to
scan submission12 per hourPOST /api/v1/scans, plus daily scan limits per project and per workspace
precedent search40 per minuteGET /api/v1/precedents/search
reads and updates300 per minuteEvery other keyed endpoint
pairing10 per 10 minutesPOST /api/v1/pair, per IP address

Endpoints

GET/api/v1/whoami

The project the key belongs to.

{ "orgId": "…", "projectId": "…", "projectName": "Coach", "dashboardUrl": "https://www.lawpatch.studio/app/projects/…" }

GET/api/v1/areas

The project's areas and the ids this key may scan. A project key's grant is ["*"]; a personal key follows its member's current access. The CLI and MCP server use it to scope a scan.

{ "areas": [{ "id": "billing", "name": "Billing", "description": null, "paths": ["src/billing/**"] }], "grant": ["billing"] }

POST/api/v1/scans

Submit a scan payload — the JSON lawpatch scan --dry-run prints: sdkVersion, context, manifest (languages, frameworks, locales, file count, dependencies), signals, structure and git. Up to 4 MB, 2,500 signals and 5,000 dependencies. Returns 202 and the analysis continues in the background. An identical payload analysed in the last 24 hours returns 200 with "reused": true instead; add ?force=1 to re-analyse.

{ "scanId": "…", "url": "https://www.lawpatch.studio/app/projects/…/scans/…" }

GET/api/v1/scans/{scanId}

Poll until status is completed or failed. stage moves through queued, model, triage, research, analysis, counsel, review, strategy and finalizing, and ends at done or failed. A completed scan carries the risk score (0–100), counts by severity, the diff against the previous scan, the fix plan and a summary.

{
  "id": "…", "projectId": "…", "status": "completed", "stage": "done",
  "riskScore": 62, "counts": { "critical": 1, "high": 2, "medium": 3, "low": 0 },
  "partialFailures": 0, "issueProgress": { "total": 6, "processed": 6, "incomplete": 0 },
  "error": null,
  "diff": { "new": [{ "id": "F-…", "title": "…" }], "regressed": [], "fixed": [] },
  "plan": [{ "findingId": "F-…", "why": "…", "batch": "consent layer", "dependsOn": [] }],
  "portfolioSummary": "…",
  "url": "https://www.lawpatch.studio/app/projects/…/scans/…"
}

partialFailures above zero means part of the analysis did not finish; re-scan before treating a missing finding as fixed.

GET/api/v1/findings?scanId=&status=

Findings from the given scan, or the project's latest scan when scanId is omitted; up to 100. status filters by open, in_progress, resolved, accepted_risk or false_positive (not an issue).

{ "scanId": "…", "findings": [ /* Finding */ ] }

GET/api/v1/findings/{findingId}

One finding: title, category, severity, status, jurisdictions, laws, summary, analysis, evidence (file, line, rule), precedents (each with verdict, applicability, authority, element check, treatment and whether it is editor-verified), scenarios, remediation (steps and acceptance criteria) and agentPrompt, the brief a coding agent can implement.

PATCH/api/v1/findings/{findingId}

Update triage status, with a note of up to 500 characters that goes to the workspace's audit log, sealed, not onto the finding. false_positive (not an issue) requires a note of at least 10 characters saying why; at high or critical severity it needs a key whose owner is an owner or admin. A key cannot set accepted_risk.

{ "status": "resolved", "note": "Session replay now starts after consent" }   →   { "ok": true }

GET/api/v1/legal/feedback?findingId=

What counsel has decided in the legal workspace: the published solution and its requirements, the engineering brief, an accepted risk with its rationale, and open questions for engineering. With findingId, the matter for that finding (from any scan); without it, every matter the key may see, decisions first. Drafts and privileged notes are never included.

{ "feedback": [{
  "matterId": "…", "findingId": "F-…", "title": "…", "severity": "high",
  "status": "published", "codeStatus": "detected", "assigned": true, "dueAt": null, "overdue": false,
  "solution": { "text": "…", "requirements": [ … ], "publishedBy": "…", "publishedAt": "…" },
  "engineering": { "status": "ready", "brief": { … }, "agentPrompt": "…" },
  "riskAcceptance": null, "questionsForEngineering": [], "updatedAt": "…"
}] }

GET/api/v1/precedents/search?q=&j=&c=

Search the corpus by fact pattern. q is plain English, at least 3 characters. Repeat j to filter by jurisdiction (US, UK, EU, TR, ECHR, INTL) and c by category (for example tracking-wiretap, consent-cookies, dark-patterns, children, payments). Returns up to 8 results; mode is semantic, or keyword when embeddings are unavailable.

{
  "mode": "semantic",
  "results": [{
    "id": "…", "title": "…", "citation": "…", "forum": "…", "jurisdiction": "US", "date": "2025-04-03",
    "url": "https://…", "holding": "…", "softwareLesson": "…",
    "elements": ["…"], "distinguishing": ["…"], "penalty": null,
    "treatment": "good-law", "verified": false, "similarity": 0.82,
    "attribution": { "text": "Source: …", "licence": { "name": "…", "url": "…" } }
  }]
}

verified: false means the summary is machine-written and not yet checked by an editor. attribution is the credit the source's licence asks for, or null; keep it wherever you show the result.

POST/api/v1/pair

Redeem a pairing code for a new project key. Needs no key — the code is the credential. A code works once and expires after ten minutes.

{ "code": "ABCD-EFGH-JKMN" }
→ { "apiKey": "olv_…", "projectId": "…", "projectName": "…", "apiUrl": "https://www.lawpatch.studio",
    "context": { "markets": ["EU"], "businessModel": "b2c", … }, "dashboardUrl": "…" }

Security model

Precedent corpus

A curated set of court decisions and regulator actions across the US, UK, EU, Türkiye and the ECtHR, each summarised as a holding and a concrete lesson for software teams and embedded for semantic retrieval. A weekly job ingests new decisions and enforcement actions from sources whose terms allow commercial reuse: US federal courts of appeals through GovInfo, the FTC, the CFPB, the ICO, the EDPB, EUR-Lex and the EU's Consumer Protection Cooperation network. Each precedent carries the credit its source's licence asks for. Machine-summarised entries are labelled until an editor verifies them, and an ingested court decision is marked as not yet checked for later appellate history. The counsel agent can only cite decisions that exist in the corpus; a citation guard removes anything else.

Limitations