VideoAnnotator v1.3.0+ uses token-based authentication to secure the API. This guide covers obtaining, using, and managing API keys.
Run the database bootstrap command once per environment (it is safe to rerun). It creates/updates tables and mints an admin API key:
uv run videoannotator setup-db \
--admin-email you@example.com \
--admin-username youYou will see an output block containing the API key. Copy and store it securely—the raw token is never displayed again. Use --skip-admin if you only want schema creation without making a key (for example in automated deployments). If you forget this step, the API server will still auto-generate an admin key the first time it starts and detects that no tokens exist, but running setup-db keeps the workflow predictable.
Set it as an environment variable for easy reuse:
export API_KEY="va_api_ya9C5x_OZkweZf8JSxen60WY6KD98_VTa5_uHfN5wRM"Then use it in API requests:
# List jobs
curl -H "Authorization: Bearer $API_KEY" \
http://localhost:18011/api/v1/jobs
# Submit new job
curl -X POST -H "Authorization: Bearer $API_KEY" \
-F "video=@video.mp4" \
-F "selected_pipelines=scene,person,face" \
http://localhost:18011/api/v1/jobs/
# Get job status
curl -H "Authorization: Bearer $API_KEY" \
http://localhost:18011/api/v1/jobs/{job_id}import requests
API_KEY = "va_api_ya9C5x_OZkweZf8JSxen60WY6KD98_VTa5_uHfN5wRM"
BASE_URL = "http://localhost:18011/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
# List jobs
response = requests.get(f"{BASE_URL}/jobs", headers=headers)
jobs = response.json()
# Submit job
with open("video.mp4", "rb") as f:
files = {"video": f}
data = {"selected_pipelines": "scene,person,face"}
response = requests.post(
f"{BASE_URL}/jobs/",
headers=headers,
files=files,
data=data
)
job_id = response.json()["job_id"]If you lose your API key, generate a new one with the CLI (it automatically ensures the database schema is ready before creating the key):
# Interactive mode
uv run videoannotator generate-token
# Provide details explicitly
uv run videoannotator generate-token \
--user john@example.com \
--username john \
--key-name "John's laptop" \
--expires-days 365
# Save JSON for automation
uv run videoannotator generate-token \
--user service@example.com \
--key-name "service bot" \
--output tokens/service.jsonNote: For security, the full key is never stored - only a hash. If lost, you must generate a new key.
Create multiple keys for different users or applications:
# Interactive key generation
uv run python -m scripts.manage_tokens create
# You'll be prompted for:
# - User ID
# - Username
# - Email
# - Scopes (read, write, admin)
# - Expiration (optional)Revoke compromised or unused keys:
# List keys to find the key ID
uv run python -m scripts.manage_tokens list
# Revoke specific key
uv run python -m scripts.manage_tokens revoke va_api_xyz...API keys support granular permissions:
- read: View jobs, results, and system info
- write: Submit jobs, update configurations
- admin: Manage other users' keys, system settings
# Generate read-only key
uv run python -m scripts.manage_tokens create --scopes read
# Generate full-access key (default)
uv run python -m scripts.manage_tokens create --scopes read,write,adminFor local development, you can disable authentication:
export AUTH_REQUIRED=false
uv run videoannotatorYour test suite should use a test API key:
# tests/conftest.py
@pytest.fixture
def api_client():
"""API client with test authentication."""
from videoannotator.auth.token_manager import get_token_manager
manager = get_token_manager()
api_key, _ = manager.generate_api_key(
user_id="test",
username="test",
email="test@example.com",
scopes=["read", "write", "admin"]
)
client = TestClient(app)
client.headers["Authorization"] = f"Bearer {api_key}"
return client-
Use HTTPS: Always deploy behind HTTPS (TLS/SSL)
# nginx reverse proxy location /api/ { proxy_pass http://localhost:18011/; proxy_ssl_server_name on; }
-
Rotate Keys Regularly: Generate new keys every 90 days
# Create new key uv run python -m scripts.manage_tokens create # Update your application config # Then revoke old key uv run python -m scripts.manage_tokens revoke va_api_old...
-
Use Different Keys per Environment:
- Development: Local test key
- Staging: Dedicated staging key
- Production: Production-only key with limited expiration
-
Store Keys Securely:
- Use environment variables (not hardcoded)
- Use secrets managers (AWS Secrets Manager, Hashicorp Vault, etc.)
- Never commit keys to version control
-
Monitor Key Usage:
# Check recent key activity tail -f logs/api_requests.log | grep "user_id"
Set expiration for production keys:
# Generate key that expires in 90 days
uv run python -m scripts.manage_tokens create --expires-in-days 90Expired keys are automatically rejected with a clear error message.
For lab/team environments:
# Generate key for each user
uv run python -m scripts.manage_tokens create \
--user-id alice \
--username "Alice Johnson" \
--email alice@lab.edu \
--scopes read,write
uv run python -m scripts.manage_tokens create \
--user-id bob \
--username "Bob Smith" \
--email bob@lab.edu \
--scopes readSome endpoints don't require authentication:
/health- Server health check/docs- API documentation (Swagger UI)/redoc- Alternative API documentation/openapi.json- OpenAPI specification
These are safe to expose for monitoring and discovery.
Problem: API returns {"error": {"code": "AUTHENTICATION_REQUIRED", ...}}
Solutions:
-
Check API key is set:
echo $API_KEY # Should show va_api_...
-
Check Authorization header format:
# Correct -H "Authorization: Bearer va_api_..." # Wrong (old format, no longer supported) -H "X-API-Key: va_api_..."
-
Verify key hasn't expired:
uv run python -m scripts.manage_tokens list
Keys are persisted in tokens/tokens.json (encrypted). If this file is deleted, all keys are lost.
Solution: Never delete tokens/tokens.json. Back it up with your database.
Check the AUTH_REQUIRED environment variable:
# Verify it's set to false
echo $AUTH_REQUIRED
# Start server with explicit flag
AUTH_REQUIRED=false uv run videoannotatorProblem: Server started but didn't show the API key.
Possible Causes:
- Key already existed (check
tokens/tokens.json) - Output was hidden by background process
- AUTO_GENERATE_API_KEY=false was set
Solution:
# Generate new key manually
uv run python -m scripts.manage_tokens create| Variable | Default | Description |
|---|---|---|
AUTH_REQUIRED |
true |
Enable/disable authentication |
AUTO_GENERATE_API_KEY |
true |
Auto-generate key if server starts without existing keys |
SECURITY_ENABLED |
(alias for AUTH_REQUIRED) | Backward compatibility |
- Location:
tokens/tokens.json - Format: Encrypted JSON
- Encryption Key:
tokens/encryption.key(auto-generated) - Permissions: 600 (Unix systems)
- Prefix:
va_api_ - Length: 47 characters total
- Encoding: URL-safe base64
- Entropy: 256 bits
Example: va_api_ya9C5x_OZkweZf8JSxen60WY6KD98_VTa5_uHfN5wRM
- CORS Configuration - Configure allowed origins
- Production Checklist - Security hardening guide
- Getting Started - API usage and common workflows