OGuardAI
API Reference

API Changelog

Breaking changes, new endpoints, and deprecations by version for the OGuardAI API

Track breaking changes, new endpoints, and deprecations across OGuardAI API versions.


Versioning Policy

OGuardAI is pre-1.0 (0.1.0) and not yet released. The /v1/ surface may still change while we stabilize toward 1.0, so the policy below is the contract we are converging on for 1.0, not a guarantee that holds today. See API Stability for the full pre-1.0 stance.

The OGuardAI API follows semantic versioning. All endpoints are served under the /v1 prefix. From 1.0 onward, within a major version:

  • Patch releases (e.g., 1.0.1) contain bug fixes only. No API changes.
  • Minor releases (e.g., 1.1.0) may add new endpoints, new optional fields, or new enum values without removing or changing existing ones.
  • Major releases (e.g., 2.0.0) may introduce breaking changes to existing endpoints, request/response schemas, or authentication mechanisms.

New optional fields in responses are not considered breaking changes. Clients should ignore unknown fields.


Unreleased (0.1.0)

Idempotent retries with Idempotency-Key

The mutating, non-streaming endpoints (/v1/transform, /v1/batch/transform, /v1/rag/ingest, /v1/rag/ingest/batch, /v1/rag/delete, /v1/revoke, /v1/revoke/bulk, DELETE /v1/sessions) accept an optional Idempotency-Key request header. A retry with the same key, method, path, and body replays the stored response byte-identically (stamped Idempotency-Replayed: true) instead of executing the mutation twice; reusing a key for a different request is rejected with 409 (GUARDAI_IDEMPOTENCY_KEY_REUSE), and a concurrent duplicate returns 409 (GUARDAI_IDEMPOTENCY_IN_PROGRESS). Keys partition by tenant. The store is configurable under idempotency (memory default or redis, retention ttl_seconds). Requests without the header are unaffected. See Idempotent retries.

RAG batch ingest

New POST /v1/rag/ingest/batch (rag scope) ingests multiple documents in one request. Each item is a full /v1/rag/ingest body and is processed exactly like a single ingest with its own chunks and sealed session, so documents never share a token map. Results are per item with per-item errors; one document's failure never fails the batch. The batch is capped by limits.max_batch_size and the rate limiter charges per document.

Channel-aware policy preview

POST /v1/evaluate-policy accepts an optional output_channel (default user_output) and each decision now includes a restore_mode resolved against that channel and clamped by the safe-sink ceiling, so an operator can preview what an entity would restore to at a given audience without applying anything. New optional request/response fields are non-breaking. The MCP guardai_evaluate_policy tool forwards output_channel and surfaces restore_mode to match.

Policy hot reload

New POST /v1/admin/policy/reload (admin scope) re-reads the policy directory and atomically swaps the active engine without a restart. It runs the same fail-closed chain as startup (integrity, load, validate); on a failed verification or an empty set in non-dev it keeps the running policy unchanged and audits a policy_reloaded failure, so a bad reload never weakens protection. A new policy.watch config flag (off by default) auto-reloads the same way a few seconds after the directory changes. /v1/diagnostics now reports policy_watch_enabled and last_policy_reload_unix. New optional response fields are non-breaking.

Granular route scopes (breaking, pre-1.0)

The batch, RAG, and revoke endpoints now require their own auth scope instead of reusing transform, detect, or admin. Three scopes are added: batch (/v1/batch/*), rag (/v1/rag/ingest, /query, /context, /answer), and revoke (/v1/revoke, /v1/revoke/bulk, /v1/rag/delete). admin still implies every scope, so admin keys are unaffected, and /v1/revocations/count stays admin-only (a global count can leak cross-tenant metadata). This is a deliberate least-privilege change so a deployment can issue a key that does RAG but not transform, or revocation but not full admin. A key that previously relied on transform to reach batch or RAG, or on admin to revoke, must be granted the new scope.

Structured output guard: opt-in NER second pass

The structured (JSON / chat / tool-payload) output guard scanned with the built-in regex detector only, so a model-generated person/org/location name in a JSON response field escaped output scanning even when NER output scanning was enabled. It now runs the installed output NER detector additively over the regex floor on field VALUES (object keys stay regex-only), threaded with the policy detection plan. Off by default; enabled with GUARDAI_OUTPUT_SCAN_NER=1 (modes both/advanced), like the plain-text guard. A NER outage fails closed (the affected response is redacted rather than forwarded unscanned).

Server-side request-replay rejection

Transform continuations are now protected against replay. A store records the highest sealed request counter seen per session id and rejects a continuation whose counter does not strictly exceed it, so a captured session_state blob can no longer be replayed within its TTL to re-continue a superseded session. A replayed continuation returns 409 (GUARDAI_SESSION_REPLAY); a store outage fails closed with 503 (GUARDAI_REPLAY_STORE_UNAVAILABLE). The backend is configurable via replay_backend: memory (default, per-replica) or redis (shared across replicas, closing the cold-start/cross-replica window), with GUARDAI_REPLAY_BACKEND as an env override. Fresh (stateless) requests are never checked. Rehydrate (idempotent) and RAG context (multi-retrieval fan-out) are exempt by design and remain TTL-bounded; keep TTLs short and protect blobs in transit.

RAG corpus-scoped token identity

POST /v1/rag/ingest, /rag/query, and /rag/context accept an optional corpus_id. When set, the same normalized value tokenizes to the same token across every document and query in that corpus, so a cross-document RAG pipeline can deduplicate and correlate entities. Identity is keyed by a per-corpus secret derived server-side (HKDF(server_key, tenant, corpus_id)), never leaving the runtime, so the same value in a different corpus (or in the random transform path) yields an unrelated token: it is corpus-scoped, not a global fingerprint. Pass the same corpus_id to ingest, query, and context for ids to align. Absent corpus_id keeps the existing random per-token scheme; an empty or malformed value is rejected. Transform and single-session flows are unchanged.

RAG cross-session token composition

POST /v1/rag/ingest now returns a per-chunk chunk_session_state on each entry of chunks (a sealed blob holding only that chunk's tokens), alongside the existing aggregate session_state. POST /v1/rag/context accepts an optional document_sessions array, parallel to chunks: pass each retrieved chunk's chunk_session_state so a document-only entity resolves in the answer. When set, its length must equal chunks. Each admitted chunk's tokens are merged into the context fail closed: a token id that collides on a different type or value, a cap two documents disagree on, or conflicting ownership is rejected, and restoration is never widened. A merged document token is tightened to the active context policy, so a document sealed under a looser ingest policy cannot over-restore. The per-chunk blob is audience-scoped, so an aggregate or query session substituted into a document_sessions slot is rejected; its document classification is bound into the seal, so a restricted chunk swapped into a slot the caller labels permissively is still filtered on its server-authenticated classification; and a built-in cap bounds the per-request count. Both fields are additive and optional; a client that omits document_sessions keeps the prior behavior.

Caller-gated restore re-authorized at rehydrate

A policy override_restore_mode gated on caller_role or caller_purpose is now re-resolved at rehydrate against the authenticated rehydrate caller (API key or JWT) instead of the role baked in at transform. A caller who lacks the role degrades to the policy floor and a restrictive override still tightens, so possessing a session_state produced under a privileged role does not by itself grant elevated restoration. No request or response schema changes: the rehydrate caller is taken from the auth credential, never a request field, so existing clients are unaffected. Applies to /v1/rehydrate, /v1/rehydrate/file, /v1/rehydrate/stream, and /v1/rag/answer.

Field-level JSON transform

POST /v1/transform with input_type: "json" processes input_json field by field through the protection kernel instead of flattening it to text. The response includes a safe_json object with the same shape as the input, every string value and object key tokenized in place; safe_text is the serialized safe_json. The same value across fields shares one token id, and one sealed session_state rehydrates the whole document. See JSON input mode. A numeric leaf that is PII (e.g. a credit-card number sent as a JSON number) tokenizes to a string token (its type changes from number to string); a number with no PII is returned unchanged. Structure is always preserved and any drift fails closed.

Field-level chat transform

POST /v1/transform with input_type: "chat_messages" processes input_messages field by field through the same kernel. The response includes a safe_messages array with the same shape as the input, every string-bearing field (content, multimodal text parts, tool-call arguments) tokenized in place; safe_text is the serialized safe_messages. The same value across messages shares one token id and one sealed session_state covers the conversation. Tool-call arguments JSON is protected structurally; only the known OpenAI message fields are accepted and any unknown text-bearing field is rejected. See Chat input mode. The TS and Python SDK guardedChat / guarded_chat helpers now make one call instead of looping per message, and input_messages accepts a free-form message object (so tool calls and array content round-trip).

Config-driven partial-restore strategies

Policies can declare per-entity-type partial-restore strategies under restore_strategies, and config can declare deployment-wide defaults under restore_templates.partial_formats. Each strategy has a kind: reveal_last_n (keep the last n characters, n between 1 and 64, rest replaced by an optional mask_char), reveal_first_n (keep the first n characters, for a fixed prefix such as an IBAN country code), reveal_range (keep a len-character window at 0-based start, for an interior span such as a phone area code), mask (replace the whole value), or bucket (generalize a numeric value into a labelled range, for example salary bands, via ranges). Strategies apply only when the channel-resolved restore mode is Partial; they are channel-ceiling gated. They fail closed, masking when they cannot produce a bounded result, and invalid config aborts startup. See Policy Authoring and Configuration.

Locale-regex detector overlay (detector.vocab.extra_patterns)

detector.vocab.extra_patterns in oguardai.yaml adds extra regex detectors keyed by entity-type label, for any entity type and any locale. An operator can, for example, add an address pattern for a locale outside the built-in DE and US shapes. The overlay is additive: spans merge so a built-in detector is never suppressed, and the base detection floor stays. A built-in label produces the built-in entity type; a custom:<name> label produces a Custom token. Invalid patterns abort startup.

Bidi isolation of restored values

When a restored value contains right-to-left text or explicit bidi-control characters, the rehydrate engine wraps it in a Unicode FSI..PDI isolate (U+2068..U+2069) and strips inner bidi-control characters, so a restored value cannot reorder or spoof the surrounding text (a Trojan-Source defence). Left-to-right values are returned unchanged.

Family-first name handling in partial restore

Partial restore now masks family-first personal names (zh, ja, ko, vi, hu) instead of revealing a possibly misidentified "F. Surname" form. Given-first scripts such as Arabic and Hebrew keep their existing given-first reveal.

30-language abstract-label coverage

Company, address, and location abstract labels now cover the full 30-language built-in floor. Any additional language can be supplied through restore_templates; unknown languages fall back to a safe generic label.

Pre-release cleanups

  • Removed the unused JwtConfig.jwks_url field from the JWT auth config.
  • Tightened the sealed-envelope fields to only what the session blob requires.

API surface

Status: Pre-release (0.1.0, not yet released)

The endpoints, request/response schemas, and error codes documented below are the 0.1.0 API surface. OGuardAI is not yet released; this surface may still change before 1.0.

Core Endpoints

MethodPathDescription
POST/v1/transformTransform text, replacing detected entities with semantic tokens
POST/v1/rehydrateRestore semantic tokens in LLM output to original values
POST/v1/detectDetect entities in text without transforming
POST/v1/evaluate-policyEvaluate policy against entities (dry run)
POST/v1/entity-typesValidate a custom entity-type name (validate only, transform scope)
POST/v1/patternsValidate a custom regex detection pattern (validate only, transform scope)
GET/v1/healthRuntime health check (requires auth when enabled; use /livez for unauthenticated probes)
GET/v1/capabilitiesList entity types, languages, and detectors (requires auth when enabled)
GET/v1/diagnosticsRuntime diagnostics: detector mode, config, versions (Admin scope required)
GET/metricsPrometheus metrics endpoint (Admin scope required)
GET/livezLiveness probe (public, no auth)
GET/readyzReadiness probe (public, no auth)

Streaming Endpoints

MethodPathDescription
POST/v1/transform/streamStreaming transform via Server-Sent Events (SSE)
POST/v1/rehydrate/streamStreaming rehydrate via SSE

File and Image Endpoints

MethodPathDescription
POST/v1/transform/fileTransform an uploaded file (multipart form data)
POST/v1/transform/imageTransform text detected in an image via OCR
POST/v1/rehydrate/fileRehydrate an uploaded file (multipart form data)
POST/v1/redact/imageRedact PII regions in an image

Batch Endpoints

MethodPathDescription
POST/v1/batch/transformBatch transform multiple inputs in one request
POST/v1/batch/detectBatch detect entities in multiple inputs

RAG Endpoints

MethodPathDescription
POST/v1/rag/ingestIngest a document for RAG with PII protection
POST/v1/rag/ingest/batchIngest multiple documents in one request
POST/v1/rag/queryQuery RAG index with PII-safe context
POST/v1/rag/contextRetrieve RAG context chunks (tokenized)
POST/v1/rag/answerGenerate a RAG answer with automatic rehydration
POST/v1/rag/deleteDelete ingested RAG documents

Revocation Endpoints

MethodPathDescription
POST/v1/revokeRevoke a single entity (makes it permanently non-restorable)
POST/v1/revoke/bulkRevoke multiple entities in one request
GET/v1/revocations/countGet the count of revoked entities

Session Endpoints

MethodPathDescription
POST/v1/sessions/statusReport a session's lifecycle state (requires session scope)
DELETE/v1/sessionsInvalidate a server-held session (requires session scope)

Admin Endpoints

MethodPathDescription
POST/v1/admin/policy/validateValidate policy configuration files (requires admin scope)
POST/v1/admin/policy/reloadHot-reload the policy directory, atomic and fail-closed (requires admin scope)

Authentication

Authentication modes:

  • API Key: send in the X-API-Key header.
  • JWT Bearer Token: HS256, HS384, or HS512, sent in the Authorization: Bearer <token> header.
  • OIDC: asymmetric JWKS verification (RS256, ES256, PS256) with an algorithm allowlist and validation of issuer, audience, expiry, not-before, and a required tenant claim; it fails closed on a JWKS outage.
  • Dev: authentication disabled (the development default), so all endpoints are accessible without credentials.

Authorization is scope based. The runtime enforces a per-route scope for each surface: transform, rehydrate, detect, policy, batch, rag, revoke, session, and admin (which implies every scope). revoke erases within the caller's tenant; an untenanted caller also needs global_revoke (which admin implies) to write a cross-tenant revocation. The detect_values scope additionally gates receiving raw detected values from /v1/detect and /v1/batch/detect. A global count endpoint such as /v1/revocations/count stays admin only. See the API Reference scopes table.

Request/Response Format

  • All request and response bodies use JSON (Content-Type: application/json), except file upload endpoints which use multipart form data.
  • Transform accepts three input modes: input (plain text), input_json (structured JSON), or input_messages (OpenAI-format chat messages). Exactly one is required.
  • Session state is returned as an encrypted blob in transform responses and must be passed back to rehydrate requests.
  • Six restore modes are supported: full, partial, masked, formatted, abstract, none.

Error Codes

See the full error codes table in the API Reference. All codes use the GUARDAI_* prefix.

Known Limitations

  • Streaming error recovery: if a detection error occurs mid-stream, the server emits a single terminal SSE frame { "error": "...", "complete": true } and then ends the stream. Partial results are not recoverable, and the SDKs surface that terminal error rather than ending as an empty stream.
  • Batch size: batch endpoints accept up to the configured limits.max_batch_size per request (also GUARDAI_MAX_BATCH_SIZE); the built-in maximum is not currently raisable above its default ceiling.

Upcoming Changes

Everything shipped so far is part of the 0.1.0 release. The following reflect current development direction, not commitments, and may change.

Under consideration

  • Raisable batch ceiling: allow the maximum batch size to be configured above the current default ceiling.
  • Webhook notifications: optional webhook delivery for async batch completion events.

Toward a stable 1.0

  • API stability target (post-1.0): from 1.0 onward, the /v1 surface is intended to follow the stability contract within a major version, with breaking changes landing only in a new major version. These are 1.0 goals, not guarantees that hold while the product is pre-1.0 (0.x).
  • Potential breaking changes before 1.0:
    • Error code prefix may change from GUARDAI_ to a shorter form.
    • The entities array in transform responses may move to a separate diagnostic endpoint to reduce default response size.
    • Session state format may change (existing sealed blobs will not be compatible with 1.0).

These potential breaking changes will be documented in detail before the 1.0.0 release, with migration guidance.