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_urlfield 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
| Method | Path | Description |
|---|---|---|
POST | /v1/transform | Transform text, replacing detected entities with semantic tokens |
POST | /v1/rehydrate | Restore semantic tokens in LLM output to original values |
POST | /v1/detect | Detect entities in text without transforming |
POST | /v1/evaluate-policy | Evaluate policy against entities (dry run) |
POST | /v1/entity-types | Validate a custom entity-type name (validate only, transform scope) |
POST | /v1/patterns | Validate a custom regex detection pattern (validate only, transform scope) |
GET | /v1/health | Runtime health check (requires auth when enabled; use /livez for unauthenticated probes) |
GET | /v1/capabilities | List entity types, languages, and detectors (requires auth when enabled) |
GET | /v1/diagnostics | Runtime diagnostics: detector mode, config, versions (Admin scope required) |
GET | /metrics | Prometheus metrics endpoint (Admin scope required) |
GET | /livez | Liveness probe (public, no auth) |
GET | /readyz | Readiness probe (public, no auth) |
Streaming Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/transform/stream | Streaming transform via Server-Sent Events (SSE) |
POST | /v1/rehydrate/stream | Streaming rehydrate via SSE |
File and Image Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/transform/file | Transform an uploaded file (multipart form data) |
POST | /v1/transform/image | Transform text detected in an image via OCR |
POST | /v1/rehydrate/file | Rehydrate an uploaded file (multipart form data) |
POST | /v1/redact/image | Redact PII regions in an image |
Batch Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/batch/transform | Batch transform multiple inputs in one request |
POST | /v1/batch/detect | Batch detect entities in multiple inputs |
RAG Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/rag/ingest | Ingest a document for RAG with PII protection |
POST | /v1/rag/ingest/batch | Ingest multiple documents in one request |
POST | /v1/rag/query | Query RAG index with PII-safe context |
POST | /v1/rag/context | Retrieve RAG context chunks (tokenized) |
POST | /v1/rag/answer | Generate a RAG answer with automatic rehydration |
POST | /v1/rag/delete | Delete ingested RAG documents |
Revocation Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/revoke | Revoke a single entity (makes it permanently non-restorable) |
POST | /v1/revoke/bulk | Revoke multiple entities in one request |
GET | /v1/revocations/count | Get the count of revoked entities |
Session Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/sessions/status | Report a session's lifecycle state (requires session scope) |
DELETE | /v1/sessions | Invalidate a server-held session (requires session scope) |
Admin Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/admin/policy/validate | Validate policy configuration files (requires admin scope) |
POST | /v1/admin/policy/reload | Hot-reload the policy directory, atomic and fail-closed (requires admin scope) |
Authentication
Authentication modes:
- API Key: send in the
X-API-Keyheader. - 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), orinput_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_sizeper request (alsoGUARDAI_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
/v1surface 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
entitiesarray 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).
- Error code prefix may change from
These potential breaking changes will be documented in detail before the 1.0.0 release, with migration guidance.
Related Resources
- API Reference: Complete endpoint documentation with request/response examples
- API Stability: Stability tiers and deprecation policy
- Migration Guide: Guidance for upgrading between versions
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.
TypeScript SDK
Typed client for the OGuardAI API with automatic session management, error handling, and full TypeScript type exports