Skip to main content

Authentication

Every request to the Qpher API must be authenticated. This page explains how authentication works, what happens when it fails, and how to keep your credentials secure.


API Key Authentication

The primary authentication method for the Qpher API is an API key passed in the X-API-Key header.

Authenticate with your API key
curl -X POST https://api.qpher.ai/api/v1/kem/encrypt \
-H "X-API-Key: qph_your_key_here" \
-H "Content-Type: application/json" \
-d '{"plaintext": "SGVsbG8gV29ybGQh", "key_version": 1}'
Header name

The header is X-API-Key (case-insensitive). Do not use Authorization: Bearer for API key authentication -- that pattern is reserved for JWT-based portal sessions.


The Auth Pipeline

When your request reaches the Qpher API Gateway, it passes through a 7-stage authentication and authorization pipeline before reaching the target service. Understanding this pipeline helps you diagnose errors and optimize performance.

Pipeline Stages

StageWhat HappensOn Failure
1. Skip CheckDetermines if the endpoint is public (/health, /metrics, /docs). Public endpoints bypass all remaining stages.--
2. ExtractReads the X-API-Key header from the request.401 ERR_AUTH_001
3. ResolveHashes the API key and looks up the associated tenant. Verifies the key is valid and active.401 ERR_AUTH_001
4. InjectAdds X-Tenant-ID, X-Request-ID, and X-API-Key-Version headers to the request for downstream services.--
5. Rate LimitChecks the tenant's request rate against their plan's limit using a Redis-backed sliding window.429 ERR_RATE_LIMIT_001
6. PolicyEvaluates the request against the tenant's access policy (plan restrictions, endpoint permissions, quota limits).403 ERR_FORBIDDEN_001
7. RouteForwards the authenticated, authorized request to the target microservice.502 or 504
Fail-closed by design

If the Policy Engine is unavailable, the gateway denies all requests (HTTP 503). Qpher never falls back to permissive mode. This fail-closed behavior ensures that a service outage cannot be exploited to bypass authorization.


Auth Error Codes

When authentication or authorization fails, the API returns a structured error response. Here are the error codes you may encounter:

HTTP StatusError CodeDescriptionCommon Cause
401ERR_AUTH_001Missing or invalid API keyNo X-API-Key header, or the key is revoked/expired
403ERR_FORBIDDEN_001Operation not allowedYour plan does not permit this operation, or a policy rule denied the request
403ERR_POLICY_001Policy engine denied requestTenant-specific policy restriction
429ERR_RATE_LIMIT_001Rate limit exceededToo many requests in the current time window
503ERR_SERVICE_001Service unavailable (fail-closed)The Policy Engine or a critical service is temporarily down

Error Response Format

All errors follow a consistent structure:

{
"error": {
"code": "ERR_AUTH_001",
"message": "Missing or invalid API key",
"details": "Provide a valid API key in the X-API-Key header"
},
"request_id": "d4e5f6a7-b8c9-0123-def0-123456789abc",
"timestamp": "2026-01-15T10:30:00Z"
}

The request_id is always present, even on error responses. Include it when contacting support to help us trace the issue.

Handling Rate Limits

When you receive a 429 response, implement exponential backoff:

Retry with exponential backoff
import time
import requests

def call_with_retry(url, headers, json_data, max_retries=3):
  for attempt in range(max_retries):
      response = requests.post(url, headers=headers, json=json_data)
      if response.status_code == 429:
          wait = 2 ** attempt  # 1s, 2s, 4s
          print(f"Rate limited. Retrying in {wait}s...")
          time.sleep(wait)
          continue
      return response
  raise Exception("Max retries exceeded")

Context Headers

After successful authentication, the gateway injects context headers that flow through the entire request chain. These are useful for debugging and tracing:

HeaderDescriptionExample
X-Tenant-IDYour tenant UUIDa1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Request-IDUnique request trace IDf6a7b8c9-d0e1-2345-6789-abcdef012345
X-API-Key-VersionVersion of the API key used3

You can pass your own X-Request-ID header in the request, and the gateway will propagate it. If you do not provide one, the gateway generates a UUID automatically. This is useful for correlating requests across your own logging infrastructure.


Security Best Practices

Store Keys in Environment Variables

Never hardcode API keys in your source code. Use environment variables or a secrets manager:

# Set the environment variable
export QPHER_API_KEY="qph_your_key_here"
import os

api_key = os.environ["QPHER_API_KEY"]

Never Commit Keys to Source Control

Add API keys to your .gitignore and use tools like git-secrets or pre-commit hooks to prevent accidental commits:

# .gitignore
.env
.env.local
*.key

Use Separate Keys Per Environment

Create distinct API keys for development, staging, and production. If a dev key is compromised, your production data remains safe.

Rotate Keys Regularly

Rotate your API keys periodically and immediately after any suspected compromise. Qpher supports zero-downtime rotation -- the new key becomes active while the old key remains valid for a brief transition window before automatic revocation.

curl -X POST https://api.qpher.ai/api/v1/tenants/{tenant_id}/api-keys/rotate \
-H "X-API-Key: qph_your_key_here"

Restrict Key Permissions

Use the Policy Engine to restrict what each API key can do. For example, a key used only for encryption should not have access to key management endpoints.

Revoked keys are permanent

When you rotate an API key, the old key is revoked after the transition window. Revoked keys cannot be reactivated. Ensure all systems are updated to use the new key before the window closes.


Portal Authentication (JWT)

The Qpher User Portal uses a separate authentication mechanism based on JSON Web Tokens (JWT) with httpOnly cookies.

How It Differs from API Key Auth

AspectAPI KeyPortal JWT
Used byYour application codeBrowser sessions on portal.qpher.ai
TransportX-API-Key headerhttpOnly cookie (automatic)
LifetimeUntil rotated or revokedAccess: 24 hours, Refresh: 7 days
ScopePQC operations + key managementDashboard, settings, billing

Dual-Auth Gateway

The API Gateway supports both authentication methods. It determines which method to use based on the presence of the X-API-Key header:

  • X-API-Key present -- API key authentication (for programmatic access)
  • No X-API-Key, valid JWT cookie -- Portal session authentication (for browser access)

You do not need to manage JWT tokens directly. The portal handles token refresh and cookie management automatically.


Unauthenticated Endpoints

The following endpoints are publicly accessible and do not require authentication:

EndpointPurpose
GET /healthService health check
GET /metricsPrometheus metrics (typically restricted by network policy)
GET /docsOpenAPI documentation UI
GET /openapi.jsonOpenAPI specification

Next Steps