Skip to content
JAOT

Authentication

JAOT supports two authentication methods: API keys for programmatic access and JWT tokens for browser-based sessions. All protected endpoints accept either method via the Authorization header.

API Key Authentication

API keys are long-lived credentials that identify a user and their organization. Keys are prefixed with ok_live_ (production) or ok_test_ (test environments) and are SHA-256 hashed before storage, so a database breach does not expose any keys.

Using an API Key

Include the key in the Authorization header with the Bearer scheme:

import httpx

API_KEY = "ok_live_a1b2c3d4e5f6789012345678901234567890abcdef"

response = httpx.get(
    "https://api.jaot.io/api/v2/auth/me",
    headers={"Authorization": f"Bearer {API_KEY}"},
)

user = response.json()
print(user["organization_name"])

Creating API Keys

Create additional keys via the API or the dashboard under Settings > API Keys.

import httpx

response = httpx.post(
    "https://api.jaot.io/api/v2/keys",
    headers={"Authorization": "Bearer ok_live_your_existing_key"},
    json={
        "name": "Production key",
        "description": "Used by the production server",
        "expires_days": 365,
    },
)

data = response.json()
print(data["api_key"])  # Save this -- it will not be shown again

The response includes the plaintext key exactly once:

{
  "api_key": "ok_live_a1b2c3d4e5f6789012345678901234567890abcdef",
  "id": "key_f3a9b2c1",
  "name": "Production key",
  "is_active": true,
  "created_at": "2026-02-19T10:00:00Z"
}

API keys are shown only once at creation. Store the key in a secure location immediately. If you lose a key, revoke it and create a new one.

Key Prefixes

PrefixEnvironment
ok_live_Production keys for live API access
ok_test_Test keys for development and staging

JWT Authentication

JWT tokens are used for browser-based sessions in the JAOT dashboard. The login flow returns a short-lived access token and a longer-lived refresh token.

Login

Send email and password to obtain tokens:

import httpx

response = httpx.post(
    "https://api.jaot.io/api/v2/auth/login",
    json={
        "email": "alice@example.com",
        "password": "your_password",
    },
)

data = response.json()
access_token = data["access_token"]   # Expires in 15 minutes
refresh_token = data["refresh_token"] # Expires in 7 days

Use the access token in subsequent requests:

curl https://api.jaot.io/api/v2/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Token Refresh

Access tokens expire after 15 minutes. Use the refresh token to obtain a new access token without re-entering credentials:

import httpx

response = httpx.post(
    "https://api.jaot.io/api/v2/auth/refresh",
    headers={"Authorization": f"Bearer {refresh_token}"},
)

data = response.json()
new_access_token = data["access_token"]

Token Lifetimes

TokenLifetimePurpose
Access token15 minutesAuthenticates API requests
Refresh token7 daysObtains new access tokens

Dual-Auth Middleware

All protected endpoints accept both API keys and JWT tokens. The auth middleware inspects the Authorization: Bearer <token> header and determines the auth path based on the token format:

  • Tokens starting with ok_live_ or ok_test_ are treated as API keys and verified via SHA-256 hash lookup.
  • All other Bearer tokens are treated as JWTs and verified via signature validation.

Most API users should use API keys. JWT authentication is primarily for the JAOT web dashboard and browser-based integrations.

Best Practices

Use environment variables for keys. Never hardcode API keys in source code.

import os
import httpx
 
API_KEY = os.environ["JAOT_API_KEY"]
headers = {"Authorization": f"Bearer {API_KEY}"}

Rotate keys periodically. Create a new key, update your applications, then revoke the old key.

Use separate keys per environment. Create distinct keys for development, staging, and production to isolate access and simplify rotation.

Restrict key scope. List keys via GET /api/v2/keys to audit which keys exist and when they were last used. Revoke any key that is no longer needed.

Never expose API keys in client-side JavaScript. Browser code is visible to end users. Use server-side code or a backend proxy to make authenticated API calls.

Error Responses

Authentication failures return standard error responses:

401 Unauthorized

Returned when the token is missing, invalid, or expired.

{
  "error": "unauthorized",
  "message": "Invalid or expired API key"
}

Common causes:

ScenarioFix
Missing Authorization headerAdd Authorization: Bearer <token> to the request
Missing Bearer prefixUse the full format: Authorization: Bearer ok_live_...
Expired API keyCreate a new key and update your configuration
Expired JWT access tokenRefresh the token via POST /api/v2/auth/refresh
Revoked keyCreate a new key in the dashboard

403 Forbidden

Returned when the authenticated user lacks permission for the requested resource.

{
  "error": "forbidden",
  "message": "Admin access required"
}

Admin endpoints (/api/v2/admin/*) require is_admin=true on the user account. Contact your organization administrator to request elevated access.