API Explorer
An interactive-style overview of every OGuardAI v1 endpoint, with method, purpose, required scope, and a copy-paste curl example built from the real request shape.
This page is a hands-on catalog of the OGuardAI HTTP API. Every endpoint below exists in the running server. Each entry gives the method, its purpose, the auth scope it requires, and a curl example that matches the real request body. Copy a call, set your key, and run it against a live server.
For field-by-field request and response definitions, see the API Reference. This page is the fast path: find the endpoint, run the curl, read the response.
Before you start
- Base URL. The server listens on
http://localhost:3000by default (configurable viaserver.host/server.port). The Docker quick start (deploy/docker/docker-compose.yml) also publishes it on3000; the HA compose stack (deploy/docker/docker-compose.ha.yml) fronts it with nginx on8080instead. Replace the host and port in the examples with your deployment. - Content type. JSON endpoints expect and return
application/json. The file and image endpoints usemultipart/form-data. - Tokens. OGuardAI replaces detected values with semantic tokens. The emitted form is always three segments,
{{type:id:cap}}(for example{{email:e_001:5873cc5722d8}}): a lowercase type, an id stable within a session, and a per-token capability cap. Every transform emits the cap, RAG or not; it is matched exactly at rehydrate and fails closed, so a capless{{type:id}}token or a guessed id never resolves. RAG calls that pass acorpus_idderive a deterministic corpus-scoped id and cap (a 24-hex id suffix and a 32-hex cap) so the same value maps to the same token across documents and queries; other calls use a per-requestprefix_NNNid with a 12-hex random cap. The curl examples below show the full form, because only that form round-trips. - Session state.
transformreturns a sealedsession_stateblob. Carry it intorehydrate(and into the RAG context and answer calls) so restoration is deterministic. It holds the token map, not raw text you need to persist elsewhere. - Retries. The mutating, non-streaming endpoints (transform, batch transform, RAG ingest, RAG delete, revoke, session delete) accept an optional
Idempotency-Keyheader so a network retry never executes the same mutation twice. See Idempotent retries in the reference.
Authentication
When auth is enabled, send either an API key or a JWT. When auth is disabled (local dev), the header is optional.
# API key
curl -H "X-API-Key: $GUARDAI_API_KEY" http://localhost:3000/v1/health
# JWT bearer token
curl -H "Authorization: Bearer eyJhbG..." http://localhost:3000/v1/healthEach protected endpoint requires a specific scope (transform, rehydrate, detect, policy, batch, rag, revoke, global_revoke, session, admin), and the detect_values scope additionally authorizes returning raw detected values from /v1/detect and /v1/batch/detect. revoke erases within the caller's tenant; an untenanted caller needs global_revoke (which admin implies) for a cross-tenant revocation. The scope column in the catalog below tells you which one. The admin scope implies full access. See Authentication for how scopes bind to keys and tokens.
Is there an OpenAPI spec to load into Swagger UI?
Yes. The repository ships a single OpenAPI 3.1 document at schemas/openapi.yaml. It is the canonical description of every route the server mounts, and apps/server/tests/openapi_contract.rs enforces that the paths it lists and the mounted router never drift. Point Swagger UI, Redoc, or any OpenAPI import at that file.
The repository also ships per-endpoint JSON Schema (draft 2020-12) files under schemas/api/ for validating request and response bodies in your own client, or for driving a schema-aware editor:
schemas/api/transform-request.json
schemas/api/transform-response.json
schemas/api/rehydrate-request.json
schemas/api/rehydrate-response.json
schemas/api/detect-request.json
schemas/api/detect-response.json
schemas/api/session-status-request.json
schemas/api/session-status-response.json
schemas/api/session-delete-request.json
schemas/api/session-delete-response.json
schemas/api/capabilities-response.json
schemas/api/health-response.json
schemas/api/error-response.jsonFor endpoints without a dedicated JSON Schema file (batch, RAG, revocation, evaluate-policy, streaming, file, image), schemas/openapi.yaml plus the curl examples on this page and the API Reference are the contract. Everything below is the interactive reference: the curl call is the "try it" button.
Endpoint catalog
| Method | Path | Purpose | Scope |
|---|---|---|---|
POST | /v1/transform | Replace detected entities with semantic tokens | transform |
POST | /v1/rehydrate | Restore tokens in LLM output to real values | rehydrate |
POST | /v1/detect | Detect entities without transforming | detect |
POST | /v1/evaluate-policy | Dry-run a policy against a list of entities | policy |
POST | /v1/transform/stream | Streaming transform (SSE) | transform |
POST | /v1/rehydrate/stream | Streaming rehydrate (SSE) | rehydrate |
POST | /v1/batch/transform | Transform many inputs in one request | batch |
POST | /v1/batch/detect | Detect entities across many inputs | batch |
POST | /v1/rag/ingest | Chunk and tokenize a document for a vector store | rag |
POST | /v1/rag/ingest/batch | Ingest multiple documents in one request | rag |
POST | /v1/rag/query | Tokenize a user query for retrieval | rag |
POST | /v1/rag/context | Tokenize retrieved chunks for the prompt | rag |
POST | /v1/rag/answer | Rehydrate an LLM answer built from RAG context | rag |
POST | /v1/rag/delete | Revoke every value in a RAG session (erasure) | revoke |
POST | /v1/revoke | Revoke one entity value (future rehydrate returns [DELETED]) | revoke |
POST | /v1/revoke/bulk | Revoke many entity values at once | revoke |
GET | /v1/revocations/count | Scope-local count of revoked entities | admin |
POST | /v1/sessions/status | Report a session's lifecycle state | session |
DELETE | /v1/sessions | Invalidate a server-held session | session |
POST | /v1/transform/file | Transform an uploaded file (multipart) | transform |
POST | /v1/rehydrate/file | Rehydrate an uploaded file (multipart) | rehydrate |
POST | /v1/transform/image | Tokenize text detected in an image (multipart, OCR) | transform |
POST | /v1/redact/image | Return the image with PII regions blacked out (multipart, OCR) | transform |
POST | /v1/entity-types | Validate a custom entity-type name (nothing persisted) | transform |
POST | /v1/patterns | Validate a custom regex detection pattern (nothing persisted) | transform |
GET | /v1/health | Health status and component breakdown | any authed |
GET | /v1/capabilities | Supported entity types, languages, detectors, restore modes | any authed |
GET | /v1/diagnostics | Runtime diagnostics (detector mode, policies, config) | admin |
GET | /metrics | Prometheus metrics | admin |
POST | /v1/admin/policy/validate | Validate policy YAML in a directory | admin |
POST | /v1/admin/policy/reload | Hot-reload the policy directory (atomic, fail-closed) | admin |
GET | /livez | Liveness probe | public |
GET | /readyz | Readiness probe | public |
Quick round trip
The core loop is protect, then generate, then restore. Two calls of yours wrap one call to your LLM.
# 1. Protect: raw text in, tokenized safe_text plus session_state out
curl -sS http://localhost:3000/v1/transform \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "Bitte antworten Sie Frau Julia Schneider unter julia@firma.example.",
"language": "de",
"policy": "german-support",
"context": { "destination": "external_llm" }
}'
# The response carries safe_text (send this to your LLM) and session_state.
# 2. Your LLM generates a reply that still contains the tokens.
# 3. Restore: LLM output plus session_state in, restored_text out
curl -sS http://localhost:3000/v1/rehydrate \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"output": "Sehr geehrte {{person:p_001:9f2c4a7b1e8d}}, wir melden uns unter {{email:e_001:5873cc5722d8}}.",
"session_state": "<paste session_state from step 1>",
"restore_mode": "full",
"output_channel": "customer_email"
}'The request field is input on transform but output on rehydrate, because rehydrate's input is the LLM's output. The tokenized field is safe_text after transform and restored_text after rehydrate.
Core endpoints
POST /v1/transform
Detects entities in the input and replaces them with semantic tokens. Accepts plain text, structured JSON (input_type: "json" with input_json), or OpenAI-style chat arrays (input_type: "chat_messages" with input_messages).
curl -sS http://localhost:3000/v1/transform \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "Contact Julia Schneider at julia@firma.example or +49 30 12345678.",
"input_type": "text",
"language": "de",
"policy": "german-support",
"detectors": ["builtin_regex", "python_ner"],
"protection_overrides": { "phone": "masked" },
"context": {
"destination": "external_llm",
"caller_role": "support_agent",
"caller_purpose": "support"
}
}'Response highlights: safe_text (send this to the LLM), session_state (carry into rehydrate), entity_context (type-only metadata safe for a system prompt, never raw values), entities (full diagnostic detail for your side), stats, and detector_mode.
Notes:
detectorsacceptsbuiltin_regexandpython_ner. Omit it to use the server's configured default.python_nerrequires the NER sidecar to be configured.protection_overridesandrestoremodes are one offull,partial,masked,formatted,abstract,none.context.destinationis one of the built-insexternal_llm,local_llm,vector_store,tool,agent_memory, or any custom[a-z0-9_-]+label.- In non-dev auth,
caller_roleandcaller_purposeare taken from the credential, not from the request body, so a caller cannot self-assert a privileged role.
POST /v1/rehydrate
Restores tokens in LLM output back to real values, governed by the restore mode and the output channel. session_state is required.
curl -sS http://localhost:3000/v1/rehydrate \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"output": "Sehr geehrte {{person:p_001:9f2c4a7b1e8d}}, Ihre Anfrage zu {{email:e_001:5873cc5722d8}} ist erledigt.",
"session_state": "<session_state from transform>",
"restore_mode": "full",
"restore_overrides": { "e_001": "masked" },
"output_channel": "customer_email"
}'output_channel selects which restore rules apply. Built-in channels are user_output, internal_summary, customer_email, tool_payload, export, log_safe. Any other [a-z0-9_-]+ name is a custom channel and is fail-closed: it restores nothing unless the policy defines its channel_rules.
POST /v1/detect
Detects entities and returns spans, types, and confidence. No transformation, no session state.
curl -sS http://localhost:3000/v1/detect \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "Reach me at julia@firma.example or IBAN DE89 3704 0044 0532 0130 00.",
"language": "de",
"detectors": ["builtin_regex"],
"entity_types": ["email", "iban"],
"threshold": 0.5
}'threshold is the minimum confidence in [0.0, 1.0] (default 0.5). entity_types filters the output to specific types.
POST /v1/evaluate-policy
Dry-runs a policy against a list of entities and returns the decision, protection level, matched rule, and restore mode per entity. Nothing is stored. Omit value on an entity to evaluate by type only and keep raw PII out of the request; the decision then falls back to the type-based rule, which is at least as restrictive.
curl -sS http://localhost:3000/v1/evaluate-policy \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"policy": "strict-pii",
"entities": [
{ "type": "email", "value": "julia@firma.example" },
{ "type": "iban" }
],
"context": { "destination": "external_llm", "caller_role": "analyst" },
"output_channel": "internal_summary"
}'Note the entity field is type, not entity_type, on this endpoint.
Streaming endpoints
Both streaming endpoints take the same JSON body as their non-streaming counterparts and return a text/event-stream (SSE) response. Use curl -N to keep the stream open.
POST /v1/transform/stream
curl -N http://localhost:3000/v1/transform/stream \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"input": "Contact Julia Schneider at julia@firma.example.",
"language": "de",
"policy": "german-support"
}'POST /v1/rehydrate/stream
curl -N http://localhost:3000/v1/rehydrate/stream \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"output": "Sehr geehrte {{person:p_001:9f2c4a7b1e8d}}, Danke.",
"session_state": "<session_state from transform>",
"restore_mode": "full",
"output_channel": "user_output"
}'Batch endpoints
POST /v1/batch/transform
Transforms an array of items in one request. Each item can be text, JSON, or chat. session_mode is independent (each item gets its own session) or shared (the token map carries across all items). Batch size is capped by the server's limits.max_batch_size (see /v1/capabilities).
curl -sS http://localhost:3000/v1/batch/transform \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_mode": "independent",
"policy": "german-support",
"items": [
{ "text": "Frau Julia Schneider, julia@firma.example", "language": "de" },
{ "text": "Herr Max Mustermann, max@firma.example", "language": "de" }
]
}'POST /v1/batch/detect
Detects entities across many items. Set include_values: false so results omit the raw detected value and return only types and spans, useful when the caller must not receive raw PII back.
curl -sS http://localhost:3000/v1/batch/detect \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"include_values": false,
"policy": "strict-pii",
"items": [
{ "text": "julia@firma.example", "language": "de" },
{ "text": "+49 30 12345678", "language": "de" }
]
}'RAG endpoints
The RAG flow is: ingest a document into safe chunks, query to tokenize the user question, context to tokenize the retrieved chunks, then answer to restore the model's reply. Use a shared corpus_id across ingest, query, and context so the same value tokenizes to the same id.
POST /v1/rag/ingest
Chunks a document and tokenizes each chunk for vector-store ingestion. Returns per-chunk safe_text, a chunk_session_state per chunk, and a document-level session_state.
curl -sS http://localhost:3000/v1/rag/ingest \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Kunde Julia Schneider (julia@firma.example) meldete am 3. Mai einen Defekt.",
"chunking_strategy": "paragraph",
"policy": "german-support",
"language": "de",
"corpus_id": "support-2026"
}'chunk_size (used by fixed and sliding-window strategies) must be at least 100 when supplied.
POST /v1/rag/ingest/batch
Ingests several documents in one call. Each item is a full ingest request body and yields its own
chunks and sealed session; a failed item carries a per-item error and never fails the batch. Capped
by limits.max_batch_size.
curl -sS http://localhost:3000/v1/rag/ingest/batch \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "text": "Kunde Julia Schneider meldete einen Defekt.", "policy": "german-support", "language": "de", "corpus_id": "support-2026" },
{ "text": "Rueckruf an julia@firma.example vereinbart.", "policy": "german-support", "language": "de", "corpus_id": "support-2026" }
]
}'POST /v1/rag/query
Tokenizes the user query so it can hit the vector store without leaking PII.
curl -sS http://localhost:3000/v1/rag/query \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Was hat Julia Schneider gemeldet?",
"policy": "german-support",
"language": "de",
"corpus_id": "support-2026"
}'POST /v1/rag/context
Tokenizes the chunks you retrieved from the vector store and merges their tokens into the session. Pass the session_state from the query call. Optional access_level plus chunk_classifications filter out chunks above the requested level (fail-closed).
curl -sS http://localhost:3000/v1/rag/context \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"chunks": ["Julia Schneider meldete einen Defekt an julia@firma.example."],
"session_state": "<session_state from /v1/rag/query>",
"policy": "german-support",
"language": "de",
"access_level": "internal",
"chunk_classifications": ["internal"],
"corpus_id": "support-2026"
}'POST /v1/rag/answer
Rehydrates the LLM's answer using the accumulated session. Pass the session_state from the context call and the model's tokenized answer.
curl -sS http://localhost:3000/v1/rag/answer \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"answer": "{{person:p_3e7a1c5f9b2d8e4a6c0f2b8d:7c1e9a3f5d2b8c4e6a0d1f3b5e7c9a2f}} meldete einen Defekt und ist unter {{email:e_6b2d8f4a0c9e1b7d3f5a2c8e:2a8c4e6f0b1d3a5c7e9f2b4d6a8c0e1f}} erreichbar.",
"session_state": "<session_state from /v1/rag/context>",
"output_channel": "user_output"
}'POST /v1/rag/delete
Erasure: revokes every value carried in the given session so any later rehydrate resolves them to [DELETED]. To forget one document, pass that document's ingest session_state. Requires the revoke scope.
curl -sS http://localhost:3000/v1/rag/delete \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "session_state": "<ingest session_state to forget>" }'Revocation endpoints
POST /v1/revoke
Revokes one entity value so future rehydration returns [DELETED] instead of the original. Only an HMAC digest of the value is stored, never the raw value.
curl -sS http://localhost:3000/v1/revoke \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "entity_type": "email", "value": "julia@firma.example" }'POST /v1/revoke/bulk
Revokes several values in one call.
curl -sS http://localhost:3000/v1/revoke/bulk \
-H "X-API-Key: $GUARDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"entities": [
{ "entity_type": "email", "value": "julia@firma.example" },
{ "entity_type": "person", "value": "Julia Schneider" },
{ "entity_type": "phone", "value": "+49 30 12345678" }
]
}'GET /v1/revocations/count
Returns the count of revoked entities in the caller's own scope: a tenant-authenticated caller sees only its own tenant's revocations, and an unscoped platform-admin caller sees only the unscoped revocations. Requires the admin scope. The store partitions digests by tenant, so one tenant can never observe another tenant's revocation volume.
curl -sS http://localhost:3000/v1/revocations/count \
-H "X-API-Key: $GUARDAI_ADMIN_KEY"File and image endpoints
These endpoints use multipart/form-data rather than JSON.
POST /v1/transform/file
Uploads a file, extracts its text, and tokenizes it. Form fields: file (required), policy (optional), language (optional).
curl -sS http://localhost:3000/v1/transform/file \
-H "X-API-Key: $GUARDAI_API_KEY" \
-F "file=@complaint.pdf" \
-F "policy=german-support" \
-F "language=de"POST /v1/rehydrate/file
Rehydrates an uploaded file's tokenized text using a prior session_state. Multipart, rehydrate scope.
POST /v1/transform/image
Runs OCR on an uploaded image and tokenizes the detected text. Form field: image. Requires Tesseract OCR installed on the server, otherwise the endpoint returns GUARDAI_OCR_UNAVAILABLE.
curl -sS http://localhost:3000/v1/transform/image \
-H "X-API-Key: $GUARDAI_API_KEY" \
-F "image=@scan.png"POST /v1/redact/image
Returns the uploaded image as image/png with detected PII regions blacked out. Form field: image. Also requires Tesseract OCR.
curl -sS http://localhost:3000/v1/redact/image \
-H "X-API-Key: $GUARDAI_API_KEY" \
-F "image=@scan.png" \
--output redacted.pngHealth and introspection
GET /v1/health
Returns status (healthy, degraded, or unhealthy), version, uptime_seconds, and a per-component breakdown.
curl -sS http://localhost:3000/v1/health \
-H "X-API-Key: $GUARDAI_API_KEY"GET /v1/capabilities
Lists the supported entity_types (with protection level and source), languages, detectors (type builtin, python, or custom), restore_modes, ner_active, and max_batch_size. Use it to discover what the deployment can do before you send work.
curl -sS http://localhost:3000/v1/capabilities \
-H "X-API-Key: $GUARDAI_API_KEY"GET /v1/diagnostics
Runtime diagnostics: detector mode, session backend, loaded policies, default policy, and feature toggles (rate limiting, prompt security, output guard). Requires the admin scope.
curl -sS http://localhost:3000/v1/diagnostics \
-H "X-API-Key: $GUARDAI_ADMIN_KEY"GET /metrics
Prometheus exposition format. Requires the admin scope.
curl -sS http://localhost:3000/metrics \
-H "X-API-Key: $GUARDAI_ADMIN_KEY"GET /livez and GET /readyz
Kubernetes liveness and readiness probes. Public: they sit in front of the auth layer and never require a key.
curl -sS http://localhost:3000/livez
curl -sS http://localhost:3000/readyzAdmin: policy management
POST /v1/admin/policy/validate
Validates the policy YAML files in a directory and previews the changes against the currently loaded set, without swapping them in. Requires the admin scope.
curl -sS http://localhost:3000/v1/admin/policy/validate \
-H "X-API-Key: $GUARDAI_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{ "directory": "policies/" }'POST /v1/admin/policy/reload
Hot-reloads the policy directory with an atomic, fail-closed swap: if the new set does not parse, the running policies stay in place. No request body. Requires the admin scope.
curl -sS -X POST http://localhost:3000/v1/admin/policy/reload \
-H "X-API-Key: $GUARDAI_ADMIN_KEY"Errors
Failures return a JSON error body with a stable code of the form GUARDAI_{CATEGORY}_{SPECIFIC} (for example GUARDAI_SESSION_EXPIRED, GUARDAI_POLICY_DENIED, GUARDAI_OCR_UNAVAILABLE), plus a safe human-readable message. Error messages never contain raw PII or internal detail. The shape is defined in schemas/api/error-response.json. See Error Codes in the reference for the full code list.