One call finds the Indian court cases of a person or company, groups namesakes into separate identities, and returns the cases that belong to the one you asked about, with their status, acts, documents and links. Full mode also reads each case again from its court and analyses every case and the party.
confidence band and a why list of plain sentences.minConfidence that the evidence ties to the subject is returned in full under identities, with its cases and a code built profile (counts by court type and role, pending and disposed, categories, flags and a plain paragraph). For a company, a name only group may be the primary only when its name is exactly the one you gave (or an alias); a generic company name such as "Sharma Traders" is never the primary on name alone. When no identity qualifies, identities is empty and verdict is no_confirmed_identity.possibleOtherPeople with its case ids, the reasons, and the basic facts of each case (court, case type, title, parties, role, filing and decision dates) at no extra cost, so you can judge them yourself. GET /cases/:id gives a case in full.matchReasons in plain words, acts and FIR as the court record states them, and the documents held for it with links to the text and the PDF.notes is always present: plain sentences on anything left out, empty when nothing was. Restricted and masked records are dropped before anything is built, and a note that some records are withheld is the only disclosure.Takes every field of POST /party/screen, with the same validation and caps, plus the fields below. In quick mode the answer is the profile itself (HTTP 200). In full mode the answer is HTTP 202 with a requestId, and the profile is read later.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | Yes | The person or company to profile. Same rules as POST /party/screen. |
| entityType | "person" | "company" | Yes | Which identity rules apply. An individual is never confirmed on name alone. |
| purpose | enum | Yes | Why you are checking this party: kyc, bgv, due_diligence, litigation, research or compliance. Required under DPDP, as on POST /party/screen. |
| knownPersons | string[] | No | Father or other known persons. A known person recorded as a relation (S/o, D/o, W/o) or as a middle name is the main evidence for grouping a person's cases. |
| aliases | string[] | No | Other spellings or former names, searched alongside name. |
| identifiers | object | No | cin, gstin, pan, llpin. An identifier you give stays decisive. |
| address | object | No | city, state, stateCode. A case in the state you gave is noted in why, but is never joined to a group on that alone. |
| mode | "full" | "quick" | No | full: answer 202 at once, then refresh, analyse and write a party analysis; read the outcome later. quick: the profile from what is held, in one answer. Defaults to full where full mode is available, else quick. |
| maxCredits | integer | No | Full mode only. The most this request may spend, 100 to 5000. Default 1000, or your balance if that is lower. The work stops there. |
| webhook | object | No | Full mode only. { url, headers }: url is a public https address with no credentials in it; headers (at most 20, each value at most 1024 printable characters) are sent with the notice, for example your own Authorization. Content-Type, Host and any CourtMesh- header are refused. |
| minConfidence | "possible" | "probable" | "confirmed" | No | The lowest band an identity needs to be reported as the primary, and the lowest band a case of the primary needs to be listed, enriched and billed. Default probable for a person, possible for a company. |
| include | string[] | No | Sections to build: documents, acts, fir, status, summary. Omitted means all. documents and status need the case records read (and are what the per case reads bill for); acts, fir and summary cost nothing extra. |
| allowUnconfirmedPrimary | boolean | No | Default false. For a person, let a group matched on name alone be reported as the primary. Off by default: such a group is listed under possibleOtherPeople and the verdict is no_confirmed_identity. |
| adjudicate | boolean | No | Default false. Runs the same optional AI review POST /party/screen offers, on the search stage, held at the 80 credit allowance. |
curl -X POST https://research.courtmesh.ai/api/v1/prod/party/profile \
-H "X-API-Key: cm-YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"name": "Sample Person Name",
"entityType": "person",
"knownPersons": ["Sample Father Name"],
"address": { "state": "Karnataka", "city": "Bengaluru" },
"purpose": "bgv",
"mode": "quick"
}'With mode: "full" the call puts correctness ahead of speed. It answers 202 at once and the work runs behind it in this order:
identities[0]) that has a CNR is read again from its court, so status, hearings and orders are current. The request waits up to 25 minutes for these refreshes, then carries on with what arrived. When a court refuses or a refresh fails, the case is asked again after a wait (about 1, 2, 4 and 5 minutes, up to 4 retries). Each case is charged one refresh at most; a retry is never charged. Each case reports courtRefresh.status, and courtRefresh.attempts when more than one try was made.analysis.status: "not_done" with a plain reason and is not charged. When maxCredits cannot cover a full analysis of every case, the cases at the front get a full analysis and the rest get a short one (analysis.status: "short": summary, outcome and issues, about 5 credits each), so every case is covered. Once every case has an analysis, short ones are redone in full, front of the order first, while what is left of maxCredits covers a full analysis; a case keeps its short analysis unless the full one comes back. A short analysis is written for this request only.partyAnalysis): an overview, the litigation pattern, key risks, notable cases and open matters, each tied to case ids from this profile. Being a party to a case is not a finding against anyone.possibleOtherPeople are listed as in quick mode.notes says so.GET /party/profile/:requestId, or pass webhook to be told when it ends. A finished result can be read for 30 days.mode: "full" is a 503 with code PARTY_PROFILE_FULL_UNAVAILABLE and nothing is charged; send mode: "quick".curl -X POST https://research.courtmesh.ai/api/v1/prod/party/profile \
-H "X-API-Key: cm-YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-check-2026-11-11" \
-d '{
"name": "Acme Infrastructure Private Limited",
"entityType": "company",
"identifiers": { "cin": "U45200DL2010PTC123456" },
"purpose": "due_diligence",
"mode": "full",
"maxCredits": 1000,
"webhook": {
"url": "https://hooks.example.com/courtmesh",
"headers": { "Authorization": "Bearer your-own-token" }
}
}'Where a full mode request stands: status (queued, running, completed, failed), the stage while running, progress counts, and credits charged so far with a breakdown. Once status is completed, result holds the profile: the same shape as quick mode, with courtRefresh and analysis on every case of the primary identity, the partyAnalysis, and notes. A failed request carries failure with a code and a plain message; what it was charged before failing stays in credits.
NOT_FOUND.resultExpired is true and only the status, progress and credits remain.curl https://research.courtmesh.ai/api/v1/prod/party/profile/ppr_3f9c2a7d1b8e4c6fa0d5e2b7c9a1f4d8 \
-H "X-API-Key: cm-YOUR_KEY_HERE"When a full mode request ends, completed or failed, one POST is sent to webhook.url with a small JSON notice: event (party.profile.completed or party.profile.failed), requestId, status, creditsCharged, resultUrl, finishedAt and, on failure, failure. The profile itself is never in the notice: read it from resultUrl with your API key.
webhook.signingSecret once. Store it.CourtMesh-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">. Reject a delivery when the difference between now and t is over 300 seconds.CourtMesh-Event-Id header (the requestId): delivery is at least once.POST https://hooks.example.com/courtmesh
Content-Type: application/json
CourtMesh-Event-Id: ppr_3f9c2a7d1b8e4c6fa0d5e2b7c9a1f4d8
CourtMesh-Signature: t=1794390097,v1=5d41402abc4b2a76b9719d911017c592...
Authorization: Bearer your-own-token
{
"event": "party.profile.completed",
"requestId": "ppr_3f9c2a7d1b8e4c6fa0d5e2b7c9a1f4d8",
"status": "completed",
"creditsCharged": 150,
"resultUrl": "/api/v1/prod/party/profile/ppr_3f9c2a7d1b8e4c6fa0d5e2b7c9a1f4d8",
"finishedAt": "2026-11-11T09:41:37.000Z"
}import crypto from "node:crypto";
// rawBody: the request body exactly as received, before JSON parsing.
function verifyCourtMeshSignature(rawBody, header, signingSecret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto
.createHmac("sha256", signingSecret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1 ?? "", "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Then dedupe on the CourtMesh-Event-Id header: delivery is at least once.Each case lists the documents held for it (the case document and its orders), each with documentId, kind, date and links. links.text is the path of the order text endpoint, relative to the API base and billed as that endpoint is, or null when the text is not offered. documentsNote is a plain sentence when the text and order PDFs of the case are not offered, and documentsMore: true means the case holds more documents than one listing carries.
links.pdf is a ready download URL, valid for one hour (links.pdfExpiresAt). It needs no API key, so it can be opened in a browser.DOWNLOAD_LINK_INVALID: read the profile again for fresh links (a stored full mode result mints new ones on every read).INSUFFICIENT_CREDITS (top up and open it again), 403 CASE_RESTRICTED or API_KEY_INACTIVE, 404 PDF_NOT_STORED, 409 DOWNLOAD_IN_PROGRESS (the same link is being opened already), 429 PDF_LIMIT_REACHED.notes says the answer may be incomplete. meta.creditsBreakdown itemises searches, caseRecords, documentLists, aiReview when charged, and minimumCharge when the call used less than the minimum.maxCredits (100 to 5000, default 1000, or your balance if that is lower). The balance must cover it (or the 100 credit minimum when you send none) when the request is accepted, else 402 and nothing runs. When the ceiling or the balance is reached the work stops, and notes says what was not done.maxCredits (never more than 30 percent of it) are kept back from the case analyses so the party analysis can still be written.SETTLEMENT_UNCONFIRMED and returns no profile; later on it finishes with what it has and notes says so.maxCredits (full mode); nothing runs. Also returned with no profile when the wallet refuses the debit as a quick call settles.API_NOT_AVAILABLE_ON_TRIAL, API_ENTERPRISE_ONLY, or API_TIER_NOT_ALLOWED on the Free tier (use POST /party/screen instead).NOT_FOUND, no such request for your account.RATE_LIMITED (the per minute limit shared with POST /party/screen), DISTINCT_NAMES_LIMIT_REACHED, or, in full mode, PARTY_PROFILE_TOO_MANY_ACTIVE when the account already has 3 full profiles queued or running. All set Retry-After.PARTY_SCREEN_SEARCH_DEGRADED, the search produced nothing usable; nothing is charged.PARTY_PROFILE_FULL_UNAVAILABLE (send mode: "quick"), ACCOUNT_STANDING_UNAVAILABLE or ENTITLEMENT_UNAVAILABLE (retry with backoff). A quick call whose debit outcome is unknown is also a 503 with no profile: check your usage before retrying.Same contract as POST /party/screen: purpose is required, and the name, aliases, known persons, identifiers and address are never stored in plain text by an API key request. A full mode request has to keep the subject and the result until they can be read: both are held encrypted and erased 30 days after the request ends, together with the list of cases, the webhook address, its headers and its signing secret. Only the status, the counts and the credits remain. A party can ask for a case to be withheld under the case removal policy.