API Guide
REST API reference and integration examples
Overview
The CDN Manager exposes versioned HTTP APIs under /api (v1 and v2), using JSON payloads by default. When sending request bodies, set Content-Type: application/json. Server errors typically respond with { "message": "..." } where available, or an empty body with the relevant status code.
Authentication uses a two-step flow:
- Create a session
- Exchange that session for an access token with
grant_type=session
Use the access token in Authorization: Bearer <token> when calling bearer-protected routes. CORS preflight (OPTIONS) is supported and wildcard origins are accepted by default.
Durations such as TTLs use humantime strings (for example, 60s, 5m, 1h).
Base URL
All API endpoints are relative to:
https://<manager-host>/api
API Reference Guides
The API documentation is organized by functional area:
Authentication Flow
All authenticated API calls follow the same authentication flow. For detailed instructions, see the Authentication API Guide.
Quick Start:
# Step 1: Login to get session
curl -s -X POST "https://cdn-manager/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "Password1!"
}' | tee /tmp/session.json
SESSION_ID=$(jq -r '.session_id' /tmp/session.json)
SESSION_TOKEN=$(jq -r '.session_token' /tmp/session.json)
# Step 2: Exchange session for access token
curl -s -X POST "https://cdn-manager/api/v1/auth/token" \
-H "Content-Type: application/json" \
-d "$(jq -nc --arg sid "$SESSION_ID" --arg st "$SESSION_TOKEN" \
'{session_id:$sid,session_token:$st,grant_type:"session",scope:"openid"}')" \
| tee /tmp/token.json
ACCESS_TOKEN=$(jq -r '.access_token' /tmp/token.json)
# Step 3: Call a protected endpoint
curl -s "https://cdn-manager/api/v1/metrics" \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
Error Responses
The API uses standard HTTP response codes to indicate the success or failure of an API request.
Most errors return an empty response body with the relevant HTTP status code (e.g., 404 Not Found or 409 Conflict).
In some cases, the server may return a JSON body containing a user-facing error message:
{
"message": "Human-readable error message"
}
Next Steps
After learning the API:
- Operations Guide - Day-to-day operational procedures
- Troubleshooting Guide - Resolve API issues
- Configuration Guide - Full configuration reference
1 - Authentication API
Authentication and session management
Overview
The Authentication API provides endpoints for user authentication, session management, and token exchange. All authenticated API calls require a valid access token obtained through the authentication flow.
Base URL
https://<manager-host>/api/v1/auth
Endpoints
POST /api/v1/auth/login
Create a session from email/password credentials.
Request:
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "Password1!"
}
Success Response (200):
{
"session_id": "session-1",
"session_token": "token-1",
"verified_at": "2024-01-01T00:00:00Z",
"expires_at": "2024-01-01T01:00:00Z"
}
Errors:
401 - Authentication failure (invalid credentials)500 - Backend/state errors
POST /api/v1/auth/token
Exchange a session for an access token (required for bearer auth).
Request:
POST /api/v1/auth/token
Content-Type: application/json
{
"session_id": "session-1",
"session_token": "token-1",
"grant_type": "session",
"scope": "openid profile"
}
Success Response (200):
{
"access_token": "<token>",
"scope": "openid profile",
"expires_in": 3600,
"token_type": "bearer"
}
Token Scopes
The scope parameter in the token exchange request is a space-separated string of permissions requested for the access token.
Scope Resolution
When a token is requested, the backend system filters the requested scopes against the user’s actual permissions. The resulting access token will only contain the subset of requested scopes that the user is authorized to possess.
Naming and Design
Scope names are defined by the applications that consume the tokens, not by the central IAM system. To prevent collisions between different applications or modules, it is highly recommended that application developers use URN-style prefixes for scope names (e.g., urn:acd:manager:config:read).
Errors:
401 - Authentication failure (invalid session)500 - Backend/state errors
POST /api/v1/auth/logout
Revoke a session. Note: This does not revoke issued access tokens; they remain valid until expiration.
Request:
POST /api/v1/auth/logout
Content-Type: application/json
{
"session_id": "session-1",
"session_token": "token-1"
}
Success Response (200):
Errors:
400 - Invalid session parameters500 - Backend/state errors
Complete Authentication Flow Example
# Step 1: Login to get session
curl -s -X POST "https://cdn-manager/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "Password1!"
}' | tee /tmp/session.json
SESSION_ID=$(jq -r '.session_id' /tmp/session.json)
SESSION_TOKEN=$(jq -r '.session_token' /tmp/session.json)
# Step 2: Exchange session for access token
curl -s -X POST "https://cdn-manager/api/v1/auth/token" \
-H "Content-Type: application/json" \
-d "$(jq -nc --arg sid "$SESSION_ID" --arg st "$SESSION_TOKEN" \
'{session_id:$sid,session_token:$st,grant_type:"session",scope:"openid"}')" \
| tee /tmp/token.json
ACCESS_TOKEN=$(jq -r '.access_token' /tmp/token.json)
# Step 3: Call a protected endpoint
curl -s "https://cdn-manager/api/v1/metrics" \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
Using the Access Token
Once you have obtained an access token, include it in the Authorization header of all API requests:
Authorization: Bearer <access_token>
Example:
curl -s "https://cdn-manager/api/v1/configuration" \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
Token Expiration
Access tokens expire after the duration specified in expires_in (typically 3600 seconds / 1 hour). When a token expires, you must re-authenticate to obtain a new token.
Next Steps
2 - Health API
Liveness and readiness probe endpoints
Overview
The Health API provides endpoints for Kubernetes health probes and service health checking.
Base URL
https://<manager-host>/api/v1/health
Endpoints
GET /api/v1/health/alive
Liveness probe that indicates whether the service is running. Always returns 200 OK.
Request:
Response (200):
Use Case: Kubernetes liveness probe to determine if the pod should be restarted.
GET /api/v1/health/ready
Readiness probe that checks service readiness including downstream dependencies.
Request:
Success Response (200):
Failure Response (503):
Use Case: Kubernetes readiness probe to determine if the pod should receive traffic. Returns 503 if any downstream dependencies (database, Kafka, Redis) are unavailable.
Kubernetes Configuration
Example Kubernetes probe configuration:
livenessProbe:
httpGet:
path: /api/v1/health/alive
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/v1/health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Next Steps
3 - Selection Input API
Key-value and list storage with search capabilities
Overview
The Selection Input API provides JSON key/value storage with search capabilities. It supports two API versions (v1 and v2) with different operation models.
Base URL
https://<manager-host>/api/v1/selection_input
https://<manager-host>/api/v2/selection_input
Version Comparison
| Feature | v1 /api/v1/selection_input | v2 /api/v2/selection_input |
|---|
| Primary operation | Merge/UPSERT (POST) | Insert/Replace (PUT) |
| List append | N/A | POST to push to list |
| Search syntax | Wildcard prefix (foo* implicit) | Full wildcard (foo* explicit) |
| Query params | search, sort, limit, ttl | search, ttl, correlation_id |
| Sort support | Yes (asc/desc) | No |
| Limit support | Yes | No |
| Use case | Simple key-value with optional search | List-like operations, full wildcard |
When to Use Each Version
| Scenario | Recommended Version |
|---|
| Simple key-value storage | v1 |
| List/queue operations (append to array) | v2 POST |
| Full wildcard pattern matching | v2 |
| Need to sort or paginate results | v1 |
v1 Endpoints
Fetch stored JSON. If value is an object, optional search/limit/sort applies to its keys.
Query Parameters:
search - Wildcard prefix search (adds * implicitly)sort - Sort order (asc or desc)limit - Maximum results (must be > 0)
Success Response (200):
{
"foo": 1,
"foobar": 2
}
Errors:
404 - Path does not exist400 - Invalid search/sort/limit parameters500 - Backend failure
Example:
curl -s "https://cdn-manager/api/v1/selection_input/config?search=foo&limit=2"
POST /api/v1/selection_input/{path}
Upsert (merge) JSON at path. Nested objects are merged recursively.
Query Parameters:
ttl - Expiry time as humantime string (e.g., 10m, 1h)
Request:
{
"feature_flag": true,
"ratio": 0.5
}
Success: 201 Created echoing the payload
Errors:
500 / 503 - Backend failure
Example:
curl -s -X POST "https://cdn-manager/api/v1/selection_input/config?ttl=10m" \
-H "Content-Type: application/json" \
-d '{
"feature_flag": true,
"ratio": 0.5
}'
Delete stored value.
Success: 204 No Content
Errors: 503 - Backend failure
v2 Endpoints
Fetch stored JSON with optional wildcard filtering.
Query Parameters:
search - Full wildcard pattern (e.g., foo*, *bar*)correlation_id - Accepted but currently ignored (logging only)
Success Response (200):
{
"foo": 1,
"foobar": 2
}
Errors:
400 - Invalid search pattern404 - Path does not exist500 - Backend failure
Example:
curl -s "https://cdn-manager/api/v2/selection_input/config?search=foo*"
Insert/replace value. Old value is discarded.
Query Parameters:
ttl - Expiry time as humantime string
Request:
{
"items": ["a", "b", "c"]
}
Success: 200 OK
Example:
curl -s -X PUT "https://cdn-manager/api/v2/selection_input/catalog" \
-H "Content-Type: application/json" \
-d '{
"items": ["a", "b", "c"]
}'
POST /api/v2/selection_input/{path}
Push a value to the back of a list-like entry (append to array).
Query Parameters:
ttl - Expiry time as humantime string
Request (any JSON value):
Or a simple string:
Success: 200 OK
Example:
curl -s -X POST "https://cdn-manager/api/v2/selection_input/queue" \
-H "Content-Type: application/json" \
-d '"ready-for-publish"'
Delete stored value.
Success: 204 No Content
Next Steps
4 - Data Store API
Generic JSON key/value storage
Overview
The Data Store API provides generic JSON key/value storage for short-lived or simple structured data.
Base URL
https://<manager-host>/api/v1/datastore
Endpoints
GET /api/v1/datastore
List all known keys.
Query Parameters:
show_hidden - Boolean (default false). When true, includes internal keys starting with _.
Success Response (200):
["user:123", "config:settings", "session:abc"]
Hidden Keys: Keys starting with _ are reserved for internal use (e.g., subnet service). Writing to hidden keys via the datastore API returns 400 Bad Request.
GET /api/v1/datastore/{key}
Retrieve the JSON value for a specific key.
Success Response (200): The stored JSON value
Errors:
404 - Key does not exist500 - Backend failure
Example:
curl -s "https://cdn-manager/api/v1/datastore/user:123"
POST /api/v1/datastore/{key}
Create a new JSON value at the specified key. Fails if the key already exists.
Query Parameters:
ttl - Expiry time as humantime string (e.g., 60s, 1h)
Request:
{
"id": 123,
"name": "alice"
}
Success: 201 Created
Errors:
409 Conflict - Key already exists500 - Backend failure
Example:
curl -s -X POST "https://cdn-manager/api/v1/datastore/user:123?ttl=1h" \
-H "Content-Type: application/json" \
-d '{"id":123,"name":"alice"}'
PUT /api/v1/datastore/{key}
Update or replace the JSON value at an existing key.
Query Parameters:
ttl - Expiry time as humantime string
Success: 200 OK
Errors:
404 - Key does not exist500 - Backend failure
Example:
curl -s -X PUT "https://cdn-manager/api/v1/datastore/user:123" \
-H "Content-Type: application/json" \
-d '{"id":123,"name":"alice-updated"}'
DELETE /api/v1/datastore/{key}
Delete the value at the specified key. Idempotent operation.
Success: 204 No Content
Errors: 500 - Backend failure
Example:
curl -s -X DELETE "https://cdn-manager/api/v1/datastore/user:123"
Next Steps
5 - Subnets API
CIDR-to-value mappings for routing decisions
Overview
The Subnets API manages CIDR-to-value mappings used for routing decisions. This allows classification of IP ranges for routing purposes.
Base URL
https://<manager-host>/api/v1/subnets
Endpoints
PUT /api/v1/subnets
Create or update subnet mappings.
Request:
{
"192.168.1.0/24": "office",
"10.0.0.0/8": "internal",
"203.0.113.0/24": "external"
}
Success: 200 OK
Errors:
400 - Invalid CIDR format500 - Backend failure
Example:
curl -s -X PUT "https://cdn-manager/api/v1/subnets" \
-H "Content-Type: application/json" \
-d '{
"192.168.1.0/24": "office",
"10.0.0.0/8": "internal"
}'
GET /api/v1/subnets
List all subnet mappings.
Success Response (200): JSON object of CIDR-to-value mappings
Example:
curl -s "https://cdn-manager/api/v1/subnets" | jq '.'
DELETE /api/v1/subnets
Delete all subnet mappings.
Success: 204 No Content
GET /api/v1/subnets/byKey/{subnet}
Retrieve subnet mappings whose CIDR begins with the given prefix.
Example:
curl -s "https://cdn-manager/api/v1/subnets/byKey/192.168" | jq '.'
GET /api/v1/subnets/byValue/{value}
Retrieve subnet mappings with the given classification value.
Example:
curl -s "https://cdn-manager/api/v1/subnets/byValue/office" | jq '.'
DELETE /api/v1/subnets/byKey/{subnet}
Delete subnet mappings whose CIDR begins with the given prefix.
DELETE /api/v1/subnets/byValue/{value}
Delete subnet mappings with the given classification value.
Next Steps
6 - Routing API
GeoIP lookups and IP validation
Overview
The Routing API provides GeoIP information lookup and IP address validation for routing decisions.
Base URL
https://<manager-host>/api/v1/routing
Endpoints
GET /api/v1/routing/geoip
Look up GeoIP information for an IP address.
Query Parameters:
ip - IP address to look up
Success Response (200):
{
"city": {
"name": "Washington"
},
"asn": 64512
}
Errors:
400 - Invalid IP format500 - Backend failure
Caching: Cache-Control: public, max-age=86400 (24 hours)
Example:
curl -s "https://cdn-manager/api/v1/routing/geoip?ip=149.101.100.0"
GET /api/v1/routing/validate
Validate if an IP address is allowed (not blocked).
Query Parameters:
ip - IP address to validate
Success Response (200): Empty body (IP is allowed)
Forbidden Response (403):
Access Denied
Errors:
400 - Invalid IP format500 - Backend failure
Caching: Cache-Control headers included (default: max-age=300, configurable via [tuning] section)
Example:
curl -i "https://cdn-manager/api/v1/routing/validate?ip=149.101.100.0"
Use Cases
GeoIP-Based Routing
Use the /geoip endpoint to determine the geographic location and ASN of an IP address for routing decisions:
# Get location data for routing
IP_INFO=$(curl -s "https://cdn-manager/api/v1/routing/geoip?ip=203.0.113.50")
CITY=$(echo "$IP_INFO" | jq -r '.city.name')
ASN=$(echo "$IP_INFO" | jq -r '.asn')
echo "Routing based on city: $CITY, ASN: $ASN"
IP Validation
Use the /validate endpoint to check if an IP is allowed before processing requests:
# Check if IP is allowed
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
"https://cdn-manager/api/v1/routing/validate?ip=203.0.113.50")
if [ "$RESPONSE" = "200" ]; then
echo "IP is allowed"
elif [ "$RESPONSE" = "403" ]; then
echo "IP is blocked"
fi
Next Steps
7 - Discovery API
Host and namespace discovery
Overview
The Discovery API provides information about discovered hosts and namespaces. Discovery is configured via the Helm chart values.yaml file. Each entry defines a namespace with a list of hostnames.
Base URL
https://<manager-host>/api/v1/discovery
Endpoints
GET /api/v1/discovery/hosts
Return discovered hosts grouped by namespace.
Success Response (200):
{
"directors": [
{ "name": "director-1.example.com" }
],
"edge-servers": [
{ "name": "cdn1.example.com" },
{ "name": "cdn2.example.com" }
]
}
Example:
curl -s "https://cdn-manager/api/v1/discovery/hosts"
GET /api/v1/discovery/namespaces
Return discovery namespaces with their corresponding Confd URIs.
Success Response (200):
[
{
"namespace": "edge-servers",
"confd_uri": "/api/v1/confd/edge-servers"
},
{
"namespace": "directors",
"confd_uri": "/api/v1/confd/directors"
}
]
Example:
curl -s "https://cdn-manager/api/v1/discovery/namespaces"
Configuration
Discovery is configured via the Helm chart values.yaml file under manager.discovery:
manager:
discovery:
- namespace: "directors"
hosts:
- director-1.example.com
- director-2.example.com
- namespace: "edge-servers"
hosts:
- cdn1.example.com
- cdn2.example.com
Each entry defines a namespace with a list of hostnames. Optionally, a pattern field can be specified for regex-based host matching.
Next Steps
8 - Metrics API
Metrics submission and aggregation
Overview
The Metrics API allows submission and retrieval of metrics data from CDN components.
Base URL
https://<manager-host>/api/v1/metrics
Endpoints
POST /api/v1/metrics
Submit metrics data.
Request:
{
"example.com": {
"metric1": 100,
"metric2": 200
}
}
Success: 200 OK
Errors: 500 - Validation/backend errors
Example:
curl -s -X POST "https://cdn-manager/api/v1/metrics" \
-H "Content-Type: application/json" \
-d '{
"example.com": {
"metric1": 100,
"metric2": 200
}
}'
GET /api/v1/metrics
Return aggregated metrics per host.
Response: JSON object with aggregated metrics per host
Note: Metrics are stored per host for up to 5 minutes. Hosts that stop reporting disappear from aggregation after that window. When no metrics are being reported, returns empty object {}.
Example:
curl -s "https://cdn-manager/api/v1/metrics"
Metrics Retention
- Metrics are stored for up to 5 minutes in the aggregation layer
- For long-term metrics storage, data is forwarded to VictoriaMetrics
- Query historical metrics via Grafana dashboards at
/grafana
Next Steps
9 - Configuration API
Configuration document management
Overview
The Configuration API provides endpoints for managing the system configuration document. ETag is supported; send If-None-Match for conditional GET (may return 304).
Operational Note: This API is intended for internal verification only. Behavior is undefined in multi-replica clusters because pods do not coordinate config writes.
Base URL
https://<manager-host>/api/v1/configuration
Endpoints
GET /api/v1/configuration
Retrieve the configuration document.
Success: 200 OK with configuration JSON
Conditional GET: Returns 304 Not Modified if If-None-Match header matches current ETag
Example:
# Get ETag from response headers
etag=$(curl -s -D- "https://cdn-manager/api/v1/configuration" | awk '/ETag/{print $2}')
# Conditional GET - returns 304 if config unchanged
curl -s -H "If-None-Match: $etag" "https://cdn-manager/api/v1/configuration" -o /tmp/cfg.json -w "%{http_code}\n"
PUT /api/v1/configuration
Replace the configuration document.
Request:
{
"feature_flag": false,
"ratio": 0.25
}
Success: 200 OK
Errors:
400 - Invalid configuration format500 - Backend failure
DELETE /api/v1/configuration
Delete the configuration document.
Success: 200 OK
ETag Usage
The configuration API supports ETags for optimistic concurrency control:
# 1. Get current config and ETag
response=$(curl -s -D headers.txt "https://cdn-manager/api/v1/configuration")
etag=$(grep -i ETag headers.txt | cut -d' ' -f2 | tr -d '\r')
# 2. Modify the config as needed
modified_config=$(echo "$response" | jq '.feature_flag = true')
# 3. Update with ETag to prevent overwriting concurrent changes
curl -s -X PUT "https://cdn-manager/api/v1/configuration" \
-H "Content-Type: application/json" \
-H "If-Match: $etag" \
-d "$modified_config"
Next Steps
10 - Operator UI API
Blocked tokens, user agents, and referrers
Overview
The Operator UI API provides read-only helpers exposing curated selection input content for the operator interface.
Query Parameters: search, sort, limit (same as selection input v1)
Note: Stored keys for user agents/referrers are URL-safe base64; responses decode them to human-readable values.
Base URL
https://<manager-host>/api/v1/operator_ui
Endpoints
Blocked Household Tokens
GET /api/v1/operator_ui/modules/blocked_tokens
List all blocked household tokens.
Success Response (200):
[
{
"household_token": "house-001_token-abc",
"expire_time": 1625247600
}
]
GET /api/v1/operator_ui/modules/blocked_tokens/{token}
Get details for a specific blocked token.
Success Response (200):
{
"household_token": "house-001_token-abc",
"expire_time": 1625247600
}
Blocked User Agents
GET /api/v1/operator_ui/modules/blocked_user_agents
List all blocked user agents.
Success Response (200):
[
{
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
},
{
"user_agent": "curl/7.68.0"
}
]
GET /api/v1/operator_ui/modules/blocked_user_agents/{encoded}
Get details for a specific blocked user agent. The path variable is URL-safe base64 encoded.
Example:
# Encode the user agent
ENC=$(python3 -c "import base64; print(base64.urlsafe_b64encode(b'curl/7.68.0').decode().rstrip('='))")
# Get details
curl -s "https://cdn-manager/api/v1/operator_ui/modules/blocked_user_agents/$ENC"
Blocked Referrers
GET /api/v1/operator_ui/modules/blocked_referrers
List all blocked referrers.
Success Response (200):
[
{
"referrer": "https://spam-example.com"
}
]
GET /api/v1/operator_ui/modules/blocked_referrers/{encoded}
Get details for a specific blocked referrer. The path variable is URL-safe base64 encoded.
Example:
# Encode the referrer
ENC=$(python3 -c "import base64; print(base64.urlsafe_b64encode(b'spam-example.com').decode().rstrip('='))")
# Get details
curl -s "https://cdn-manager/api/v1/operator_ui/modules/blocked_referrers/$ENC"
URL-Safe Base64 Encoding
The Operator UI API uses URL-safe base64 encoding for path parameters. To encode values:
Python:
import base64
# Encode
encoded = base64.urlsafe_b64encode(b'value').decode().rstrip('=')
# Decode
decoded = base64.urlsafe_b64decode(encoded + '=' * (-len(encoded) % 4)).decode()
Bash (with openssl):
# Encode
echo -n "value" | openssl base64 -urlsafe | tr -d '='
# Decode
echo "encoded" | openssl base64 -urlsafe -d
Next Steps
11 - OpenAPI Specification
Complete OpenAPI 3.0 specification
Overview
The CDN Manager API is documented using the OpenAPI 3.0 specification. This appendix provides the complete specification for reference and for generating API clients.
OpenAPI Specification (YAML)
openapi: 3.0.3
info:
title: AgileTV CDN Manager API
version: "1.0"
servers:
- url: https://<manager-host>/api
description: CDN Manager API server
paths:
/v1/auth/login:
post:
summary: Login and create session
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Session created
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'401': { description: Unauthorized, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
'500': { description: Internal error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/auth/token:
post:
summary: Exchange session for access token
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TokenRequest'
responses:
'200':
description: Access token
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'401': { description: Unauthorized, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
'500': { description: Internal error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/auth/logout:
post:
summary: Revoke session
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LogoutRequest'
responses:
'200': { description: Revoked, content: { application/json: { schema: { $ref: '#/components/schemas/LogoutResponse' } } } }
'401': { description: Unauthorized, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
'500': { description: Internal error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/selection_input{tail}:
get:
summary: Read selection input
parameters:
- $ref: '#/components/parameters/Tail'
- $ref: '#/components/parameters/Search'
- $ref: '#/components/parameters/Sort'
- $ref: '#/components/parameters/Limit'
responses:
'200': { description: JSON value }
'400': { description: Bad request, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
'404': { description: Not found }
'500': { description: Backend failure }
post:
summary: Merge selection input
parameters:
- $ref: '#/components/parameters/Tail'
- $ref: '#/components/parameters/Ttl'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AnyJson'
responses:
'201': { description: Created, content: { application/json: { schema: { $ref: '#/components/schemas/AnyJson' } } } }
'500': { description: Backend failure }
'503': { description: Service unavailable }
delete:
summary: Delete selection input
parameters:
- $ref: '#/components/parameters/Tail'
responses:
'204': { description: Deleted }
'503': { description: Service unavailable }
/v2/selection_input{tail}:
get:
summary: Read selection input v2
parameters:
- $ref: '#/components/parameters/TailV2'
- $ref: '#/components/parameters/Search'
responses:
'200': { description: JSON value }
'400': { description: Invalid search pattern }
'404': { description: Not found }
'500': { description: Backend failure }
put:
summary: Replace selection input v2
parameters:
- $ref: '#/components/parameters/TailV2'
- $ref: '#/components/parameters/Ttl'
- $ref: '#/components/parameters/CorrelationId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AnyJson'
responses:
'200': { description: Updated }
'500': { description: Backend failure }
post:
summary: Push to selection input v2
parameters:
- $ref: '#/components/parameters/TailV2'
- $ref: '#/components/parameters/Ttl'
- $ref: '#/components/parameters/CorrelationId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AnyJson'
responses:
'200': { description: Pushed }
'500': { description: Backend failure }
delete:
summary: Delete selection input v2
parameters:
- $ref: '#/components/parameters/TailV2'
responses:
'204': { description: Deleted }
'500': { description: Backend failure }
/v1/configuration:
get:
summary: Read configuration
responses:
'200': { description: Configuration, content: { application/json: { schema: { $ref: '#/components/schemas/AnyJson' } } }, headers: { ETag: { schema: { type: string } } } }
'304': { description: Not modified }
'500': { description: Backend failure }
put:
summary: Replace configuration
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AnyJson'
responses:
'200': { description: Replaced }
'500': { description: Backend failure }
delete:
summary: Delete configuration
responses:
'200': { description: Deleted }
'500': { description: Backend failure }
/v1/routing/geoip:
get:
summary: GeoIP lookup
parameters:
- name: ip
in: query
required: true
schema: { type: string }
responses:
'200': { description: GeoIP data, content: { application/json: { schema: { $ref: '#/components/schemas/GeoIpResponse' } } } }
'400': { description: Invalid IP }
'500': { description: Backend failure }
/v1/routing/validate:
get:
summary: Validate routing
parameters:
- name: ip
in: query
required: true
schema: { type: string }
responses:
'200': { description: Allowed }
'403': { description: Access Denied }
'400': { description: Invalid IP }
'500': { description: Backend failure }
/v1/metrics:
post:
summary: Ingest metrics
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MetricsIngress'
responses:
'200': { description: Stored }
'500': { description: Validation/back-end error }
get:
summary: Aggregate metrics
responses:
'200': { description: Aggregated metrics, content: { application/json: { schema: { $ref: '#/components/schemas/AnyJson' } } } }
'500': { description: Backend failure }
/v1/discovery/hosts:
get:
summary: List discovered hosts by namespace
responses:
'200':
description: Discovered hosts keyed by namespace
content:
application/json:
schema:
type: object
additionalProperties:
type: array
items:
$ref: '#/components/schemas/DiscoveryHost'
'500': { description: Backend failure }
/v1/discovery/namespaces:
get:
summary: List discovery namespaces with Confd URIs
responses:
'200':
description: Namespaces with Confd links
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DiscoveryNamespace'
'500': { description: Backend failure }
/v1/datastore:
get:
summary: List datastore keys
responses:
'200': { description: Keys list, content: { application/json: { schema: { type: array, items: { type: string } } } } }
'500': { description: Backend failure }
/v1/datastore/{key}:
get:
summary: Get a JSON value by key
parameters:
- name: key
in: path
required: true
schema: { type: string }
responses:
'200': { description: JSON value, content: { application/json: { schema: { $ref: '#/components/schemas/AnyJson' } } } }
'404': { description: Not found }
'500': { description: Backend failure }
post:
summary: Create a JSON value at key
parameters:
- name: key
in: path
required: true
schema: { type: string }
- $ref: '#/components/parameters/Ttl'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AnyJson'
responses:
'201': { description: Created }
'409': { description: Conflict (already exists) }
'500': { description: Backend failure }
put:
summary: Update/replace a JSON value at key
parameters:
- name: key
in: path
required: true
schema: { type: string }
- $ref: '#/components/parameters/Ttl'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AnyJson'
responses:
'200': { description: Updated }
'404': { description: Not found }
'500': { description: Backend failure }
delete:
summary: Delete a datastore key
parameters:
- name: key
in: path
required: true
schema: { type: string }
responses:
'204': { description: Deleted }
'500': { description: Backend failure }
/v1/subnets:
get:
summary: List all subnet mappings
responses:
'200': { description: Subnet mappings, content: { application/json: { schema: { type: object, additionalProperties: { type: string } } } } }
'500': { description: Backend failure }
put:
summary: Create or update subnet mappings
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties:
type: string
description: Map of CIDR strings to classification values
responses:
'200': { description: Created }
'400': { description: Invalid CIDR format }
'500': { description: Backend failure }
delete:
summary: Delete all subnet mappings
responses:
'204': { description: Deleted }
'500': { description: Backend failure }
/v1/subnets/byKey/{subnet}:
get:
summary: Get subnet mappings by CIDR prefix
parameters:
- name: subnet
in: path
required: true
schema: { type: string }
responses:
'200': { description: Subnet mappings, content: { application/json: { schema: { type: object, additionalProperties: { type: string } } } } }
'500': { description: Backend failure }
delete:
summary: Delete subnet mappings by CIDR prefix
parameters:
- name: subnet
in: path
required: true
schema: { type: string }
responses:
'204': { description: Deleted }
'500': { description: Backend failure }
/v1/subnets/byValue/{value}:
get:
summary: Get subnet mappings by value
parameters:
- name: value
in: path
required: true
schema: { type: string }
responses:
'200': { description: Subnet mappings, content: { application/json: { schema: { type: object, additionalProperties: { type: string } } } } }
'500': { description: Backend failure }
delete:
summary: Delete subnet mappings by value
parameters:
- name: value
in: path
required: true
schema: { type: string }
responses:
'204': { description: Deleted }
'500': { description: Backend failure }
/v1/operator_ui/modules/blocked_tokens:
get:
summary: List blocked tokens
parameters:
- $ref: '#/components/parameters/Search'
- $ref: '#/components/parameters/Sort'
- $ref: '#/components/parameters/Limit'
responses:
'200': { description: Blocked tokens, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/BlockedToken' } } } } }
'400': { description: Parse error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/operator_ui/modules/blocked_tokens/{token}:
get:
summary: Get blocked token
parameters:
- name: token
in: path
required: true
schema: { type: string }
responses:
'200': { description: Blocked token, content: { application/json: { schema: { $ref: '#/components/schemas/BlockedToken' } } } }
'404': { description: Not found }
'400': { description: Parse error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/operator_ui/modules/blocked_user_agents:
get:
summary: List blocked user agents
parameters:
- $ref: '#/components/parameters/Search'
- $ref: '#/components/parameters/Sort'
- $ref: '#/components/parameters/Limit'
responses:
'200': { description: Blocked user agents, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/BlockedUserAgent' } } } } }
'400': { description: Parse error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/operator_ui/modules/blocked_user_agents/{encoded}:
get:
summary: Get blocked user agent
parameters:
- name: encoded
in: path
required: true
schema: { type: string }
responses:
'200': { description: Blocked user agent, content: { application/json: { schema: { $ref: '#/components/schemas/BlockedUserAgent' } } } }
'400': { description: Parse error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/operator_ui/modules/blocked_referrers:
get:
summary: List blocked referrers
parameters:
- $ref: '#/components/parameters/Search'
- $ref: '#/components/parameters/Sort'
- $ref: '#/components/parameters/Limit'
responses:
'200': { description: Blocked referrers, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/BlockedReferrer' } } } } }
'400': { description: Parse error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/operator_ui/modules/blocked_referrers/{encoded}:
get:
summary: Get blocked referrer
parameters:
- name: encoded
in: path
required: true
schema: { type: string }
responses:
'200': { description: Blocked referrer, content: { application/json: { schema: { $ref: '#/components/schemas/BlockedReferrer' } } } }
'400': { description: Parse error, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
/v1/health/alive:
get:
summary: Liveness check
responses:
'200': { description: Alive, content: { application/json: { schema: { $ref: '#/components/schemas/HealthStatus' } } } }
/v1/health/ready:
get:
summary: Readiness check
responses:
'200': { description: Ready, content: { application/json: { schema: { $ref: '#/components/schemas/HealthStatus' } } } }
'503': { description: Unready, content: { application/json: { schema: { $ref: '#/components/schemas/HealthStatus' } } } }
components:
parameters:
Tail:
name: tail
in: path
required: true
schema: { type: string }
TailV2:
name: tail
in: path
required: true
schema: { type: string }
Search:
name: search
in: query
required: false
schema: { type: string }
Sort:
name: sort
in: query
required: false
schema: { type: string, enum: [asc, desc] }
Limit:
name: limit
in: query
required: false
schema: { type: integer, minimum: 1 }
Ttl:
name: ttl
in: query
required: false
schema: { type: string, description: Humantime duration }
CorrelationId:
name: correlation_id
in: query
required: false
schema: { type: string }
schemas:
LoginRequest:
type: object
required: [email, password]
properties:
email: { type: string, format: email }
password: { type: string, format: password }
LoginResponse:
type: object
properties:
session_id: { type: string }
session_token: { type: string }
verified_at: { type: string, format: date-time }
expires_at: { type: string, format: date-time }
LogoutRequest:
type: object
required: [session_id]
properties:
session_id: { type: string }
session_token: { type: string }
LogoutResponse:
type: object
properties:
status: { $ref: '#/components/schemas/StatusValue' }
TokenRequest:
type: object
required: [session_id, session_token, grant_type]
properties:
session_id: { type: string }
session_token: { type: string }
scope: { type: string }
grant_type: { type: string, enum: [session] }
TokenResponse:
type: object
required: [access_token, scope, expires_in, token_type]
properties:
access_token: { type: string }
scope: { type: string }
expires_in: { type: integer, format: int64 }
token_type: { type: string, enum: [bearer] }
ErrorResponse:
type: object
properties:
message: { type: string }
AnyJson:
description: Arbitrary JSON value
MetricsIngress:
type: object
additionalProperties:
type: object
additionalProperties: { type: number }
GeoIpResponse:
type: object
properties:
city:
type: object
properties:
name: { type: string }
asn: { type: integer }
is_anonymous: { type: boolean }
BlockedToken:
type: object
properties:
household_token: { type: string }
expire_time: { type: integer, format: int64 }
BlockedUserAgent:
type: object
properties:
user_agent: { type: string }
BlockedReferrer:
type: object
properties:
referrer: { type: string }
DiscoveryHost:
type: object
properties:
name: { type: string }
DiscoveryNamespace:
type: object
properties:
namespace: { type: string }
confd_uri: { type: string }
HealthStatus:
type: object
properties:
status: { $ref: '#/components/schemas/StatusValue' }
StatusValue:
type: string
enum: [Ok, Fail]
Using the OpenAPI Specification
Generating API Clients
The OpenAPI specification can be used to generate client libraries in multiple languages:
Using openapi-generator:
# Generate Python client
openapi-generator generate -i openapi.yaml -g python -o ./python-client
# Generate TypeScript client
openapi-generator generate -i openapi.yaml -g typescript-axios -o ./typescript-client
# Generate Go client
openapi-generator generate -i openapi.yaml -g go -o ./go-client
Using swagger-codegen:
swagger-codegen generate -i openapi.yaml -l python -o ./python-client
Validating the Specification
To validate the OpenAPI specification:
# Using swagger-cli
swagger-cli validate openapi.yaml
# Using spectral
spectral lint openapi.yaml
Next Steps