How to authenticate a request, how key rotation and revocation work, and how organisations restrict calls by IP.
Every endpoint below requires a key, with two exceptions: GET /health, which is unauthenticated so you can probe availability from your own monitoring, and GET /coverage, which is public on purpose so you can check our corpus numbers before you ever create a key. The middleware reads X-API-Key first and falls back to a bearer token, so either of these is fine.
# Either header authenticates. Pick one, do not send both.
curl "https://research.courtmesh.ai/api/v1/prod/judges/search?q=nariman" \
-H "X-API-Key: cm-YOUR_KEY_HERE"
curl "https://research.courtmesh.ai/api/v1/prod/judges/search?q=nariman" \
-H "Authorization: Bearer cm-YOUR_KEY_HERE"The key is checked for shape before it is looked up, then for existence, then for whether the account is entitled to call the API at all. Suspended users and deactivated organisations are refused at this layer too, and if the identity service cannot confirm your standing the request fails closed with HTTP 503 rather than being let through.
// 401, no key on the request
{ "error": "API key required. Provide it in X-API-Key header or Authorization: Bearer <key>" }
// 401, the key does not match cm- or vv- followed by 32 then 4 characters
{ "error": "Invalid API key format" }
// 401, well formed but unknown, or the key was deactivated
{ "error": "Invalid API key" }
{ "error": "API key is deactivated" }
// 403, the account is not on Enterprise (this is the common one today)
{
"error": "API access is only available on the Enterprise plan. Upgrade to an Enterprise plan for API access.",
"callsToday": 0,
"maxAllowed": 0
}
// 403, the account is Enterprise but has never created a key
{
"error": "No API keys found. Please create an API key first.",
"callsToday": 0,
"maxAllowed": -1
}POST /api/settings/api-keys/:id/rotate mints a replacement key with the same name (suffixed "(rotated)") and the same organisation, returns its plaintext once, and gives the OLD key a 24 hour grace window rather than revoking it outright: the old key keeps authenticating for 24 hours, so a process still holding it in an environment variable does not break the instant you rotate. After the window the old key is expired and answers with HTTP 401 API_KEY_EXPIRED. This is a dashboard action, called with your logged in session, not with an API key.
# Dashboard session, not an API key: called from the API Keys settings
# screen, or with your logged in session cookie.
curl -X POST https://research.courtmesh.ai/api/settings/api-keys/<keyId>/rotate \
-H "Cookie: <your dashboard session>"{
"message": "API key rotated successfully",
"apiKey": {
"id": "68f2b0...",
"name": "Production (rotated)",
"fullKey": "cm-....................................-abcd",
"displayKey": "cm-...-abcd"
},
"rotatedFrom": {
"id": "68a110...",
"expiresAt": "2026-09-19T06:00:00.000Z"
}
}Deleting a key marks it inactive rather than erasing its row: it stops authenticating immediately (HTTP 401 API_KEY_REVOKED on its next use) while its call history stays intact for your audit log and usage reporting. An ORG_ADMIN can rotate or revoke any key that belongs to their organisation, not only their own; every other caller can only manage their own keys.
An organisation can restrict its API keys to a list of IP addresses or CIDR ranges. It is off by default for every organisation; enable it by contacting support@courtmesh.ai with the ranges to allow. Once enabled, a request from an address not on the list is refused with HTTP 403 IP_NOT_ALLOWED, for every key that organisation owns.
// HTTP 403, strict mode organisation, calling IP not on the allowlist
{
"success": false,
"code": "IP_NOT_ALLOWED",
"message": "Access denied. Your IP address is not whitelisted for this organization.",
"error": "Access denied. Your IP address is not whitelisted for this organization.",
"requestId": "req_5d10ee31"
}The allowlist is cached for up to a minute per organisation to avoid a database round trip on every keyed request, so a change can take up to a minute to take effect. An organisation can additionally opt into strict mode: if a later lookup for its allowlist errors, a strict organisation fails CLOSED (the request is refused) rather than open. An organisation that has never had a successful lookup, or has not opted into strict mode, fails open on a lookup error so a transient database issue does not lock out every integration at once. Ask support for strict mode if failing closed is the right trade-off for your compliance posture.