OGuardAI
API Reference

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:3000 by default (configurable via server.host / server.port). The Docker quick start (deploy/docker/docker-compose.yml) also publishes it on 3000; the HA compose stack (deploy/docker/docker-compose.ha.yml) fronts it with nginx on 8080 instead. 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 use multipart/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 a corpus_id derive 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-request prefix_NNN id with a 12-hex random cap. The curl examples below show the full form, because only that form round-trips.
  • Session state. transform returns a sealed session_state blob. Carry it into rehydrate (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-Key header 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/health

Each 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.json

For 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

MethodPathPurposeScope
POST/v1/transformReplace detected entities with semantic tokenstransform
POST/v1/rehydrateRestore tokens in LLM output to real valuesrehydrate
POST/v1/detectDetect entities without transformingdetect
POST/v1/evaluate-policyDry-run a policy against a list of entitiespolicy
POST/v1/transform/streamStreaming transform (SSE)transform
POST/v1/rehydrate/streamStreaming rehydrate (SSE)rehydrate
POST/v1/batch/transformTransform many inputs in one requestbatch
POST/v1/batch/detectDetect entities across many inputsbatch
POST/v1/rag/ingestChunk and tokenize a document for a vector storerag
POST/v1/rag/ingest/batchIngest multiple documents in one requestrag
POST/v1/rag/queryTokenize a user query for retrievalrag
POST/v1/rag/contextTokenize retrieved chunks for the promptrag
POST/v1/rag/answerRehydrate an LLM answer built from RAG contextrag
POST/v1/rag/deleteRevoke every value in a RAG session (erasure)revoke
POST/v1/revokeRevoke one entity value (future rehydrate returns [DELETED])revoke
POST/v1/revoke/bulkRevoke many entity values at oncerevoke
GET/v1/revocations/countScope-local count of revoked entitiesadmin
POST/v1/sessions/statusReport a session's lifecycle statesession
DELETE/v1/sessionsInvalidate a server-held sessionsession
POST/v1/transform/fileTransform an uploaded file (multipart)transform
POST/v1/rehydrate/fileRehydrate an uploaded file (multipart)rehydrate
POST/v1/transform/imageTokenize text detected in an image (multipart, OCR)transform
POST/v1/redact/imageReturn the image with PII regions blacked out (multipart, OCR)transform
POST/v1/entity-typesValidate a custom entity-type name (nothing persisted)transform
POST/v1/patternsValidate a custom regex detection pattern (nothing persisted)transform
GET/v1/healthHealth status and component breakdownany authed
GET/v1/capabilitiesSupported entity types, languages, detectors, restore modesany authed
GET/v1/diagnosticsRuntime diagnostics (detector mode, policies, config)admin
GET/metricsPrometheus metricsadmin
POST/v1/admin/policy/validateValidate policy YAML in a directoryadmin
POST/v1/admin/policy/reloadHot-reload the policy directory (atomic, fail-closed)admin
GET/livezLiveness probepublic
GET/readyzReadiness probepublic

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:

  • detectors accepts builtin_regex and python_ner. Omit it to use the server's configured default. python_ner requires the NER sidecar to be configured.
  • protection_overrides and restore modes are one of full, partial, masked, formatted, abstract, none.
  • context.destination is one of the built-ins external_llm, local_llm, vector_store, tool, agent_memory, or any custom [a-z0-9_-]+ label.
  • In non-dev auth, caller_role and caller_purpose are 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.png

Health 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/readyz

Admin: 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.