Party Profile | API Reference Guide | CourtMesh
    Skip to main content

    Party profile (background check)

    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.

    Party profile endpoints
    Start a profile, then read it. Metered by what the call used.

    What one call returns

    • Identities. Everything POST /party/screen finds is grouped by the evidence the cases share: a known person you gave recorded as a relation or as a middle name, and the same statutory identifier. A case naming a different relation is kept apart, and cases matched on name alone form one unconfirmed group that is never merged into a confirmed identity. Each identity carries a confidence band and a why list of plain sentences.
    • The primary identity. The strongest identity at or above 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.
    • Possible other people. Every other identity is listed under 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.
    • Each case carries its court (and bench for tribunals), case type, title, party role, status, next hearing date, decision date, a confidence band, 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. 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.
    POST

    /party/profile

    Metered, minimum 100 creditsIdempotency-KeyNot on Free tier

    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

    FieldTypeRequiredNotes
    namestringYesThe person or company to profile. Same rules as POST /party/screen.
    entityType"person" | "company"YesWhich identity rules apply. An individual is never confirmed on name alone.
    purposeenumYesWhy you are checking this party: kyc, bgv, due_diligence, litigation, research or compliance. Required under DPDP, as on POST /party/screen.
    knownPersonsstring[]NoFather 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.
    aliasesstring[]NoOther spellings or former names, searched alongside name.
    identifiersobjectNocin, gstin, pan, llpin. An identifier you give stays decisive.
    addressobjectNocity, 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"Nofull: 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.
    maxCreditsintegerNoFull mode only. The most this request may spend, 100 to 5000. Default 1000, or your balance if that is lower. The work stops there.
    webhookobjectNoFull 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"NoThe 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.
    includestring[]NoSections 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.
    allowUnconfirmedPrimarybooleanNoDefault 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.
    adjudicatebooleanNoDefault 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"
      }'

    Full mode

    With mode: "full" the call puts correctness ahead of speed. It answers 202 at once and the work runs behind it in this order:

    1. The same search and grouping as quick mode, with the case records and documents listings read.
    2. Every case of the primary identity (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.
    3. Every case of that identity is analysed from the documents this API may show for it, pending cases first, then newest first. A current analysis is reused; one older than 180 days or older than the newest document is written again. A case with no open document gets 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.
    4. One analysis of the person or company over all of those cases (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.
    • Only the primary identity is refreshed and analysed. Identities under possibleOtherPeople are listed as in quick mode.
    • The set of cases is fixed when the search stage ends. If the court refresh changes which cases match, the cases already found are still the ones analysed and notes says so.
    • If the API key that made the request is revoked or expires while it runs, the request stops and no further stage is charged.
    • Read the outcome from GET /party/profile/:requestId, or pass webhook to be told when it ends. A finished result can be read for 30 days.
    • An account may have at most 3 full profiles queued or running at once.
    • Where full mode is not available, 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" }
        }
      }'
    GET

    /party/profile/:requestId

    Free

    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.

    • Poll every few seconds to a minute. Most requests finish within minutes; waiting for court refreshes, with their retries, can take up to 25 minutes.
    • A request can be read only by the account that made it; any other id is the same 404 NOT_FOUND.
    • The result can be read for 30 days after the request ends; after that resultExpired is true and only the status, progress and credits remain.
    • A case withheld since the request ran is left out, and the party analysis with it. Send the request again for a current profile.
    curl https://research.courtmesh.ai/api/v1/prod/party/profile/ppr_3f9c2a7d1b8e4c6fa0d5e2b7c9a1f4d8 \
      -H "X-API-Key: cm-YOUR_KEY_HERE"

    Completion webhook

    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.

    • The 202 answer carries webhook.signingSecret once. Store it.
    • Every delivery is signed: 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.
    • Dedupe on the CourtMesh-Event-Id header (the requestId): delivery is at least once.
    • A delivery that does not get a 2xx is retried with growing gaps for about two days. The url must be a public https address; redirects are not followed.
    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.

    Pricing

    • Every profile costs at least 100 credits, even when no case is found. Above that it is priced by what the call actually used, at the existing rates.
    • Quick mode: 1 credit per search call (name, alias, known person and identifier queries, at most 24), 1 per case record read and 1 per documents listing that offers a document (at most 100 cases are enriched), plus the optional AI review by its tokens, held at 80 credits. One quick call never costs more than 304 credits; a run that would exceed it stops enriching and 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.
    • Full mode: charged stage by stage as each finishes: the search stage at the quick mode rates, 1 credit per court refresh started (retries are free), each new, rewritten or short case analysis by the tokens it used, each reused analysis at the price of reading a stored analysis, and the party analysis by its tokens. An analysis that fails or is cut off is not charged. Accepting the request and reading its status cost nothing.
    • A full mode request never spends more than 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.
    • Up to 60 credits of maxCredits (never more than 30 percent of it) are kept back from the case analyses so the party analysis can still be written.
    • A refused or failed call charges nothing. If a charge cannot be confirmed, no further paid work is done: on the first charge the request fails with SETTLEMENT_UNCONFIRMED and returns no profile; later on it finishes with what it has and notes says so.
    • Not available on the Free tier.

    Errors

    • HTTP 400: validation failed, for example a missing purpose or a maxCredits outside its range.
    • HTTP 401: no key, or an unknown or inactive key.
    • HTTP 402: the balance cannot cover the most this request could cost (quick mode) or maxCredits (full mode); nothing runs. Also returned with no profile when the wallet refuses the debit as a quick call settles.
    • HTTP 403: API_NOT_AVAILABLE_ON_TRIAL, API_ENTERPRISE_ONLY, or API_TIER_NOT_ALLOWED on the Free tier (use POST /party/screen instead).
    • HTTP 404 on the status call: NOT_FOUND, no such request for your account.
    • HTTP 409: an Idempotency-Key conflict.
    • HTTP 429: 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.
    • HTTP 502: PARTY_SCREEN_SEARCH_DEGRADED, the search produced nothing usable; nothing is charged.
    • HTTP 503: 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.

    DPDP and retention

    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.