Admin API Reference
Manage gateway resources through the JWT-protected REST API. Database and Control Plane modes allow writes; File, Data Plane, and Mesh are read-only.
Authentication
Use an HS256 JWT signed with FERRUM_ADMIN_JWT_SECRET (at least 32 characters). Required claims are iss, sub, iat, nbf, exp, jti, and role (viewer, operator, or admin). The default issuer is ferrum-edge.
The example below uses Node.js built-in crypto and an already-configured gateway secret. It issues a 15-minute admin token scoped to ferrum. If your gateway configures an audience or a different issuer, use those matching values. Namespace-scoped requests use X-Ferrum-Namespace; multi-namespace CPs require a matching ns claim.
# Export FERRUM_ADMIN_JWT_SECRET with the same value used by your gateway.
export ADMIN_TOKEN=$(node <<'NODE'
const { createHmac, randomUUID } = require('node:crypto');
const secret = process.env.FERRUM_ADMIN_JWT_SECRET;
if (!secret || secret.length < 32) throw new Error('Set the gateway JWT secret (32+ characters)');
const now = Math.floor(Date.now() / 1000);
const claims = { iss: 'ferrum-edge', sub: 'website-example', role: 'admin',
ns: ['ferrum'], iat: now, nbf: now, exp: now + 900, jti: randomUUID() };
const encode = value => Buffer.from(JSON.stringify(value)).toString('base64url');
const body = `${encode({ alg: 'HS256', typ: 'JWT' })}.${encode(claims)}`;
console.log(`${body}.${createHmac('sha256', secret).update(body).digest('base64url')}`);
NODE
)
curl -H "Authorization: Bearer $ADMIN_TOKEN" \
-H "X-Ferrum-Namespace: ferrum" http://localhost:9000/proxies
Probes and Metrics
| Endpoint | Access / purpose |
|---|---|
GET /live | Unauthenticated liveness; 200 while the process/admin listener is alive |
GET /health, /status | Coarse status + ready without auth; full diagnostics with admin JWT, metrics bearer token, or allowed metrics CIDR |
GET /metrics | Prometheus text; requires admin JWT, metrics bearer token, or allowed metrics CIDR |
GET /admin/metrics | Admin JWT; gateway metrics JSON |
GET /metrics/runtime | Admin JWT; process/host state, traffic windows, pools, DNS, and overload |
GET /charges | Admin JWT; chargeback data |
Resource Endpoints
| Path | Operations |
|---|---|
/proxies, /consumers, /upstreams | GET list, POST create; GET/PUT/DELETE on /:id |
/plugins | GET built-in plugin types |
/plugins/config | GET list, POST create; GET/PUT/DELETE on /:id |
/consumers/:id/credentials/:type | POST append, PUT replace, DELETE credential type |
/api-specs | Import, list, replace, and delete OpenAPI-managed resources |
/namespaces | Manage namespace registry and tenant resources |
/backup, /restore?confirm=true | GET export; POST replace namespace configuration with explicit confirmation |
/batch | POST atomic additive resource graph; not a list of arbitrary HTTP operations |
/audit, /cluster, /tls | Audit, CP/DP topology, and TLS lifecycle surfaces; see the full reference |
Lists support pagination. For request/response schemas, permissions, and the complete endpoint inventory, use the OpenAPI specification and full Admin API reference.
Create a Proxy
curl -X POST http://localhost:9000/proxies \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "X-Ferrum-Namespace: ferrum" \
-H "Content-Type: application/json" \
-d '{
"listen_path": "/my-api",
"backend_scheme": "http",
"backend_host": "localhost",
"backend_port": 3000,
"strip_listen_path": true,
"labels": {"provisioned-by": "website-example"}
}'
Save the returned id. Plugin associations use plugins: [{"plugin_config_id": "…"}]. Creating a proxy-scoped plugin as below attaches it automatically. Labels require Edge v0.9.5+ and are informational metadata.
Attach API-Key Authentication
# Replace YOUR_PROXY_ID with the returned proxy ID.
curl -X POST http://localhost:9000/plugins/config \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "X-Ferrum-Namespace: ferrum" \
-H "Content-Type: application/json" \
-d '{
"plugin_name": "key_auth",
"scope": "proxy",
"proxy_id": "YOUR_PROXY_ID",
"config": {"key_location": "header:X-API-Key"}
}'
File-mode configs must declare both the plugin’s proxy_id and its proxy association. Global plugins need no proxy association.
Create a Consumer
curl -X POST http://localhost:9000/consumers \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "X-Ferrum-Namespace: ferrum" \
-H "Content-Type: application/json" \
-d '{
"username": "mobile-app",
"custom_id": "app-v2",
"credentials": {
"keyauth": [{"key": "replace-with-a-random-client-api-key"}]
}
}'
Credential map keys are keyauth, basicauth, jwt, hmac_auth, and mtls_auth. Values are rotation arrays. JWT/HMAC entries contain secret, not subject. Ordinary reads redact secrets and omit Basic credentials.
Create an Upstream
curl -X POST http://localhost:9000/upstreams \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "X-Ferrum-Namespace: ferrum" \
-H "Content-Type: application/json" \
-d '{
"name": "api-backend-pool",
"algorithm": "least_connections",
"targets": [
{"host": "api-1.internal", "port": 3000, "weight": 100},
{"host": "api-2.internal", "port": 3000, "weight": 50}
]
}'
Set a proxy’s upstream_id to the returned ID to use the pool. Active/passive health checks use the schema in the upstream reference.
Batch Create
curl -X POST http://localhost:9000/batch \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "X-Ferrum-Namespace: ferrum" \
-H "Content-Type: application/json" \
-d '{
"proxies": [{
"id": "batch-api", "listen_path": "/batch-api",
"backend_scheme": "http", "backend_host": "localhost", "backend_port": 3000
}],
"consumers": [{"username": "batch-client"}],
"plugin_configs": [],
"upstreams": []
}'
Error Handling
| Status | Meaning |
|---|---|
| 400 | Malformed input, unknown fields, or validation failure |
| 401 | Missing or invalid token |
| 403 | Role/namespace denied, or read-only mode |
| 404 | Resource does not exist |
| 409 | Conflicting resource or state |
| 503 | Unavailable dependency or admission guard; inspect the response before retrying a mutation |
{"error": "description of the rejected request"}