Errors | API Reference Guide | CourtMesh
    Skip to main content

    Errors and idempotency

    One response envelope, one error shape, a request id on every response, and idempotency keys for safe retries on credit charging endpoints.

    Response format
    One envelope for handlers, a different shape from the middleware.
    // Success, every endpoint except /health
    {
      "success": true,
      "data": { },          // object or array, endpoint specific
      "meta": { },          // optional: query echo, responseTime, notes
      "pagination": { }     // search endpoints only
    }
    
    // Error raised by a route handler
    {
      "success": false,
      "error": "Case not found"
    }
    
    // Validation error, HTTP 400
    {
      "success": false,
      "error": "Validation failed. Please check your request and try again.",
      "details": ["query: Search query cannot be empty. Please provide a search term."]
    }
    
    // The auth and rate limit layers answer with a bare object and no success field
    {
      "error": "Rate limit exceeded",
      "message": "Too many requests. Maximum 10 requests per minute allowed.",
      "retryAfter": 37,
      "resetTime": "2026-08-10T09:41:12.884Z"
    }

    Two exceptions to check for in client code. First, GET /health returns a flat object with no data key. Second, POST /search/cases/semantic writes its 200 status line before it starts work, in order to hold the connection open through a slow vector search. If that work then fails you receive HTTP 200 with success: false in the body. Branch on the body, not only on the status code.

    Error codes
    Every refusal this API can return, and what it means.

    Every error body on this API has the same shape: { success: false, code, message, error, requestId }. error mirrors message for backward compatibility with older bare { error } bodies. Every response, success or failure, also carries an X-Request-Id header you can hand to support to join a report to a server side log line.

    CourtMesh API error codes
    CodeHTTP statusMeaning
    API_KEY_MISSING401No X-API-Key header or Authorization: Bearer token was sent.
    API_KEY_INVALID_FORMAT401The key does not match the prefix-32characters-4characters shape.
    API_KEY_INVALID401The key does not match any key on record.
    API_KEY_REVOKED401The key was deleted or rotated by its owner.
    API_KEY_EXPIRED401The key carries an expiry date that has passed.
    ORGANIZATION_DEACTIVATED403The organisation that owns this key is deactivated.
    ACCOUNT_SUSPENDED403The IAM standing check found the account behind this key suspended.
    BILLING_INACTIVE403The IAM standing check found billing inactive for this account.
    ACCOUNT_NOT_FOUND403The IAM standing check found no account behind this key any more.
    ACCOUNT_STANDING_UNAVAILABLE503The identity service could not verify account standing; the endpoint fails closed rather than serving an unauthorised result.
    IP_NOT_ALLOWED403Your organisation has IP allowlisting enabled and the calling address is not on the list.
    API_ENTERPRISE_ONLY403Flag off (today's production default): the Enterprise only gate refused this plan.
    API_NOT_AVAILABLE_ON_TRIAL403Trial accounts have no API access at all.
    API_TIER_NOT_ALLOWED403Free tier calling an AI analysis endpoint (analyze, analyze-consolidated, or the stored analysis read).
    SEMANTIC_NOT_ALLOWED403Free tier calling POST /search/cases/semantic.
    LIVE_FETCH_NOT_ALLOWED403Free tier sending refresh: true to POST /request-timeline.
    LIVE_FETCH_LIMIT_REACHED429Tier's daily live fetch cap already used.
    PARTY_SCREEN_LIMIT_REACHED403Free tier's monthly litigation check cap already used.
    DISTINCT_NAMES_LIMIT_REACHED429Tier's per minute or per day distinct screened name cap exceeded.
    DISTINCT_CASES_LIMIT_REACHED429Tier's distinct case detail fetches per day cap reached.
    PDF_LIMIT_REACHED429Tier's PDF calls per month cap reached.
    CONCURRENT_ANALYSIS_LIMIT429Tier's concurrent analyze job cap is saturated; Retry-After: 30.
    TOO_MANY_KEYS_FROM_IP429One client IP has used more distinct API keys than the daily cap allows.
    RATE_LIMITED429The per key or per IP requests-per-minute ceiling was exceeded.
    INSUFFICIENT_API_CREDITS402Balance does not cover this call's credit price; nothing is charged.
    PAGE_LIMIT_EXCEEDED400limit is above your tier's maxPageSize.
    PAGINATION_DEPTH_EXCEEDED400page times limit, or the depth carried in a cursor, exceeds your tier's cap.
    CURSOR_INVALID400The cursor failed to decode, or was issued for a different query or filters.
    REMOTE_FETCH_NOT_ALLOWED403An analyze allowRemoteFetch: true call targeted a host outside the allowlist.
    EMAIL_NOT_VERIFIED403Free tier key creation requires a verified email address.
    API_KEY_LIMIT_REACHED403Tier's live API key count is already at the tier's cap.
    VALIDATION_ERROR400The request body failed schema validation; details name the offending field.
    CASE_NOT_FOUND404The case id in the path does not resolve to a stored case.
    PDF_NOT_STORED404The case exists but carries no stored PDF; the body carries a hint.
    PARTY_SCREEN_SEARCH_DEGRADED502The underlying case search errored and returned no usable results; not charged, carries a Retry-After header.
    CASE_RESTRICTED403The case is withheld under CourtMesh's case removal policy and is not returned by any endpoint.
    JOB_NOT_FOUND404The timeline request id in the path does not exist, or its job record has expired.
    MALFORMED_JSON400The request body could not be parsed as JSON.
    PAYLOAD_TOO_LARGE413The request body is larger than the 256kb limit.
    UPSTREAM_TIMEOUT408A dependency (OpenSearch, the embedding service, or a live court portal fetch) did not answer in time.
    UPSTREAM_UNAVAILABLE502A dependency failed to respond at all.
    INTERNAL_ERROR500An unhandled server error. Retry, and quote the X-Request-Id if it recurs.
    IDEMPOTENCY_KEY_INVALID400The Idempotency-Key header is present but is not 1 to 128 characters of A-Z, a-z, 0-9, underscore, period or hyphen.
    IDEMPOTENCY_KEY_REUSED409The same Idempotency-Key was sent with a request body that does not match the first call that used it.
    IDEMPOTENCY_IN_PROGRESS409A call carrying this Idempotency-Key is still being processed. Wait for it to finish rather than sending a third copy.
    Request ids and support
    Every response carries one. Put it in your support request and we can find the exact server side log line.

    Every response this API sends, success or failure, carries an X-Request-Id header. Failures also echo it in the body, as requestId, alongside the same one error shape used everywhere on this API: { success: false, code, message, error, requestId }. code is the stable machine readable string to branch your error handling on; error mirrors message for backward compatibility with older bare { error } bodies.

    When you write to us about a call

    Include the X-Request-Id (or the body's requestId), the endpoint, and roughly when the call was made. That is what lets us join your report to the actual server side log line rather than asking you to reproduce it. Contact support@courtmesh.ai, or your Enterprise named contact if you have one. There is no published uptime, latency or freshness SLA today; see API security for exactly what is and is not in place.

    Idempotency
    Safe retries on every endpoint that charges credits for real work.

    Send an Idempotency-Key request header on POST /party/screen, POST /party/screen/batch, POST /cases/:id/analyze, POST /cases/:id/analyze-consolidated or POST /request-timeline and a dropped connection, a client side timeout, or a retry loop can never charge you twice for the same call. The header is 1 to 128 characters of letters, digits, underscore, period or hyphen, scoped to your API key, and honoured for 24 hours from the first call that used it.

    • Same key, identical request body: the stored response from the first call is replayed verbatim, with an added Idempotency-Replayed: true header, and nothing is charged a second time.
    • Same key, a different request body: refused with HTTP 409 IDEMPOTENCY_KEY_REUSED, so a bug that reuses a key by accident cannot silently serve the wrong stored answer.
    • Same key, sent again while the first call is still in flight: refused with HTTP 409 IDEMPOTENCY_IN_PROGRESS rather than doing the work twice concurrently.
    • A header that does not match the allowed shape: refused with HTTP 400 IDEMPOTENCY_KEY_INVALID before the request body is even read.
    • An error response is never stored against the key: a call that fails is free to retry with the same Idempotency-Key and actually run again.
    • The header is optional. Omit it and every call runs independently, exactly as today.
    curl -X POST https://research.courtmesh.ai/api/v1/prod/party/screen \
      -H "X-API-Key: cm-YOUR_KEY_HERE" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: signup-flow-user-88213-kyc-check" \
      -d '{ "name": "Rambaksh Enterprises", "entityType": "company", "purpose": "kyc" }'