API Reference
All endpoints exposed by the Universal Guardrail FastAPI backend.
Base URL: http://localhost:8000
Authentication
Every request needs either credential:
x-api-keyheader — a gateway key fromGUARDRAIL_API_KEYS, or aGUARDRAIL-…license key (get one by email →)Authorization: Bearer <JWT>— fromPOST /api/auth/token
Note: if GUARDRAIL_API_KEYS is unset (dev mode), key auth is disabled and
JWT login still works. /api/auth/* endpoints are public by design.
curl -H "x-api-key: your-api-key" http://localhost:8000/api/analytics
Requests missing valid credentials receive 401 Unauthorized. The health check
endpoint GET / does not require auth.
/api/license/purchase
Self-service key issuance — no auth required. Generates a signed
GUARDRAIL-… license key and emails it to the buyer (the key is never shown on-screen
or stored server-side in plaintext). Rate-limited to 5 req/min. Requires
LICENSE_SECRET; email delivery uses SMTP_* env vars (dev mode without
SMTP writes the email to logs/outbound_email.log).
Request Body
| Field | Type | Description |
|---|---|---|
email required | string | Delivery address (disposable domains rejected) |
plan | string | "starter" (default, 5 seats / 3 apps) | "pro" (25/15) |
"enterprise" (200/50). Licenses run 365 days. |
POST /api/license/purchase
{ "email": "you@company.com", "plan": "pro" }
Response
{ "purchase": "ok", "sent_to": "you@company.com", "plan": "pro", "emailed": true }
# dev mode without SMTP configured:
{ "purchase": "ok", "emailed": false, "note": "check logs/outbound_email.log" }
/api/validate
Validates a prompt through all configured Input Rails without calling an LLM. Use this for standalone guardrail checks. Rate-limited to 50 req/min.
Request Body
| Field | Type | Description |
|---|---|---|
prompt required |
string | The user input to validate |
role |
string | RBAC role for the user. Default: "intern". Must match a role in
guard_config.yaml
|
history |
array | Conversation history (array of message objects). Currently unused for validation but accepted. |
POST /api/validate
{
"prompt": "What is the company refund policy?",
"role": "user"
}
Response
# Allowed
{ "allowed": true, "response": null, "sanitized_prompt": "What is the company refund policy?" }
# Blocked
{ "allowed": false, "response": "⚠️ Security Alert: Potential Secret/API Key detected." }
# Flagged (allowed but with warning)
{ "allowed": true, "response": "⚠️ Warning: Abusive language detected.", "sanitized_prompt": "..." }
/api/chat
Full guardrail pipeline: Input Rails → LLM → Output Rails. The recommended integration point for most applications. Rate-limited to 20 req/min.
Request Body
| Field | Type | Description |
|---|---|---|
prompt required |
string | The user message to process |
role |
string | RBAC role. Default: "intern" |
history |
array | Prior conversation messages for context |
POST /api/chat
{
"prompt": "Explain our return policy",
"role": "user",
"history": [
{ "role": "user", "content": "Hello" },
{ "role": "assistant", "content": "Hi! How can I help?" }
]
}
Response
# Clean response from LLM (passed output guardrail)
{ "allowed": true, "response": "Our return policy is...", "source": "llm" }
# Blocked at input
{ "allowed": false, "response": "Security Alert: Jailbreak attempt detected.", "source": "guardrail" }
# Blocked at output (after retries exhausted)
{ "allowed": false, "response": "Quality Alert: Hallucination detected.", "source": "output_guardrail" }
The source field indicates where the response
originated: guardrail | llm | output_guardrail |
circuit_breaker | fail_closed (a guard check itself errored — request
blocked safe-mode) | system (meta error)
/api/upload
Multi-modal file scanning for images, audio, and video. Accepts multipart/form-data. Rate-limited to 10 req/min. Max file size: 10 MB.
Form Fields
| Field | Type | Description |
|---|---|---|
file required |
file | The file to scan |
file_type required |
string | "image" | "audio" | "video" |
Accepted MIME types: image/png,
image/jpeg, image/jpg, audio/wav, audio/x-wav,
audio/mpeg, audio/mp3, video/mp4,
video/quicktime. The server never trusts the client-declared type alone: the
filename extension must agree with file_type, otherwise 415.
# cURL example — image scan curl -X POST http://localhost:8000/api/upload \ -H "x-api-key: your-key" \ -F "file=@screenshot.png" \ -F "file_type=image"
Response
# Image: blocked (GPS metadata found)
{ "allowed": false, "response": "🛡️ IMAGE BLOCKED: Privacy Risk - GPS Metadata found in image." }
# Audio: allowed (with transcript)
{ "allowed": true, "response": null, "transcript": "Hello, how do I reset my password?", "is_deepfake": false }
# Audio: deepfake detected
{ "allowed": false, "response": "🛡️ AUDIO BLOCKED: Potential AI-Generated/Cloned Voice Detected.", "is_deepfake": true }
/api/config
Read or update the active guard_config.yaml. Changes
are applied immediately via hot-reload — no container restart required.
# Read current config
curl -H "x-api-key: your-key" http://localhost:8000/api/config
# Update config (POST with full YAML as JSON object)
curl -X POST http://localhost:8000/api/config \
-H "x-api-key: your-key" \
-H "Content-Type: application/json" \
-d '{ "global": { "deployment_mode": "passive" }, ... }'
The config endpoint automatically backs up the previous config and rolls back on any validation error.
/api/analytics
Returns aggregated threat statistics from the audit log for the
past N hours. Works with either credential: x-api-key or
Authorization: Bearer <JWT>.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
hours |
integer | 24 | Lookback window in hours |
# Response
{
"allowed": 1823,
"blocked": 47,
"flagged": 12,
"total_events": 1882,
"avg_latency_ms": 38.4,
"cost_saved_usd": 0.021,
"threat_types": { "Jailbreak Detection": 18, "PII Redaction": 14, ... },
"top_offenders": [{ "user": "intern", "violations": 22 }],
"recent_violations": [...],
"trend": [{ "time": "2026-02-24 10:00", "allowed": 142, "blocked": 5, "avg_latency_ms": 36.1 }]
}
/ws/telemetry
Real-time WebSocket stream broadcasting threat telemetry every 2 seconds to all connected SOC dashboard clients. Data includes live metrics from all 5 threat groups.
// JavaScript example
// Browsers cannot set headers on WebSocket, so auth goes in the query string:
const ws = new WebSocket('ws://localhost:8000/ws/telemetry?api_key=your-key');
// or: ?token= from POST /api/auth/token
ws.onmessage = (event) => {
const telemetry = JSON.parse(event.data);
// telemetry.group1 — Prompt & LLM Firewall
// telemetry.group2 — Model & Data Security
// telemetry.group3 — Offensive AI Defense
// telemetry.group4 — AI Supply Chain
// telemetry.group5 — Physical & Side-Channel
};