Skip to main content

Key Validation

The validation endpoint is how Hokusai services verify incoming API keys. It checks that a key is active, not expired, authorized for the requested service, and optionally validates scopes and IP restrictions.

POST /api/v1/keys/validate

info

The validation endpoint is public — it does not require an admin token. This allows any Hokusai microservice to validate keys independently.

Sending API Keys​

There are three ways to send an API key for validation, listed in priority order:

curl -X POST https://auth.hokus.ai/api/v1/keys/validate \
-H "Authorization: Bearer hk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"service_id": "prediction"}'

Method 2: X-API-Key Header​

curl -X POST https://auth.hokus.ai/api/v1/keys/validate \
-H "X-API-Key: hk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"service_id": "prediction"}'

Method 3: Request Body​

curl -X POST https://auth.hokus.ai/api/v1/keys/validate \
-H "Content-Type: application/json" \
-d '{
"api_key": "hk_live_YOUR_API_KEY",
"service_id": "prediction"
}'
tip

Use the Authorization header for production integrations. It follows the OAuth 2.0 Bearer Token standard and keeps keys out of request bodies and server logs.

Request Parameters​

FieldTypeRequiredDescription
service_idstringNoVerify the key is authorized for this service
required_scopestringNoVerify the key has this specific scope
client_ipstringNoCheck IP against the key's allowlist (auto-detected if omitted)
api_keystringNoAPI key (only needed if not sent via header)

Validation Flow​

Response​

Successful Validation​

{
"is_valid": true,
"key_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "admin",
"service_id": "prediction",
"scopes": ["predict", "read"],
"rate_limit_per_hour": 5000,
"billing_plan": "free",
"monthly_prediction_limit": null
}

Failed Validation​

{
"is_valid": false,
"key_id": null,
"user_id": null,
"service_id": null,
"scopes": null,
"rate_limit_per_hour": null,
"billing_plan": null,
"monthly_prediction_limit": null,
"error": "API key is inactive or has been revoked"
}

Caching Behavior​

Validation results are cached in Redis for 5 minutes (configurable via CACHE_TTL). This means:

  • First validation of a key queries the database and caches the result
  • Subsequent validations within 5 minutes are served from cache (under 5ms response time)
  • Key revocation invalidates the cache immediately
  • Key rotation invalidates the old key's cache entry
  • If Redis is unavailable, every validation falls back to a database query
warning

After revoking a key, there may be up to a 5-minute window where cached validation results still return is_valid: true from other service instances. For immediate revocation, consider reducing the CACHE_TTL or triggering a cache flush.

Integration Examples​

Python Service Integration​

import requests
from functools import wraps
from flask import request, jsonify

AUTH_SERVICE_URL = "https://auth.hokus.ai"

def require_api_key(service_id: str, required_scope: str = None):
"""Decorator to validate API keys on incoming requests."""
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
api_key = request.headers.get("Authorization", "").replace("Bearer ", "")
if not api_key:
api_key = request.headers.get("X-API-Key", "")

if not api_key:
return jsonify({"error": "API key required"}), 401

validation = requests.post(
f"{AUTH_SERVICE_URL}/api/v1/keys/validate",
headers={"Authorization": f"Bearer {api_key}"},
json={
"service_id": service_id,
"required_scope": required_scope,
"client_ip": request.remote_addr,
},
).json()

if not validation.get("is_valid"):
return jsonify({"error": validation.get("error", "Invalid API key")}), 401

request.key_info = validation
return f(*args, **kwargs)
return wrapper
return decorator

@app.route("/predict")
@require_api_key(service_id="prediction", required_scope="predict")
def predict():
# request.key_info contains validation result
rate_limit = request.key_info["rate_limit_per_hour"]
# ... handle prediction

JavaScript / Node.js Integration​

const AUTH_SERVICE_URL = "https://auth.hokus.ai";

async function validateApiKey(req) {
const apiKey =
req.headers.authorization?.replace("Bearer ", "") ||
req.headers["x-api-key"];

if (!apiKey) {
throw new Error("API key required");
}

const response = await fetch(`${AUTH_SERVICE_URL}/api/v1/keys/validate`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
service_id: "prediction",
client_ip: req.ip,
}),
});

const result = await response.json();
if (!result.is_valid) {
throw new Error(result.error || "Invalid API key");
}

return result;
}

Status Codes​

StatusMeaning
200Key is valid
400No API key provided in request
401Key is invalid, expired, revoked, or fails scope/IP checks
500Internal server error

JWT Token Validation​

POST /api/v1/tokens/validate

Downstream services (e.g., the Contract Deployer API) use this endpoint to verify JWT tokens issued by the auth service via SIWE authentication.

info

This endpoint is public — it does not require an admin token.

Request Body​

FieldTypeRequiredDescription
tokenstringYesThe JWT token to validate
servicestringNoVerify the token grants access to this service

Example​

curl -X POST https://auth.hokus.ai/api/v1/tokens/validate \
-H "Content-Type: application/json" \
-d '{"token": "eyJhbGciOiJIUzI1NiIs...", "service": "prediction"}'

Response​

{
"valid": true,
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"permissions": ["predict", "read"]
}

A failed validation returns 401 with:

{
"valid": false,
"error": "Token expired"
}

Sign-In with Ethereum (SIWE)​

SIWE allows users to authenticate using an Ethereum wallet. The flow issues a JWT token that can be used with protected endpoints including organization management.

Step 1: Request a Challenge​

POST /auth/siwe/challenge

curl -X POST https://auth.hokus.ai/auth/siwe/challenge \
-H "Content-Type: application/json" \
-d '{"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"}'

Returns a message to sign and a nonce:

{
"message": "auth.hokus.ai wants you to sign in with your Ethereum account:\n0x742d35Cc...\n\nSign in to Hokusai\n\nNonce: abc123...",
"nonce": "abc123..."
}

Step 2: Sign and Verify​

Sign the message with your wallet, then submit the signature:

POST /auth/siwe/verify

curl -X POST https://auth.hokus.ai/auth/siwe/verify \
-H "Content-Type: application/json" \
-d '{
"message": "auth.hokus.ai wants you to sign in with...",
"signature": "0x..."
}'

Returns a JWT access token:

{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"is_new_user": false
}
tip

If the wallet address is not yet registered, a new user account is created automatically. Set is_new_user in the response to detect first-time logins.

SIWE Flow Diagram​

Next Steps​