Router.law for developers: REST API and MCP

Router.law Forms fills official Ontario legal forms (Small Claims Court and FSRA accident benefits) from one client profile and checks them. You get back the filled official PDF and a report. Router.law never files or sends anything, and it does not give legal advice. A person must review every form before it is signed or filed.

Pilot: use fake test data only until Router.law announces that it accepts client information.

Using Router.law means accepting the developer terms (draft).

The seven tools

Both interfaces offer the same seven tools.

Tool REST Scope What it does
list_forms GET /v1/forms?family=scc|ab specs:read Every supported form, with its purpose and who uses it
find_form POST /v1/forms/find specs:read Suggests forms for a situation, with reasons. Never chooses
get_form_spec GET /v1/forms/{id} specs:read The questions to ask, the input JSON Schema, the parts a person completes, and the filing and deadline notes
validate POST /v1/forms/{id}/validate forms:fill Status, issues and missing fields, each with a question. No PDF
fill_form POST /v1/forms/{id}/fill forms:fill The report plus the PDF (base64 over REST, an attached file over MCP)
fill_many POST /v1/fill-many forms:fill Up to 10 forms from one profile
check_form_updates GET /v1/forms/updates?form_id=7A specs:read The official version and SHA-256 that Router.law Forms fills now

Recommended agent flow

  1. find_form with the person's own words. Confirm the form with the person.
  2. get_form_spec to get the questions and the schema.
  3. Ask the person for the answers. Never invent them.
  4. validate. For each item in missing_fields or issues, ask the person its question, update the profile, and validate again.
    • Some questions are must ask (MUST_ASK, marked x-routerlaw-must-ask in the schema). The form isn't complete until each is asked. If the person can't answer, record it in no_answer, for example "no_answer": {"accident_benefits.accident.charged": "unknown"}. The value is "unknown" or "not_applicable", and the box stays blank.
  5. fill_form. Show the person the status and the outstanding_actions (for example: sign page 5). Remind them to review the form before filing.

REST

Authenticate with Authorization: Bearer rlk_.... Router.law issues keys.

curl -s https://router.law/v1/forms/find \
  -H "Authorization: Bearer $ROUTERLAW_KEY" -H "Content-Type: application/json" \
  -d '{"situation": "I was rear-ended last week and need to apply for accident benefits"}'

curl -s https://router.law/v1/forms/OCF-1/fill \
  -H "Authorization: Bearer $ROUTERLAW_KEY" -H "Content-Type: application/json" \
  -d @request.json | jq -r .pdf_base64 | base64 -d > OCF-1.pdf

request.json is {"profile": {...}, "flatten": false}. You can add a "password" of 12 or more characters to encrypt the PDF with AES-256.

Every response includes api_version, requires_human_review: true, the form's version and hash. Results carry no repeated disclaimers: this page and the tool descriptions state the notice once.

Errors look like {"error": {"code": "...", "message": "..."}}:

Status Codes
401 unauthorized
403 insufficient_scope
404 unknown_form
411 length_required
413 payload_too_large
422 invalid_request (lists the problem locations only)
429 rate_limited (with Retry-After)

Messages never quote what you sent.

Tools and usage

MCP

Local (stdio): for development with the repository checked out. It runs the engine on your machine, needs no key, and makes no network calls:

{"mcpServers": {"routerlaw": {"command": "uv", "args": ["run", "python", "-m", "routerlaw.mcp_server"],
  "cwd": "/path/to/routerlaw"}}}

Limits

Limit Value
Tool calls 60 per minute per key
Fills 20 per minute per key
Request body 256 KB
Forms per fill_many 10

Keys (Router.law staff)

uv run python -m routerlaw.auth.keys create --label "pilot: name" --key-file ~/routerlaw-key.txt \
    --account marty-pilot --traffic internal
uv run python -m routerlaw.auth.keys revoke <key_id>

The create command writes the key into a new file that only you can read; it never appears on screen. Move it into a password manager and delete the file. The command also prints a record (with the key's scrypt hash, not the key) to add to the ROUTERLAW_API_KEYS variable in Railway (D43). Revoking a key also ends every MCP connection made with it.

--account names the account the key belongs to (lowercase letters, digits and hyphens); usage is counted per account. --traffic internal marks our own products and testing, such as Claim.ca; leave it out for everyone else (external).