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.
- Interactive reference:
https://router.law/v1/docs - OpenAPI spec:
https://router.law/v1/openapi.json
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
- find_form with the person's own words. Confirm the form with the person.
- get_form_spec to get the questions and the schema.
- Ask the person for the answers. Never invent them.
- validate. For each item in
missing_fieldsorissues, ask the person itsquestion, update the profile, and validate again.- Some questions are must ask (
MUST_ASK, markedx-routerlaw-must-askin the schema). The form isn't complete until each is asked. If the person can't answer, record it inno_answer, for example"no_answer": {"accident_benefits.accident.charged": "unknown"}. The value is"unknown"or"not_applicable", and the box stays blank.
- Some questions are must ask (
- fill_form. Show the person the
statusand theoutstanding_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
GET /v1/tools?jurisdiction=CA-ONlists every tool, the provider serving it, the scope it needs, where it applies and whether your key can use it. Leave outjurisdictionfor all of them.GET /v1/usageshows your account's metered calls since the service last restarted: calls and units per day, tool and outcome. Use is free during early access; every call is counted.fill_manycounts one unit per form.
MCP
- Remote server:
https://router.law/mcp(streamable HTTP). - Sign-in: OAuth 2.1. Your MCP client discovers it automatically from
/.well-known/oauth-protected-resource/mcp. When you connect, Router.law opens a page where you enter your API key once. The client then gets its own short-lived token. - Claude: add a custom connector with the URL
https://router.law/mcp. - Filled PDFs over MCP come back as
pdf_download, a link for the person that works once, for 10 minutes. Chat apps can't pass files from connectors to the person. Give the person the URL exactly as returned. The local stdio server returns the PDF itself.
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).