Error Codes
v1
JSON over HTTPS
Bearer credentials or HMAC signatures
Error envelope
Every hard failure uses one shape:
{
"success": false,
"error": {
"code": "LICENSE_EXPIRED",
"message": "The license has expired.",
"details": { "expired_at": "2026-10-01T00:00:00+00:00" }
},
"meta": { "request_id": "req_9f2c1a7b4d6e" }
}
Always log the request_id. It is the only thing that ties a customer's report to the server's structured logs.
Status codes
| Status | Meaning |
|---|---|
| 200 | The call succeeded — including a refused license |
| 401 | Authentication failed |
| 403 | Authenticated but not entitled |
| 404 | No such product, endpoint or release |
| 422 | The request body failed validation |
| 426 | The SDK is too old for this endpoint |
| 429 | Rate limited; see Retry-After |
| 500 | Unexpected server fault |
A refused license is not an error status. license/validate and license/activate answer 200 with success: false and an error_code, because the transport succeeded and the licence did not. Handling this keeps customer software's error paths simple.
License codes
| Code | Meaning |
|---|---|
INVALID_LICENSE |
The key is not recognised |
LICENSE_EXPIRED |
Past expires_at |
LICENSE_SUSPENDED |
Temporarily disabled by an administrator |
LICENSE_REVOKED |
Permanently withdrawn |
LICENSE_BLOCKED |
Withheld, e.g. after a chargeback |
LICENSE_INACTIVE |
Still pending, or the installation is deactivated |
DEACTIVATION_NOT_ALLOWED |
Self-service deactivation is disabled |
Product codes
| Code | Meaning |
|---|---|
PRODUCT_NOT_FOUND |
No product with that code |
PRODUCT_MISMATCH |
The license belongs to a different product |
Domain and activation codes
| Code | Meaning |
|---|---|
DOMAIN_NOT_ALLOWED |
Not in the allowlist, or an environment the product forbids |
DOMAIN_BLOCKED |
Explicitly blocked for this license |
ACTIVATION_LIMIT_REACHED |
All activation slots are in use |
DOMAIN_LIMIT_REACHED |
All domain slots are in use |
INSTALLATION_NOT_FOUND |
No matching installation on this license |
IP_NOT_ALLOWED |
The caller's IP is not on the allowlist |
Authentication codes
| Code | Meaning |
|---|---|
INVALID_API_KEY |
Unknown, revoked or expired key |
INVALID_SIGNATURE |
HMAC mismatch, or the key requires signing |
REPLAY_DETECTED |
The nonce was already used |
STALE_REQUEST |
Timestamp outside the permitted window |
RATE_LIMIT_EXCEEDED |
Too many requests |
Distribution codes
| Code | Meaning |
|---|---|
DOWNLOAD_NOT_AUTHORIZED |
No valid entitlement for this package |
DOWNLOAD_UNAVAILABLE |
No published package for that version |
UPDATE_NOT_AVAILABLE |
Already on the latest release |
Platform codes
| Code | Meaning |
|---|---|
VALIDATION_FAILED |
The body failed validation |
HTTPS_REQUIRED |
The call did not arrive over TLS |
NOT_FOUND |
Unknown endpoint |
SERVER_ERROR |
Unexpected fault — quote the request id |
MAINTENANCE_MODE |
Temporarily offline |
SDK_TOO_OLD |
Upgrade the client SDK |
Certificate codes
Returned by the SDK when a cached certificate cannot be trusted:
| Code | Meaning |
|---|---|
INVALID_CERTIFICATE |
Malformed or wrong token type |
INVALID_SIGNATURE |
Signature does not verify |
UNKNOWN_SIGNING_KEY |
kid is not in the keyring — refresh it |
UNSUPPORTED_ALGORITHM |
Only ed25519 is accepted |
CERTIFICATE_NOT_YET_VALID |
Clock skew |
LICENSE_GRACE_EXPIRED |
Offline window closed; the server must be reachable |
UNKNOWN_SIGNING_KEY usually means the server rotated its signing key. Fetch the keyring again from /products/{code}/public-key — this is the supported recovery path and needs no new SDK release.
Page 5 of 8