How to Build a Claude-Powered Legal Document Summarizer
Building a Claude-powered legal document summarizer means combining three things: a reliable way to call Claude's API, a prompt structure that extracts the specific information lawyers and reviewers actually need, and a pipeline that handles long contracts without losing accuracy. This article walks through all three, with working code you can adapt to contracts, NDAs, court filings, or compliance documents.
The core workflow is simple: send the document text (or relevant sections) to Claude with a prompt that defines what "summary" means for your use case — key clauses, obligations, dates, risks — then parse the structured response into your application. The hard parts are chunking long documents correctly, keeping the output consistent across runs, and making sure the summarizer never invents clauses that aren't in the source text.
Why legal summarization needs a different prompt than generic summarization
A generic "summarize this" prompt produces vague paragraphs. Legal reviewers need specific, extractable fields: parties involved, effective dates, termination clauses, liability caps, governing law, and anything unusual or non-standard. The prompt has to tell Claude exactly what categories to look for and what to do when a category isn't present in the document.
A good system prompt for this task looks like:
You are a legal document analyst. You summarize contracts and legal
documents for internal review, not as a substitute for legal advice.
For each document, extract:
1. Document type and parties involved
2. Effective date and term/duration
3. Key obligations of each party
4. Termination conditions
5. Liability and indemnification clauses
6. Governing law and dispute resolution
7. Any unusual or non-standard clauses
If a category is not present in the document, write "Not specified."
Never infer information that is not explicitly stated in the text.
Quote the relevant clause text when flagging unusual terms.
That last instruction matters more than it looks. Without an explicit "never infer" rule, models will sometimes fill gaps with plausible-sounding boilerplate — exactly the kind of hallucination you cannot afford in a legal context.
Handling long documents
Contracts and filings routinely exceed what fits comfortably in a single prompt alongside your instructions and desired output format. Two practical approaches:
Single-pass with Claude's large context window. For most contracts (even 50–100 pages), Claude's context window handles the full document in one call. This is simpler and avoids the accuracy loss that comes from chunking and merging summaries.
Chunk-and-merge for very large filings. For documents that exceed your budget or latency requirements, split by logical sections (clauses, articles, exhibits) rather than fixed character counts, summarize each chunk separately, then run a final pass that merges the chunk summaries into one coherent output. Fixed-size chunking tends to cut sentences mid-clause and produces worse extraction quality than splitting on structural boundaries like numbered sections or headings.
function chunkByClauses(text) {
// Split on numbered clause headers like "1.", "2.1", "Article III"
return text.split(/\n(?=\d+\.\s|\d+\.\d+\s|Article\s[IVX]+)/g);
}
Calling the API
Here's a minimal implementation using SubToAPI, which exposes Claude through a standard HTTPS API with an application key (sub_live_...) instead of requiring you to manage Anthropic credentials and billing directly.
async function summarizeLegalDoc(documentText) {
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 2000,
system: LEGAL_SYSTEM_PROMPT,
messages: [
{
role: "user",
content: `Summarize the following document:\n\n${documentText}`
}
]
})
});
const data = await response.json();
return data.content[0].text;
}
If you're integrating this into an internal tool with multiple users or a review queue, SubToAPI's dashboard gives you per-key usage metadata, so you can see token consumption per document or per team member without building that tracking yourself. See /docs/quickstart for setup and /docs/messages for the full request reference.
Getting structured output instead of free text
For a summarizer feeding a database or a review UI, you want JSON, not prose. Ask Claude directly for structured output and validate the shape before storing it:
const structuredPrompt = `
Return a JSON object with these exact keys:
{
"documentType": string,
"parties": string[],
"effectiveDate": string,
"term": string,
"obligations": { "party": string, "obligation": string }[],
"terminationConditions": string,
"liabilityClauses": string,
"governingLaw": string,
"unusualClauses": { "clause": string, "quote": string }[]
}
Document:
${documentText}
`;
Always wrap the parse in a try/catch and have a fallback — Claude is reliable with JSON formatting when instructed explicitly, but production code should never assume a perfect parse on the first attempt.
Streaming for long documents
If you're summarizing large filings in a UI where users wait for output, streaming the response improves perceived speed considerably — reviewers see the first sections of the summary (parties, dates) appear immediately while the model is still working through termination and liability clauses. SubToAPI supports streaming responses over the same endpoint; see /docs/streaming for implementation details.
Compliance and disclaimer considerations
Whatever you build, the output should never be presented as legal advice. Bake a disclaimer into your system prompt and surface it in the UI. Also log which model version and prompt version produced each summary — if a clause gets missed, you need to reproduce exactly what the model saw and how it was instructed, which is straightforward if you're versioning prompts alongside your code.
Getting started
You can prototype this entire pipeline in an afternoon: write the system prompt, test it against a handful of real (or sample) contracts, add structured JSON output, and wire it into whatever review tool your team already uses. Start with /signup for a free trial, check /pricing for plan details (Solo at €9/month is enough for most prototyping), and use /docs for the full API reference once you're ready to scale to a team.
Questions
Does Claude read scanned/PDF contracts directly? Claude processes text, so scanned PDFs need OCR first. If your documents are already digital text (most modern contracts), you can extract the text and send it straight to the API.
How accurate is Claude at flagging unusual clauses? It's strong at identifying clauses that deviate from standard boilerplate when you explicitly ask it to quote the relevant text, but it should assist human review, not replace it, especially for high-stakes agreements.
Can I summarize multiple documents in one batch? Yes — loop through documents and call the API per document, or combine related exhibits into one request if they fit within context limits. Keep each document's summary separate rather than merging prompts, since mixing documents increases the risk of cross-contamination in the output.