API overview

One gateway, four data endpoints, JSON over HTTPS. Protect text or a document, restore an answer, or plug into LiteLLM as a guardrail.

Base URL and authentication

Your deployment exposes one public origin, the Salus route or ingress. Data calls carry an API key in the Authorization header:

Authorization: Bearer salus_sk_AbCdEfGh.…

Keys are issued per tenant with a role and a list of allowed routes, and are shown once at creation. The tenant is taken from the key; a caller cannot select another tenant in headers or body. See Keys and tenants.

Endpoints

Method Path Permission Purpose
POST /v1/detect/text detect_text Find and tokenise sensitive values in text
POST /v1/detect/file detect_file The same for a PDF, DOCX, XLSX, PPTX, image or text file
POST /v1/recover/text recover_text Put the original values back into an answer
POST /beta/litellm_basic_guardrail_api litellm_guardrail Generic Guardrail endpoint for a LiteLLM gateway
GET /health none Liveness
GET /ready none Readiness: checks PostgreSQL and Redis

Administration endpoints under /admin/v1/ manage tenants and keys and are described in Keys and tenants.

A first call

curl -fsS "$SALUS_BASE_URL/v1/detect/text" \
  -H "Authorization: Bearer $SALUS_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"text":"E-posta: [email protected]","context_id":"smoke-test"}'
{
  "document_id": "6f1c…",
  "context_id": "smoke-test",
  "prompt_id": null,
  "spans": [
    { "id": "…", "start": 9, "end": 31, "window_id": 0, "entity_type": "email",
      "source": "regex", "confidence": 1.0, "verified": null,
      "cluster_id": "…", "replacement": "EMAIL_dlwqzb" }
  ],
  "redacted_text": "E-posta: EMAIL_dlwqzb",
  "redacted_file": null,
  "stats": { "windows_processed": 1, "sources": ["regex"], "warnings": [], "cache_hit": false, "elapsed_ms": 42 }
}

Request headers

  • Content-Type must be application/json for text calls and multipart/form-data for file calls. Anything else returns 415.
  • X-Request-Id is echoed on every response. Send your own (up to 128 characters) to correlate with your logs, or let the gateway generate one.
  • Responses carry Cache-Control: no-store.

Errors

The gateway returns one envelope for every error:

{ "error": { "code": "UNPROCESSABLE_ENTITY", "message": "…", "request_id": "…" } }
Status Code When
400 INVALID_REQUEST Malformed body, unknown field, missing file part, mode not configured
401 UNAUTHORIZED Missing, malformed, unknown, expired or revoked key
403 ACCESS_DENIED Disabled key or tenant, route not allowed for the key, source IP not allowed
404 NOT_FOUND Unknown route, or an FPE document id that has expired
413 PAYLOAD_TOO_LARGE Body over the limit (16 MiB by default, 33 MiB with OCR)
415 UNSUPPORTED_MEDIA_TYPE Wrong content type, or a file format that is not supported
422 UNPROCESSABLE_ENTITY Document cannot be parsed, scanned input with OCR off, unreadable OCR, text over the model limit
429 RATE_LIMITED Tenant rate or concurrency limit; Retry-After is set
503 DEPENDENCY_UNAVAILABLE A detection layer, the verifier or Redis is unavailable. Nothing was sent.
502 UPSTREAM_UNAVAILABLE The gateway could not reach the engine

Request bodies reject unknown fields, so a typo in a field name is a 400, never a silently ignored option.

Limits and timing

  • Request body: 16 MiB by default. With the OCR service enabled the chart raises it to 33 MiB.
  • Each downstream detection call has a 10 second budget with one retry on transport errors; the gateway waits up to 30 seconds for the engine (longer when OCR is enabled).
  • Identical text submitted again in the same context returns from cache with stats.cache_hit: true. Degraded results are never cached.

All example values in this documentation are synthetic.