API Documentation
Integrate DemandAI's legal-document engines into your own systems. Five JSON endpoints, one API key, PII protection built in.
Overview
The DemandAI Public API v1 gives banks, firms, and partners programmatic access to the same AI engines that power the web application — including the triple-check generation pipeline (draft, independent forensic audit, reconcile) and in-memory PII tokenization.
Base URL
https://demandai.pro/api/v1
Generate a settlement demand letter through the triple-check pipeline: draft, independent forensic damages audit, reconcile. Party names and document text are PII-tokenized in memory before reaching the AI and re-identified only on the finished letter.
Predict a conservative settlement-value range (low / expected / high) before drafting a demand, with value drivers, risk factors, comparable basis, and full reasoning. Detected identifiers in caseSummary and injuries are PII-tokenized before reaching the AI.
Analyze an insurer’s offer against the original demand (adequacy, recommendation, defensible counter, leverage points — offerRatio recomputed deterministically) and draft the counter-offer response letter. Detected identifiers in caseSummary and insurer are PII-tokenized before reaching the AI.
Project future / ongoing medical (life-care) costs as itemized line items. Arithmetic is reconciled deterministically: each totalCost = annualCost × durationYears, and the grand total is the exact sum of the line items. Detected identifiers in injuries, prognosis, and currentTreatment are PII-tokenized before reaching the AI.
Generate mortgage-servicer correspondence (default notice, acceleration, reinstatement, payoff, FDCPA validation, or general demand) with FDCPA/RESPA compliance guardrails and an independent figures audit. Loan numbers and borrower names are PII-tokenized before the AI. Every letter is a DRAFT for compliance and legal review.
Privacy-first by design. Detected identifiers — names, emails, SSNs, loan and account numbers, addresses — are replaced with tokens in memory before any text reaches the AI, and restored only on the finished output. Detection is automated and best-effort: supply party names in their dedicated fields so every occurrence tokenizes deterministically. If residual identifiers cannot be fully scrubbed, generation is blocked with a 422, and every response reports whether tokenization actually occurred via piiProtected.
Authentication
Every request must carry a DemandAI API key. Keys start with dl_ and are created in the dashboard under Settings → API Keys. Each key carries a set of scopes; an endpoint rejects keys without its required scope (403).
Send the key in either header:
Authorization: Bearer dl_your_api_keyX-API-Key: dl_your_api_key| Endpoint | Required scope |
|---|---|
| /api/v1/demand-letters | documents:write |
| /api/v1/case-value | case-value:read |
| /api/v1/negotiation | negotiation:write |
| /api/v1/future-medical | future-medical:read |
| /api/v1/mortgage-demand | mortgage:write |
Treat API keys like passwords: store them in a secrets manager, never commit them, and never use them in browser-side code. API requests do not use cookies or CSRF tokens — the key is the sole credential.
Endpoints
All endpoints accept POST with a JSON body and return JSON. Full request/response schemas live in the OpenAPI spec.
/api/v1/demand-letters
documents:writeGenerate a settlement demand letter through the triple-check pipeline: draft, independent forensic damages audit, reconcile. Party names and document text are PII-tokenized in memory before reaching the AI and re-identified only on the finished letter.
Returns 422 (PII scrub incomplete) if sensitive values in documentsText cannot be fully tokenized — supply clientName/defendantName so every occurrence collapses to a token, then retry.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| clientName | string | required | Claimant name. |
| caseType | "personal-injury" | "property" | optional | Default personal-injury (premium pipeline). property runs the standard homeowner pipeline. |
| defendantName | string | optional | Defendant / at-fault party. |
| incidentDate | string | optional | ISO date — drives the statute-of-limitations check. |
| incidentLocation | string | optional | Where the incident occurred. |
| jurisdiction | string | optional | US state (name or 2-letter code). |
| documentsText | string | optional | Pre-extracted text of medical records, bills, police reports (max 800,000 chars). |
| attorney | object | optional | Sender letterhead: { name, firmName, firmAddress, phone, email, barNumber }. |
Example — curl
curl -X POST https://demandai.pro/api/v1/demand-letters \
-H "Authorization: Bearer dl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"caseType": "personal-injury",
"clientName": "Jane Doe",
"defendantName": "John Roe",
"incidentDate": "2026-01-15",
"jurisdiction": "OH",
"documentsText": "--- DOCUMENT: er-bill.pdf ---\nGrant Medical Center ER 01/15/2026. CPT 99284: $1,850.00 ...",
"attorney": { "name": "Alex Counsel", "firmName": "Counsel & Associates LLP" }
}'Example — fetch
const res = await fetch('https://demandai.pro/api/v1/demand-letters', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DEMANDAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
caseType: 'personal-injury',
clientName: 'Jane Doe',
defendantName: 'John Roe',
incidentDate: '2026-01-15',
jurisdiction: 'OH',
documentsText: erBillText,
}),
})
const { letter, demandAmount, audit, demandStrength } = await res.json()Response — 200
{
"success": true,
"letter": "…full demand letter…",
"demandAmount": 187500,
"figures": {
"medicalExpenses": 28500,
"economicDamages": 42500,
"nonEconomicDamages": 114000,
"multiplier": 4,
"punitiveDamages": 0
},
"audit": {
"completed": true,
"mathVerified": true,
"corrected": false,
"discrepancies": []
},
"caseStrength": "Strong",
"confidence": 0.92,
"demandStrength": 82,
"strengthLabel": "Strong",
"statuteOfLimitations": { "status": "ok", "…": "…" },
"piiProtected": true,
"modelUsed": "claude-opus-4-8",
"processingTimeMs": 148213
}/api/v1/case-value
case-value:readPredict a conservative settlement-value range (low / expected / high) before drafting a demand, with value drivers, risk factors, comparable basis, and full reasoning. Detected identifiers in caseSummary and injuries are PII-tokenized before reaching the AI.
Returns 422 (PII scrub incomplete) if detected identifiers in caseSummary/injuries cannot be fully tokenized — remove names and identifiers, then retry.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| caseSummary | string | required | Facts of the matter. |
| jurisdiction | string | optional | US state — applies fault rules and damage caps. |
| injuries | string | optional | Injury description. |
| medicalSpecials | number | optional | Documented medical specials in dollars. |
| liability | "clear" | "disputed" | "shared" | optional | Liability posture. |
Example — curl
curl -X POST https://demandai.pro/api/v1/case-value \
-H "Authorization: Bearer dl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"caseSummary": "Rear-end collision at a stoplight; 4 months of treatment.",
"jurisdiction": "TX",
"medicalSpecials": 28500,
"liability": "clear"
}'Example — fetch
const res = await fetch('https://demandai.pro/api/v1/case-value', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DEMANDAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
caseSummary: 'Rear-end collision at a stoplight; 4 months of treatment.',
jurisdiction: 'TX',
medicalSpecials: 28500,
liability: 'clear',
}),
})
const { prediction } = await res.json()Response — 200
{
"success": true,
"prediction": {
"low": 65000,
"expected": 95000,
"high": 130000,
"confidence": 0.78,
"valueDrivers": ["Clear liability", "Documented specials"],
"riskFactors": ["Soft-tissue component"],
"comparableBasis": "3x-4x specials band for moderate injuries…",
"reasoning": "…"
},
"piiProtected": false,
"modelUsed": "claude-sonnet-4-6",
"processingTimeMs": 21874
}/api/v1/negotiation
negotiation:writeAnalyze an insurer’s offer against the original demand (adequacy, recommendation, defensible counter, leverage points — offerRatio recomputed deterministically) and draft the counter-offer response letter. Detected identifiers in caseSummary and insurer are PII-tokenized before reaching the AI.
Returns 422 (PII scrub incomplete) if detected identifiers in caseSummary cannot be fully tokenized — remove names and identifiers, then retry.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| demandAmount | number | required | Original settlement demand in dollars (> 0). |
| offerAmount | number | required | Insurer’s offer in dollars (≥ 0). |
| caseSummary | string | required | Facts supporting the demand. |
| insurer | string | optional | Carrier name. |
Example — curl
curl -X POST https://demandai.pro/api/v1/negotiation \
-H "Authorization: Bearer dl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"demandAmount": 250000,
"offerAmount": 62500,
"caseSummary": "Clear-liability collision; $48k documented specials.",
"insurer": "Acme Mutual"
}'Example — fetch
const res = await fetch('https://demandai.pro/api/v1/negotiation', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DEMANDAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
demandAmount: 250000,
offerAmount: 62500,
caseSummary: 'Clear-liability collision; $48k documented specials.',
insurer: 'Acme Mutual',
}),
})
const { analysis, responseLetter } = await res.json()Response — 200
{
"success": true,
"analysis": {
"offerAmount": 62500,
"demandAmount": 250000,
"offerRatio": 0.25,
"adequacy": "lowball",
"recommendation": "counter",
"suggestedCounter": 225000,
"leveragePoints": ["Clear liability", "…"],
"reasoning": "…"
},
"responseLetter": "…full counter-offer letter…",
"piiProtected": true,
"modelUsed": "claude-opus-4-8",
"processingTimeMs": 64110
}/api/v1/future-medical
future-medical:readProject future / ongoing medical (life-care) costs as itemized line items. Arithmetic is reconciled deterministically: each totalCost = annualCost × durationYears, and the grand total is the exact sum of the line items. Detected identifiers in injuries, prognosis, and currentTreatment are PII-tokenized before reaching the AI.
Returns 422 (PII scrub incomplete) if detected identifiers in injuries/prognosis/currentTreatment cannot be fully tokenized — remove names and identifiers, then retry.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| injuries | string | required | Injury description. |
| prognosis | string | optional | Medical prognosis. |
| claimantAge | number | optional | Claimant age in years — drives the projection horizon. |
| currentTreatment | string | optional | Current / past treatment. |
| jurisdiction | string | optional | US state. |
Example — curl
curl -X POST https://demandai.pro/api/v1/future-medical \
-H "Authorization: Bearer dl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"injuries": "L4-L5 disc herniation, status post microdiscectomy",
"prognosis": "Likely future fusion within 10-15 years",
"claimantAge": 42
}'Example — fetch
const res = await fetch('https://demandai.pro/api/v1/future-medical', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DEMANDAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
injuries: 'L4-L5 disc herniation, status post microdiscectomy',
prognosis: 'Likely future fusion within 10-15 years',
claimantAge: 42,
}),
})
const { projection } = await res.json()Response — 200
{
"success": true,
"projection": {
"lineItems": [
{
"treatment": "Anticipated lumbar fusion surgery",
"frequency": "one-time",
"durationYears": 1,
"annualCost": 85000,
"totalCost": 85000
}
],
"totalFutureMedical": 142600,
"lifeCareSummary": "…demand-ready narrative…",
"assumptions": ["…"],
"confidence": 0.72
},
"piiProtected": false,
"modelUsed": "claude-sonnet-4-6",
"processingTimeMs": 30412
}/api/v1/mortgage-demand
mortgage:writeGenerate mortgage-servicer correspondence (default notice, acceleration, reinstatement, payoff, FDCPA validation, or general demand) with FDCPA/RESPA compliance guardrails and an independent figures audit. Loan numbers and borrower names are PII-tokenized before the AI. Every letter is a DRAFT for compliance and legal review.
At least one of documentsText, borrowerName, loanNumber, or totalArrears is required. Returns 422 (PII scrub incomplete) if borrower PII in documentsText cannot be fully tokenized.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| letterType | string | optional | default-notice (default) | acceleration | reinstatement-demand | payoff-demand | fdcpa-validation | general-demand. |
| servicer | string | optional | Servicer / sender name (letterhead). |
| borrowerName | string | optional | Borrower name (tokenized). |
| coBorrowerName | string | optional | Co-borrower name (tokenized). |
| loanNumber | string | optional | Loan number (tokenized). |
| propertyAddress | string | optional | Property address (tokenized). |
| defaultDate | string | optional | Date of default. |
| totalArrears | string | number | optional | Total arrears in dollars. |
| perDiem | string | number | optional | Per-diem interest. |
| reinstatementAmount | string | number | optional | Reinstatement figure. |
| payoffAmount | string | number | optional | Payoff figure. |
| investor | string | optional | Investor / owner of the loan (tokenized). |
| jurisdiction | string | optional | US state. |
| documentsText | string | optional | Pre-extracted text of loan documents (max 800,000 chars). |
Example — curl
curl -X POST https://demandai.pro/api/v1/mortgage-demand \
-H "Authorization: Bearer dl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"letterType": "default-notice",
"servicer": "First Example Servicing LLC",
"borrowerName": "Jane Q. Borrower",
"loanNumber": "0012345678",
"totalArrears": "8420.55",
"jurisdiction": "OH"
}'Example — fetch
const res = await fetch('https://demandai.pro/api/v1/mortgage-demand', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DEMANDAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
letterType: 'default-notice',
servicer: 'First Example Servicing LLC',
borrowerName: 'Jane Q. Borrower',
loanNumber: '0012345678',
totalArrears: '8420.55',
jurisdiction: 'OH',
}),
})
const { letter, figures, figuresVerified } = await res.json()Response — 200
{
"success": true,
"letter": "…DRAFT letter for compliance review…",
"figures": {
"totalArrears": 8420.55,
"perDiem": 21.4,
"reinstatementAmount": 8420.55,
"payoffAmount": 0,
"lateFees": 316.82
},
"figuresVerified": true,
"auditCompleted": true,
"letterType": "default-notice",
"discrepancies": [],
"piiProtected": true,
"modelUsed": "claude-opus-4-8",
"processingTimeMs": 121540
}Rate Limits
Generation endpoints run expensive multi-pass AI pipelines, so v1 requests share a single per-key bucket of 50 requests per hour across all five endpoints (or your key's configured hourly / daily / monthly limits, whichever is lower). The limit is enforced per authenticated key, so it follows the key — not the header style or source IP. Repeated failed authentication attempts from one IP are rate-limited separately, and platform-level protection additionally limits each source IP to roughly 100 API requests per minute.
When a limit is exceeded the API returns 429 with a Retry-After header (seconds) — wait at least that long before retrying:
{
"error": "Too many requests",
"message": "Rate limit exceeded. Please try again later.",
"retryAfter": 1740
}Need higher throughput for a production integration? Contact us via support to discuss partner limits.
Errors
Every error response uses one JSON envelope: { "error": "...", "message": "..." }. The error field is a stable category; message explains what to fix.
| Status | error | Meaning |
|---|---|---|
| 400 | Invalid request | Malformed JSON, or a missing / invalid field. The message names the field. |
| 401 | Unauthorized | Missing, malformed, expired, or inactive API key. |
| 403 | Insufficient scope | Valid key, but it does not carry the scope the endpoint requires, the owning account lacks API access, or an IP / user-agent restriction on the key blocked the request. |
| 422 | PII scrub incomplete | Generation was blocked because detected identifiers in the request could not be fully tokenized. Supply the party names in their fields (or remove stray identifiers) and retry. |
| 429 | Too many requests | Rate limit exceeded. Honor the Retry-After header before retrying. |
| 500 | Internal error | Unexpected failure. Safe to retry. |