Authentication & API Keys
The Cert-IX Scan API uses API keys for programmatic access. API keys provide granular scopes, scan type restrictions, per-key rate limits, IP allowlisting, automatic expiration, and zero-downtime rotation.
Authentication Methodsβ
| Method | Use Case | Header |
|---|---|---|
| API Key | Programmatic API access (scripts, CI/CD, integrations) | X-API-Key |
| JWT | Dashboard operations (API key management) | Authorization: Bearer <token> |
This page covers API key authentication. JWT authentication is handled automatically by the Cert-IX Dashboard.
API Key Formatβ
Cert-IX API keys follow a deterministic format for easy identification:
cix_sk_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
| Segment | Description |
|---|---|
cix_ | Cert-IX platform prefix |
sk_ | Secret key type identifier |
XXX... | 40-character cryptographic random string |
API keys are hashed with SHA-256 before storage. The raw key is displayed only once at creation. Cert-IX cannot retrieve your key if lost β you must rotate.
Passing Your API Keyβ
Include your API key in the X-API-Key header on every request:
curl -X GET https://api.cert-ix.com/scan-api/api/v1/scans \
-H "X-API-Key: cix_sk_your_api_key_here"
Query strings may be logged in server access logs, browser history, and proxy caches.
Creating an API Keyβ
API keys are created via the Cert-IX Dashboard (JWT authentication).
Endpointβ
POST /api/v1/api-keys
Request Bodyβ
{
"name": "CI/CD Pipeline - Production",
"description": "Used by GitHub Actions for nightly vulnerability scans",
"scopes": [
"scans:create",
"scans:read",
"scans:list",
"results:read",
"webhooks:read"
],
"allowedScanTypes": ["nmap", "nuclei", "trivy", "zap"],
"allowedIpAddresses": ["203.0.113.50", "198.51.100.0/24"],
"rateLimitPerMinute": 30,
"rateLimitPerHour": 500,
"rateLimitPerDay": 5000,
"expiresInDays": 90
}
Request Parametersβ
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable key name (max 255 chars) |
description | string | No | Optional usage description |
scopes | string[] | No | Permission scopes (defaults to all scopes) |
allowedScanTypes | string[] | No | Scan engines this key can invoke (defaults to all) |
allowedIpAddresses | string[] | No | Source IP/CIDR ranges allowed to use this key |
rateLimitPerMinute | integer | No | Max requests per minute |
rateLimitPerHour | integer | No | Max requests per hour |
rateLimitPerDay | integer | No | Max requests per day |
expiresInDays | integer | No | Key expiration in days (0 or null = no expiration) |
Response (201 Created)β
{
"success": true,
"data": {
"apiKey": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "CI/CD Pipeline - Production",
"keyPrefix": "cix_sk_001e3c",
"scopes": ["scans:create", "scans:read", "scans:list", "results:read", "webhooks:read"],
"allowedScanTypes": ["nmap", "nuclei", "trivy", "zap"],
"status": "active",
"expiresAt": "2026-06-04T10:00:00Z",
"createdAt": "2026-03-06T10:00:00Z"
},
"rawKey": "cix_sk_001e3c2d92ffb23344a943df2b6a001fb1028d002"
}
}
The rawKey field is returned only at creation. Copy it immediately and store it in a secure vault (e.g., HashiCorp Vault, AWS Secrets Manager, or your CI/CD secrets store).
Scopes & Permissionsβ
Scopes control what actions an API key is authorized to perform. Apply the principle of least privilege β only grant the scopes your integration needs.
Available Scopesβ
| Scope | Description |
|---|---|
scans:create | Submit new scans |
scans:read | View individual scan details and status |
scans:list | List all tenant scans |
scans:cancel | Cancel running scans |
results:read | Retrieve scan results and findings |
templates:create | Create scan templates |
templates:read | View scan templates |
templates:update | Modify scan templates |
templates:delete | Delete scan templates |
webhooks:create | Register and test webhooks |
webhooks:read | View webhook configurations and delivery logs |
webhooks:update | Modify webhook settings |
webhooks:delete | Delete webhooks |
usage:read | View usage analytics and quota information |
Recommended Scope Setsβ
CI/CD Pipeline (scan read-write):
["scans:create", "scans:read", "scans:list", "results:read"]
Monitoring Dashboard (read-only):
["scans:read", "scans:list", "results:read", "usage:read"]
Full Automation (scans + webhooks + templates):
["scans:create", "scans:read", "scans:list", "scans:cancel", "results:read",
"templates:create", "templates:read", "templates:update",
"webhooks:create", "webhooks:read"]
Scope Enforcementβ
If a request requires a scope the API key doesn't have:
{
"success": false,
"error": "Insufficient scope: requires scans:create",
"code": "INSUFFICIENT_SCOPE"
}
HTTP Status: 403 Forbidden
Allowed Scan Typesβ
Restrict which scan engines an API key can invoke:
- Isolate CI/CD keys to container scans only (e.g.,
trivy) - Limit OSINT keys to passive reconnaissance only (e.g.,
harvester,sublist3r) - Restrict web keys to web scanners (e.g.,
zap,nikto,wapiti)
Available scan types: nmap, zap, trivy, nuclei, nikto, sqlmap, wapiti, harvester, sublist3r, sentinel
IP Allowlistingβ
Lock an API key to specific source IP addresses or CIDR ranges. Requests from other IPs are rejected with 403 IP_NOT_ALLOWED.
Supported formats:
- Single IP:
"203.0.113.50" - CIDR range:
"198.51.100.0/24" - IPv6:
"2001:db8::1"
For CI/CD runners with dynamic IPs (e.g., GitHub Actions), omit allowedIpAddresses or use the runner's published CIDR ranges.
Rate Limitsβ
Rate limits protect both your account and the platform. Limits are enforced across three windows:
| Window | Default | Configurable Range |
|---|---|---|
| Per minute | 60 | 1 β 1,000 |
| Per hour | 1,000 | 1 β 50,000 |
| Per day | 10,000 | 1 β 500,000 |
Rate Limit Headersβ
Every response includes rate limit information:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1709654460
Rate Limit Exceededβ
{
"success": false,
"error": "Rate limit exceeded. Retry in 23 seconds.",
"code": "RATE_LIMIT_EXCEEDED"
}
HTTP Status: 429 Too Many Requests
Implement exponential backoff in your client:
import time
def api_call_with_retry(func, max_retries=3):
for attempt in range(max_retries):
response = func()
if response.status_code != 429:
return response
wait = 2 ** attempt # 1s, 2s, 4s
time.sleep(wait)
raise Exception("Rate limit exceeded after retries")
Key Rotationβ
Rotate API keys with zero downtime using the grace period mechanism. During the grace period, both old and new keys are valid.
Endpointβ
POST /api/v1/api-keys/:keyId/rotate
Request Body (optional)β
{
"gracePeriodHours": 24
}
Rotation Workflowβ
1. POST /api-keys/:oldKeyId/rotate β New key created, old key enters grace period
2. Update your secrets store with the new key
3. Deploy the new key to your services
4. Old key automatically expires after the grace period
Rotate keys every 90 days. Automate rotation in your infrastructure or set a calendar reminder.
Key Revocationβ
Immediately and permanently disable an API key. Revocation is instant β the key stops working immediately with no grace period.
Endpointβ
DELETE /api/v1/api-keys/:keyId
Request Bodyβ
{
"reason": "Key compromised β rotating to new key"
}
After revocation, any request using the revoked key returns 401 KEY_REVOKED.
Key Statusesβ
| Status | Description |
|---|---|
active | Key is fully operational |
revoked | Key has been permanently disabled |
expired | Key has passed its expiresAt date |
suspended | Key has been temporarily suspended by an admin |
Only active keys can authenticate API requests.
Security Best Practicesβ
Doβ
- β Store keys in a secrets manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault)
- β Use environment variables in your applications β never hardcode keys
- β Apply least-privilege scopes β only grant what's needed
- β Set expiration dates β rotate keys every 90 days
- β Use IP allowlisting for static infrastructure
- β Use per-key rate limits to prevent runaway automation
- β Monitor usage via the usage analytics endpoints
- β Revoke immediately if a key is compromised
Don'tβ
- β Never commit keys to source control (Git, SVN)
- β Never pass keys in URL parameters
- β Never share keys across teams or environments
- β Never log API keys in application logs
- β Never use a single key for all environments β create separate keys for dev, staging, production
Environment Variable Exampleβ
# .env (never committed to Git)
CERTIX_API_KEY=cix_sk_your_api_key_here
import os
api_key = os.environ["CERTIX_API_KEY"]
apiKey := os.Getenv("CERTIX_API_KEY")
const apiKey = process.env.CERTIX_API_KEY;
Next Steps: