5-minute quickstart
-
Request an API key
API keys aren’t self-serve yet. Request your key and we’ll email it as soon as Pactlyst.ai opens. Everything below assumes you have one — store it as an environment variable, e.g.
PACTLYST_KEY. -
Score a document
Send agreement text to
score-document. You get back a 0–100 Pactlyst Score, a verdict, red flags, key terms, a plain-language explanation, and a signed attestation receipt.TERMINALcurl -X POST https://djcmqtyenuvrtotmpfxl.supabase.co/functions/v1/score-document \ -H "Authorization: Bearer $PACTLYST_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Section 9 \u2014 Term. This agreement renews automatically for successive 12-month terms unless you give written notice at least 90 days before the renewal date.", "title": "Sample lease", "language": "Spanish" }' -
Verify the receipt
Every score comes with a signed receipt. Anyone — your users, a counterparty, an auditor — can check it against the public
attestationendpoint. No API key needed.TERMINALcurl -X POST https://djcmqtyenuvrtotmpfxl.supabase.co/functions/v1/attestation \ -H "Content-Type: application/json" \ -d '{ "receipt": { "version": "pactlyst-attestation/v2", "score": 55, "verdict": "caution", "doc_sha256": "9f2c\u20264 hex chars\u2026", "document_version": "v3", "playbook_id": "pactlyst-standard", "playbook_version": "v1", "analysis_version": "gpt-4o-mini/prompt-v1", "findings_summary": {"high": 1, "medium": 2, "low": 1}, "findings_sha256": "7d4e\u20264 hex chars\u2026", "limitations": [], "key_id": "key_abc123", "timestamp": "2026-10-01T22:00:00.000Z", "requesting_agent": "procurement-agent-7", "signature": "a1b2\u20264 hex chars\u2026" } }' # {"valid": true, "receipt": {"version": "pactlyst-attestation/v2", "score": 55, ...}} # {"valid": false, "reason": "signature_mismatch"}
Authentication
Authenticated endpoints expect your key as a Bearer token:
Authorization: Bearer $PACTLYST_KEY
score-documentandmcprequire a key. Missing or unknown keys return401(missing_api_key/invalid_api_key); revoked keys return403(key_revoked).attestationis public — verification needs no key, so receipts can be checked by anyone.request-accessis public — it only records key requests.
POST /score-document
Scores one agreement or clause. Request and response bodies are JSON (Content-Type: application/json).
Parameters
| Field | Type | Notes |
|---|---|---|
| text | string, required | The agreement or clause text. Max 50,000 characters. Text that isn’t a legal document is rejected — never scored. |
| title | string, optional | A label for the document, e.g. “Sample lease”. |
| language | string, optional | Explanation language, e.g. "Spanish". Defaults to English. |
Response fields (200)
| Field | What it is |
|---|---|
| score | Integer 0–100. Higher is better. |
| verdict | sign | caution | dont_sign — computed server-side from the band rule below. |
| document_type | Detected document type, e.g. “Residential lease”. |
| summary | Short plain-language summary of the document. |
| flags | Red flags: { title, detail, severity: high | medium | low, quote? }. quote is the exact clause wording when available. |
| key_terms | Key terms: { term, meaning, quote? }. |
| explanation | Plain-language explanation of the score. |
| disclaimer | “This score is information to help you understand a document — not legal advice. Pactlyst is not a lawyer. Verify anything important independently.” |
| receipt | Signed attestation receipt — see Receipts & verification. |
| usage | { model, prompt_tokens, completion_tokens } — metering detail for the request. |
The verdict band rule
The verdict is computed server-side from the score and the number of high-severity flags. The model’s own verdict is never used.
| Verdict | Rule |
|---|---|
| sign | Score ≥ 70 and zero high-severity flags |
| dont_sign | Score < 40 or 2+ high-severity flags |
| caution | Everything else |
{
"score": 55,
"verdict": "caution",
"document_type": "Residential lease",
"summary": "A 12-month lease that renews automatically unless you give 90 days' notice.",
"flags": [
{ "title": "Automatic renewal", "detail": "Renews for 12 months unless you give 90 days' written notice.", "severity": "high", "quote": "This agreement renews automatically for successive 12-month terms\u2026" }
],
"key_terms": [
{ "term": "Renewal notice window", "meaning": "You must act 90 days before renewal or you're locked in for a year." }
],
"explanation": "Auto-renewal with a 90-day notice window is easy to miss \u2014 calendar the deadline the day you sign.",
"disclaimer": "This score is information to help you understand a document \u2014 not legal advice. Pactlyst is not a lawyer. Verify anything important independently.",
"receipt": { "version": "pactlyst-attestation/v2", "score": 55, "verdict": "caution", "doc_sha256": "9f2c\u2026", "document_version": "v3", "playbook_id": "pactlyst-standard", "playbook_version": "v1", "analysis_version": "gpt-4o-mini/prompt-v1", "findings_summary": { "high": 1, "medium": 2, "low": 1 }, "findings_sha256": "7d4e\u2026", "limitations": [], "key_id": "key_abc123", "timestamp": "2026-10-01T22:00:00.000Z", "requesting_agent": "procurement-agent-7", "signature": "a1b2\u2026" },
"usage": { "model": "gpt-4o-mini", "prompt_tokens": 1204, "completion_tokens": 386 }
}
Receipts & verification
Every score returns a signed attestation receipt — a review record that lets an organization verify what an AI agent checked before it acted: the document version reviewed, the rules or playbook applied, the analysis version, the findings and limitations, the time of review, and the agent or workflow that requested it. Receipts are verified statelessly with HMAC-SHA256 over the canonical (sorted-key) JSON of the payload:
| Receipt field | What it is |
|---|---|
| version | pactlyst-attestation/v2 (v1 receipts remain verifiable during the transition) |
| score | The 0–100 score |
| verdict | sign | caution | dont_sign |
| doc_sha256 | SHA-256 of the scored document text |
| document_version | Caller-supplied revision label for the exact document revision reviewed (e.g. v3); unspecified when not given — never invented |
| playbook_id | Rules applied — default pactlyst-standard; custom enterprise playbook ids allowed |
| playbook_version | Playbook version — default v1 (the current band methodology) |
| analysis_version | Model + prompt version that produced the analysis (e.g. gpt-4o-mini/prompt-v1) |
| findings_summary | Finding counts by severity, e.g. {high: 2, medium: 3, low: 1} |
| findings_sha256 | SHA-256 of the full findings payload — binds the findings to the receipt without putting finding text in it |
| limitations | Explicit analysis limitations for this review (e.g. machine-translated explanation, omitted unverifiable quotes); [] when none — the field is never omitted |
| key_id | ID of the API key that scored it |
| timestamp | ISO-8601 time of review |
| requesting_agent | Identifier of the agent or workflow that requested the review; unspecified when not given — never invented |
| signature | HMAC-SHA256 of the payload |
The signature proves this record was not altered. It does not prove the analysis was correct or that the contract was safe to sign.
Receipts stay small by design: they carry finding counts plus a hash of the findings — never full finding text and never document text. Any change to any payload field invalidates the signature. Verify with POST /attestation (public, no auth):
curl -X POST https://djcmqtyenuvrtotmpfxl.supabase.co/functions/v1/attestation \
-H "Content-Type: application/json" \
-d '{ "receipt": { "version": "pactlyst-attestation/v2", "score": 55, "verdict": "caution", "doc_sha256": "9f2c\u2026", "document_version": "v3", "playbook_id": "pactlyst-standard", "playbook_version": "v1", "analysis_version": "gpt-4o-mini/prompt-v1", "findings_summary": {"high": 1, "medium": 2, "low": 1}, "findings_sha256": "7d4e\u2026", "limitations": [], "key_id": "key_abc123", "timestamp": "2026-10-01T22:00:00.000Z", "requesting_agent": "procurement-agent-7", "signature": "a1b2\u2026" } }'
# valid: {"valid": true, "receipt": {"version": "pactlyst-attestation/v2", "score": 55, ...}}
# invalid: {"valid": false, "reason": "signature_mismatch"}
Verification reasons include signature_mismatch, unknown_version, bad_score, bad_verdict, bad_doc_hash, bad_document_version, bad_playbook, bad_analysis_version, bad_findings, bad_limitations, bad_requesting_agent, bad_key_id, bad_timestamp, bad_signature, and malformed_receipt.
MCP server
POST /mcp is a JSON-RPC 2.0 (streamable HTTP) endpoint so AI assistants can call Pactlyst natively. Same Bearer auth as score-document. Supported methods: initialize, tools/list, tools/call. Batching: send a JSON array of request objects and get back an array of responses — handy for scoring several documents in one round trip. Batches containing only notifications (no id) return 202 with an empty body.
Available tools
| Tool | Arguments | What it does |
|---|---|---|
| score_document | text (required), title, language, document_version, playbook_id, playbook_version, requesting_agent | Scores a document — same engine as POST /score-document, returns score, verdict, flags, key terms, explanation, and a v2 receipt. |
| verify_receipt | receipt (required) | Verifies an attestation receipt — same engine as POST /attestation. |
curl -X POST https://djcmqtyenuvrtotmpfxl.supabase.co/functions/v1/mcp \
-H "Authorization: Bearer $PACTLYST_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "score_document",
"arguments": {
"text": "Section 9 \u2014 Term. This agreement renews automatically for successive 12-month terms unless you give written notice at least 90 days before the renewal date."
}
}
}'
curl -X POST https://djcmqtyenuvrtotmpfxl.supabase.co/functions/v1/mcp \
-H "Authorization: Bearer $PACTLYST_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "verify_receipt",
"arguments": {
"receipt": { "version": "pactlyst-attestation/v2", "score": 55, "verdict": "caution", "doc_sha256": "9f2c\u2026", "document_version": "v3", "playbook_id": "pactlyst-standard", "playbook_version": "v1", "analysis_version": "gpt-4o-mini/prompt-v1", "findings_summary": {"high": 1, "medium": 2, "low": 1}, "findings_sha256": "7d4e\u2026", "limitations": [], "key_id": "key_abc123", "timestamp": "2026-10-01T22:00:00.000Z", "requesting_agent": "procurement-agent-7", "signature": "a1b2\u2026" }
}
}
}'
JSON-RPC error codes follow the standard set: -32700 parse error, -32600 invalid request, -32601 method not found, -32602 invalid params (including unknown tools), -32603 internal error, and -32001 for auth failures (surfaced at HTTP 401/403).
Idempotency
POST /score-document accepts an Idempotency-Key header (1–64 characters, [A-Za-z0-9-_]). Send the same key with the same request and the first completed result is replayed — no re-score, no double-metering. If a duplicate arrives while the original is still scoring, you get 409 idempotency_in_progress; retry with the same key instead of generating a new one.
Idempotency-Key: lease-2026-10-01-a91f
Error codes
Errors are JSON: { "error": "<code>", "message": "..." }. The message is human-readable; switch on error.
score-document
| HTTP | Code | Meaning |
|---|---|---|
| 401 | missing_api_key | No Authorization header / no key sent |
| 401 | invalid_api_key | The key is not recognized |
| 403 | key_revoked | This key has been revoked |
| 405 | method_not_allowed | Use POST |
| 409 | idempotency_in_progress | A request with this Idempotency-Key is still scoring |
| 413 | text_too_large | Text exceeds 50,000 characters |
| 415 | invalid_content_type | Send JSON with Content-Type: application/json |
| 400 | invalid_json | Request body is not valid JSON |
| 400 | missing_text | The text field is required |
| 400 | empty_text | The text field is empty |
| 422 | not_a_document | The text isn’t a legal document — never scored |
| 429 | rate_limited | Over 60 requests/minute — slow down and retry in a minute |
| 429 | quota_exceeded | Monthly scoring quota for this key is used up |
| 502 | scoring_failed | The scoring engine didn’t respond — retry |
| 503 | scoring_not_configured | Scoring isn’t configured on the server yet |
| 500 | internal_error | Something went wrong — retry |
attestation
| HTTP | Code | Meaning |
|---|---|---|
| 405 | method_not_allowed | Use POST |
| 415 | invalid_content_type | Send JSON |
| 400 | invalid_json | Request body is not valid JSON |
| 400 | missing_receipt | The receipt object is required |
| 200 | (valid: false + reason) | Receipt is structurally fine but fails checks — see reasons above |
| 500 | internal_error | Something went wrong |
mcp
| HTTP | Code | Meaning |
|---|---|---|
| 401 | -32001 (missing or invalid API key) | Auth failure at the HTTP layer |
| 403 | -32001 (API key revoked) | Auth failure at the HTTP layer |
| 405 | method_not_allowed | Use POST |
| 400/415 | -32700 (parse error) | Bad JSON or wrong Content-Type |
| 200 | -32600 / -32601 / -32602 / -32603 | Invalid request / method not found / invalid params (incl. unknown tool) / internal error |
| 202 | (empty body) | Batch contained only notifications |
request-access
| HTTP | Code | Meaning |
|---|---|---|
| 405 | method_not_allowed | Use POST |
| 415 | invalid_content_type | Send JSON |
| 400 | invalid_json | Request body is not valid JSON |
| 400 | invalid_email | A valid email address is required |
| 429 | rate_limited | More than 3 requests from this email in the last hour |
| 500 | internal_error | Something went wrong |
Rate limits & quotas
- 60 requests per minute per key on
score-documentandmcp— over the line returns429 rate_limited. - Monthly quota per key — each successful score counts against the key’s monthly quota; when it’s used up you get
429 quota_exceeded. - 50,000 characters max per
score-documentrequest — longer texts return413 text_too_large. - 3 requests per hour per email on
request-access— anti-spam, returns429 rate_limited.
Privacy
- Document text is never stored and never logged. It’s held in memory only for the duration of the scoring call.
- Only metadata is recorded: key ID, request status, input character count, and token counts (in
api_usage). - Attestation receipts contain only a SHA-256 hash of the document — never the text itself.
- The idempotency cache keeps only score, verdict, and receipt — never document text or model prose.
Pricing
| Plan | Price | What’s included |
|---|---|---|
| Free | $0 — 5 scans / month | No card required |
| Pay-as-you-go | $2.00 / scan | No commitment — billed per scored document |
| Pro | $49 / month | 50 scans included; overage $1.50 / scan |
| Enterprise | Custom | Volume pricing, custom playbooks, SLA |
A “scan” is one successful score-document call (or one score_document MCP tool call). Replays served from an Idempotency-Key don’t count as new scans.