Error Codes
Every error response from the Qpher API includes a machine-readable error_code that you can use for programmatic error handling, along with a human-readable message.
Error Response Format
All errors follow a consistent structure:
{
"error": {
"error_code": "ERR_KEM_001",
"message": "Invalid encryption request"
},
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-01-15T10:30:00.000Z"
}
| Field | Type | Description |
|---|---|---|
error.error_code | string | Stable, machine-readable code. Format: ERR_<CATEGORY>_###. |
error.message | string | Human-readable description. Do not parse this programmatically — it may change. |
request_id | string (UUID) | Unique request identifier for tracing and support. |
timestamp | string (ISO 8601) | UTC time when the error occurred. |
Error Code Naming Convention
Error codes follow the pattern ERR_<CATEGORY>_<NUMBER>:
| Category | Typical HTTP Status | Domain |
|---|---|---|
AUTH | 401 | Authentication failures |
INVALID | 400, 422 | Input validation errors |
FORBIDDEN | 403 | Authorization denied |
NOT_FOUND | 404 | Resource not found |
CONFLICT | 409 | Duplicate or concurrent operations |
RATE_LIMIT | 429 | Rate limit exceeded |
POLICY | 403 | Policy engine denied request |
KEM | 400, 500 | KEM encryption/decryption errors |
KMS | 400, 404, 409 | Key management errors |
SIG | 400 | Signature sign/verify errors |
CRYPTO | 500 | Internal cryptographic errors |
INTERNAL | 500 | Unexpected internal errors |
BAD_GATEWAY | 502 | Downstream service failure |
SERVICE | 503 | Service unavailable |
TIMEOUT | 504 | Request timeout |
Complete Error Code Reference
Use the search box to filter by error code or description, or select a category from the dropdown.
| Code | HTTP | Category | Description | Resolution |
|---|---|---|---|---|
| ERR_AUTH_001 | 401 | Authentication | Missing or invalid API key | Include a valid API key in the X-API-Key header |
| ERR_INVALID_001 | 400 | Validation | Invalid request body | Check request body matches the expected schema |
| ERR_INVALID_003 | 422 | Validation | Unprocessable entity | Verify field values match expected types and ranges |
| ERR_FORBIDDEN_001 | 403 | Authorization | Operation not allowed for plan | Upgrade your plan or reduce usage |
| ERR_NOT_FOUND_001 | 404 | Not Found | Resource not found | Verify the resource ID or key version exists |
| ERR_CONFLICT_001 | 409 | Conflict | Resource already exists | Use a unique identifier or update the existing resource |
| ERR_RATE_LIMIT_001 | 429 | Rate Limit | Rate limit exceeded | Wait and retry with exponential backoff |
| ERR_POLICY_001 | 403 | Policy | Policy engine denied request | Check policy configuration for your tenant |
| ERR_KEM_001 | 400 | KEM | Invalid encryption request | Ensure plaintext is base64-encoded and key_version is valid |
| ERR_KEM_002 | 400 | KEM | Invalid decryption request | Use the same key_version that was used for encryption |
| ERR_KEM_003 | 500 | KEM | Encryption operation failed | Retry the request; contact support if persistent |
| ERR_KMS_001 | 400 | KMS | Invalid key operation request | Use supported algorithms: Kyber768, Dilithium3 |
| ERR_KMS_002 | 404 | KMS | Key not found | List keys to find available versions |
| ERR_KMS_003 | 409 | KMS | Key version conflict | Wait for the current rotation to complete |
| ERR_SIG_001 | 400 | Signature | Invalid sign request | Ensure message is base64-encoded and key is active |
| ERR_SIG_002 | 400 | Signature | Invalid verify request | Provide all required fields for verification |
| ERR_CRYPTO_001 | 500 | Crypto | Cryptographic operation failed | Retry the request; contact support if persistent |
| ERR_INTERNAL_001 | 500 | Internal | Internal server error | Retry with backoff; contact support with request_id |
| ERR_BAD_GATEWAY_001 | 502 | Gateway | Downstream service error | Retry; check status.qpher.ai for service health |
| ERR_SERVICE_001 | 503 | Service | Service unavailable (fail-closed) | Retry after a short delay; check status page |
| ERR_TIMEOUT_001 | 504 | Timeout | Gateway timeout | Retry with a simpler request or contact support |
Using Request IDs for Debugging
Every response — success or error — includes a request_id. This ID traces the request through the entire Qpher infrastructure and is essential for debugging.
Store the request_id from every API response in your application logs. When something goes wrong, this is the fastest way to get help from the Qpher support team.
Best practices
-
Log every request ID on your side, even for successful responses. This creates an audit trail you can cross-reference later.
-
Include the request ID in support tickets. When contacting Qpher support, provide the
request_idand the approximate timestamp. This allows the team to locate the exact request in the distributed tracing system. -
Send your own request ID by including the
X-Request-IDheader. The API will use your value instead of generating one, making it easier to correlate requests across your own systems:
curl -X POST https://api.qpher.ai/api/v1/kem/encrypt \
-H "X-API-Key: qph_your_key_here" \
-H "X-Request-ID: my-trace-id-12345" \
-H "Content-Type: application/json" \
-d '{"plaintext": "SGVsbG8=", "key_version": 1}'
Handling Errors in Your Application
Retry strategy
Not all errors should be retried. Use this guide:
| Error Category | Retry? | Strategy |
|---|---|---|
ERR_AUTH_* | No | Fix your API key or credentials. |
ERR_INVALID_* | No | Fix the request payload. |
ERR_FORBIDDEN_* | No | Check plan tier or policy configuration. |
ERR_NOT_FOUND_* | No | Verify the resource exists. |
ERR_CONFLICT_* | Maybe | Wait briefly, then retry if the conflict is transient. |
ERR_RATE_LIMIT_* | Yes | Wait for Retry-After seconds, then retry. |
ERR_POLICY_* | No | Check your tenant's policy configuration. |
ERR_KEM_* / ERR_SIG_* | Depends | 400 errors: fix input. 500 errors: retry with backoff. |
ERR_KMS_* | Depends | 400/404: fix input. 409: wait and retry. |
ERR_CRYPTO_* | Yes | Retry with exponential backoff. |
ERR_INTERNAL_* | Yes | Retry with exponential backoff. |
ERR_BAD_GATEWAY_* | Yes | Retry after a short delay. |
ERR_SERVICE_* | Yes | Retry after a short delay. Check status.qpher.ai. |
ERR_TIMEOUT_* | Yes | Retry with a simpler request or increased patience. |
Exponential backoff example
import time
import requests
def call_with_retry(url, headers, json_body, max_retries=3):
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=json_body)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(retry_after)
continue
if response.status_code >= 500:
time.sleep(2 ** attempt)
continue
return response
raise Exception(f"Failed after {max_retries} retries")
For real-time service health, check status.qpher.ai. Subscribe to incident updates to get notified of outages before they affect your application.