Skip to content

API Keys

API keys provide programmatic access to the Telovix Console v2 API without a user session. Every API key uses HMAC-SHA256 request signing rather than static bearer tokens. The signing approach prevents replay attacks, protects against log-based credential exposure, and binds each request to a specific timestamp, nonce, and body hash.

API keys are scoped to specific operations. Granting only the scopes an integration actually needs limits what a leaked key can access.

Prerequisites

  • Authenticated Console session with admin role to create, list, or revoke API keys

Authentication method (HMAC-SHA256)

Static bearer tokens are not supported for API keys. Every request must be independently signed. The signing process:

  1. Compute the SHA256 hash of the raw request body bytes (empty string hash for requests with no body).
  2. Generate a unique request nonce. A UUID is recommended, but any non-empty string up to 128 characters is accepted.
  3. Construct the signing string:
    {HTTP_METHOD}\n{PATH}\n{UNIX_TIMESTAMP}\n{NONCE}\n{HEX(SHA256(body_bytes))}
  4. Sign the signing string with HMAC-SHA256 using the key's 32-byte HMAC secret.
  5. Set four required headers on every request:
HeaderValue
X-Telovix-Key-IDThe 16-character hex key identifier
X-Telovix-TimestampCurrent Unix epoch seconds (integer)
X-Telovix-NonceUnique per-request nonce or request ID (UUID recommended)
X-Telovix-Signaturesha256=<hex-encoded-hmac-sha256>

The Console rejects requests where the timestamp differs from server time by more than 300 seconds. It also rejects reused X-Telovix-Nonce values for the same key within the acceptance window. Keep the client clock synchronized with NTP and generate a fresh nonce for every signed request.

Signing example (Python)

The example below uses port 15483 (Telovix self-hosted default).

python
import hmac
import hashlib
import time
import uuid
import requests

KEY_ID = "your-key-id"
HMAC_SECRET = bytes.fromhex("your-32-byte-hmac-secret-in-hex")

def signed_request(method, path, body=b"", base_url="https://console.example.com:15483"):
    timestamp = str(int(time.time()))
    nonce = str(uuid.uuid4())
    body_hash = hashlib.sha256(body).hexdigest()
    signing_string = f"{method}\n{path}\n{timestamp}\n{nonce}\n{body_hash}"
    signature = hmac.new(HMAC_SECRET, signing_string.encode(), hashlib.sha256).hexdigest()

    headers = {
        "X-Telovix-Key-ID": KEY_ID,
        "X-Telovix-Timestamp": timestamp,
        "X-Telovix-Nonce": nonce,
        "X-Telovix-Signature": f"sha256={signature}",
        "Content-Type": "application/json",
    }
    return requests.request(method, f"{base_url}{path}", headers=headers, data=body)

# Example: list sensors
response = signed_request("GET", "/api/v2/sensors")
print(response.json())

Available scopes

ScopeWhat it grants
sensors:readRead sensor fleet inventory and state
sensors:writeCreate and modify sensor configuration
events:readQuery runtime events and analytics
events:streamConnect to the real-time event stream
anomalies:readRead anomaly scores and behavioral analytics
anomalies:writeManage anomaly suppression and configuration
compliance:readRead compliance reports and evidence
alerts:readRead alert inbox and alert rules
alerts:writeCreate and manage alert rules and webhook destinations
policies:readRead policy pack assignments and enforcement state
policies:writeAssign policy packs and change enforcement state
investigations:readRead investigations
investigations:writeCreate and update investigations
audit:readRead the Console audit log
chat:readUse the AI assistant
sbom:readRead SBOM scan results
analytics:readLegacy alias for events:read (accepted for backward compatibility)

Multiple scopes are assigned as an array. An API key can hold any combination of scopes.


Managing API keys

API key management lives in the Console UI at Settings > API Keys.

Create an API key

In Settings > API Keys, click Create API Key, set the label, select the scopes, and optionally set an expiry.

Fields:

FieldRequiredDescription
labelYesHuman-readable label (max 128 characters)
scopesYesArray of scope strings from the list above
expires_daysNoDays until expiry; omit for no expiry

The one-time secret material returned at creation looks like this:

json
{
  "key_id": "a3f8b2c1d4e5f609",
  "label": "CI event monitoring",
  "scopes": ["events:read", "alerts:read"],
  "hmac_secret": "7a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b",
  "hmac_note": "Sign every request with HMAC-SHA256. This secret will NOT be shown again.",
  "warning": "Store this secret securely. It will not be shown again."
}

The hmac_secret is returned only at creation time. It is stored encrypted in the Console database and is never retrievable again. Store it immediately in a secrets manager.

The key_id is the identifier used in the X-Telovix-Key-ID header on every signed request. The hmac_secret is the 32-byte secret (as a hex string) used for HMAC-SHA256 signing.

List API keys

The Settings > API Keys page lists active keys. Each entry includes:

FieldDescription
key_id16-character hex key identifier
labelHuman-readable label
scopesArray of assigned scopes
created_byEmail of the admin who created the key
expires_atExpiry timestamp (null if no expiry)
last_used_atLast time this key was used for a signed request (null if never used)
revokedWhether the key has been revoked (only non-revoked keys are returned)
created_atCreation timestamp

Only non-revoked keys are returned. The HMAC secret is never included in list responses.

Revoke an API key

Use Revoke on the key row in Settings > API Keys. Revocation is permanent; revoked keys are rejected immediately on the next signed request and cannot be re-activated.


V2 API endpoints

API keys are used with the /api/v2/ endpoint family only.

Representative external endpoints by scope:

ScopeRepresentative /api/v2/ endpoints
sensors:read/api/v2/sensors, /api/v2/sensors/{sensor_id}, /api/v2/sensors/{sensor_id}/metrics, /api/v2/sensors/{sensor_id}/health, /api/v2/sensors/{sensor_id}/energy, /api/v2/energy/fleet
events:read, events:stream, analytics:read/api/v2/events, /api/v2/events/stream
alerts:read, alerts:write/api/v2/alerts, /api/v2/alerts/{alert_id}, /api/v2/alerts/{alert_id}/notes
anomalies:read, anomalies:write/api/v2/anomalies/scores, /api/v2/anomalies/chains, /api/v2/anomalies/suppressions, /api/v2/anomalies/baselines
compliance:read/api/v2/compliance/posture, /api/v2/compliance/controls, /api/v2/compliance/controls/{control_id}/evidence, /api/v2/compliance/export
policies:read, policies:write/api/v2/policies/rules, /api/v2/policies/enforcement
audit:read/api/v2/audit
sbom:read/api/v2/sbom/scans, /api/v2/sbom/scans/{scan_id}, /api/v2/sbom/scans/{scan_id}/vulnerabilities, /api/v2/sbom/scans/{scan_id}/cyclonedx
investigations:read, investigations:write/api/v2/investigations, /api/v2/investigations/{case_id}, /api/v2/investigations/{case_id}/events, /api/v2/investigations/{case_id}/notes
chat:read/api/v2/chat

When a v2 endpoint receives an API key request, it:

  1. Verifies all four HMAC headers are present.
  2. Checks the timestamp is within the 300-second window.
  3. Looks up the key_id in the database (only non-revoked, non-expired keys match).
  4. Decrypts the stored HMAC secret.
  5. Recomputes the expected signature and compares it using constant-time comparison.
  6. Rejects reused nonces for the same key and records the new nonce.
  7. Checks the required scope for the endpoint.
  8. Updates last_used_at asynchronously (non-blocking).

Key expiry

When expires_days is set on creation, the Console computes expires_at = now + expires_days. Requests using a key after expires_at are rejected with HTTP 401 and "invalid_key". The list endpoint excludes expired keys because the query filters expires_at > NOW().

Keys with no expires_days do not expire. They remain active until explicitly revoked.


Operational guidance

Secret is shown once: The hmac_secret is returned only in the creation response. There is no way to retrieve it afterward. If it is lost, revoke the key and create a new one.

One key per integration: Create a separate key for each external system (CI pipeline, SIEM pull, reporting job). This allows individual rotation and revocation without disrupting other integrations.

Minimum scopes: Assign only the scopes each integration needs. An event monitoring job only needs events:read. A system that manages alert rules needs alerts:write but not policies:write.

Timestamp synchronization: The server rejects requests with timestamp skew greater than 300 seconds. Ensure the machine generating signed requests has accurate NTP synchronization. Large skews typically indicate a misconfigured system clock rather than a network delay.

Fresh nonce per request: Every signed request must use a new X-Telovix-Nonce value. Reusing a nonce within the acceptance window causes HTTP 401 with "nonce_reused".

Key rotation: Create a new key before revoking the old one to avoid a window of unavailability. Update the dependent system with the new key_id and hmac_secret, verify it works, then revoke the old key.


Further reading

Released under the Telovix Commercial License.