ApikeeApikeeDocs
REST API

Validating a key

The endpoint the SDKs call for every guarded request, and the eight checks behind it.

POST /api/v1/client/{client_uuid}?project_env=…&method_path=…

This is what the SDKs call in server mode. You rarely call it yourself — but knowing what it does explains every 401, 403 and 429 your consumers see.

Request

{
  "name": "list-users",
  "timestamp": "2026-09-16T10:00:00.000Z",
  "headers": {
    "x-api-key": "apikee_…",
    "x-forwarded-for": "203.0.113.9"
  },
  "payload": { "any": "context to log" }
}
FieldRequiredMeaning
name✓The key's name, for the log entry
timestamp✓When your service received the request, ISO 8601 with offset
headers✓Incoming headers. The key is read from here; the caller IP comes from x-forwarded-for
payloadAnything extra you want logged

The eight checks

They run cheapest-first and stop at the first failure. Every outcome — accepted or rejected — is written to the request log with credentials redacted.

#CheckFailure
1Timestamp is sane (not in the future)401 STALE_REQUEST
2Key format, HMAC signature, embedded expiry401 (400 if malformed)
3Key exists, is enabled, not expired in the database401
4Signed claims match the stored row; key belongs to this client401
5Owning client is active403
6Caller IP is in the client's allow-list403
7Request signature, when the environment has a public key401
8Template rate limits429 + Retry-After

Steps 1–2 are exactly what your SDK already did locally, repeated because the platform does not trust the caller's word for it. Steps 3 onward are the things local mode cannot know.

Why the order matters

Rate limiting is last on purpose. An invalid or revoked key is rejected before it can consume any of a tenant's budget, so someone brute-forcing keys against one of your customers cannot exhaust that customer's quota as a side effect.

Check 4 is the one people forget: a key that is individually valid but belongs to a different client fails here. Keys are not transferable between clients.

Request signing

For environments needing more than a bearer credential, set a PEM public key on the environment. The SDK then signs

{clientId}|{method_path}|{timestamp}

with the matching private key and sends it as x-apikee-signature (base64url). Signed requests are additionally held to a five-minute replay window.

Configure the key with setPublicKey. In the SDKs, supply privateKey.

Doing it yourself

curl -X POST \
  "https://apikee.example.com/api/v1/client/0b7f…?project_env=payments-api-production&method_path=GET%3A%3A%3A%2Fv1%2Fusers" \
  -H "x-api-key: apikee_..." \
  -H "content-type: application/json" \
  -d '{
    "name": "list-users",
    "timestamp": "2026-09-16T10:00:00.000Z",
    "headers": { "x-api-key": "apikee_...", "x-forwarded-for": "203.0.113.9" }
  }'

If you are writing an SDK for a language Apikee does not cover, verify the signature locally first and call this endpoint asynchronously — off the response path. That is what the four official SDKs do, and it is why a slow or unreachable platform does not slow your API down.

On this page