Develop / Reference

Shield (/v1/shield)

Inspects text standalone via POST /v1/shield/inspect and reads or updates the organisation's Shield policy via /v1/shield/policy.

Authentication
Bearer px-... · x-api-key · api-key
Price (/v1/shield/inspect)
€0.40 / 1M input tokens
Policy management
requires can_manage_shield
Format
JSON
Streaming
no

Under /v1/shield, the gateway exposes two endpoint groups: POST /v1/shield/inspect inspects arbitrary text directly on German infrastructure without calling a model, and /v1/shield/policy reads or updates your organisation’s Shield policy via the API. For how Shield operates on model endpoints, see Noirdoc Shield.

Inspect text

POST /v1/shield/inspect

Inspects a single string or a batch of up to 64 strings against your organisation’s protection filters (optionally augmented or tightened via policy). The endpoint evaluates the optional X-Noirdoc-Tags header for tag attribution and tag budgets, checks key and organisation budgets, and bills €0.40 per 1M input tokens.

Request fields

FieldRequiredContent
inputyesNon-empty string or array of 1 to 64 non-empty strings, at most 256 KB (262,144 bytes) of UTF-8 in total.
roleno"user", "tool", "system" or "assistant" (default: "user"). injection is evaluated on "user" and "tool"; "system" and "assistant" are exempt from injection while all other active filters still apply.
policynoOptional override object {"rules"?: {...}, "custom_patterns"?: [...]} (or a flat rule map when custom_patterns is omitted; mixing both returns 422 shield_policy_invalid). Applies to this request only.

When the organisation policy is enforced, policy cannot weaken any active rule (such as lowering block to redact or off) or omit configured custom_patterns; otherwise the gateway answers with status code 403 (shield_policy_enforced).

Shell
curl https://api.noirdoc.de/v1/shield/inspect \
  -H "Authorization: Bearer $NOIRDOC_API_KEY" \
  -H "X-Noirdoc-Tags: mandat-2026-042" \
  -H "Content-Type: application/json" \
  -d '{
    "input": [
      "Client on record for matter: Anna Schmidt",
      "Bank account: DE89 3704 0044 0532 0130 00",
      "Note on Projekt Helios, file AZ-2026-00418."
    ],
    "role": "user",
    "policy": {
      "rules": { "custom": "redact" },
      "custom_patterns": [
        "Projekt Helios",
        "re:AZ-[0-9]{4}-[0-9]{5}"
      ]
    }
  }'
Python
import os
import httpx

key = os.environ["NOIRDOC_API_KEY"]
patterns = ["Projekt Helios", "re:AZ-[0-9]{4}-[0-9]{5}"]

response = httpx.post(
    "https://api.noirdoc.de/v1/shield/inspect",
    headers={
        "Authorization": f"Bearer {key}",
        "X-Noirdoc-Tags": "mandat-2026-042",
    },
    json={
        "input": [
            "Client on record for matter: Anna Schmidt",
            "Bank account: DE89 3704 0044 0532 0130 00",
            "Note on Projekt Helios, file AZ-2026-00418.",
        ],
        "role": "user",
        "policy": {
            "rules": {"custom": "redact"},
            "custom_patterns": patterns,
        },
    },
)
response.raise_for_status()
result = response.json()
TypeScript
const url = "https://api.noirdoc.de/v1/shield/inspect";
const patterns = ["Projekt Helios", "re:AZ-[0-9]{4}-[0-9]{5}"];

const response = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.NOIRDOC_API_KEY}`,
    "X-Noirdoc-Tags": "mandat-2026-042",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    input: [
      "Client on record for matter: Anna Schmidt",
      "Bank account: DE89 3704 0044 0532 0130 00",
      "Note on Projekt Helios, file AZ-2026-00418.",
    ],
    role: "user",
    policy: {
      rules: { custom: "redact" },
      custom_patterns: patterns,
    },
  }),
});
const result = await response.json();

Response object (200 OK)

POST /v1/shield/inspect returns HTTP 200 OK even when action is "block", and executes with short_circuit=false so that every finding across the input is returned rather than stopping at the first blocking match:

FieldContent
idUnique inspection identifier of the form "shld_..."
model"noirdoc-shield-v1"
actionOverall outcome across all segments: "pass", "redact" or "block"
flaggedtrue whenever at least one finding matched or action is not "pass", otherwise false
outputSanitised text matching the shape of input (string or string[]) with [LABEL] placeholders applied
findingsArray of matches with category (secrets, pii, ner, injection, custom), label, kind, action, replacement ("[<LABEL>]"), start, end and index
findings_truncatedtrue if findings reached the service cap; action, flagged and output remain complete
usage{"input_tokens": <int>} – billed input tokens (minimum 1)

When input is an array, findings[].index gives the 0-based index of the matching string.

JSON
{
  "id": "shld_7f3c9a12e4b048d19c2a6e10",
  "model": "noirdoc-shield-v1",
  "action": "redact",
  "flagged": true,
  "output": [
    "Client on record for matter: [PERSON]",
    "Bank account: [IBAN]",
    "Note on [CUSTOM], file [CUSTOM]."
  ],
  "findings": [
    {
      "category": "ner",
      "label": "PERSON",
      "kind": "PERSON",
      "action": "redact",
      "replacement": "[PERSON]",
      "start": 29,
      "end": 41,
      "index": 0
    },
    {
      "category": "pii",
      "label": "IBAN",
      "kind": "IBAN",
      "action": "redact",
      "replacement": "[IBAN]",
      "start": 14,
      "end": 41,
      "index": 1
    },
    {
      "category": "custom",
      "label": "CUSTOM",
      "kind": "CUSTOM",
      "action": "redact",
      "replacement": "[CUSTOM]",
      "start": 8,
      "end": 22,
      "index": 2
    },
    {
      "category": "custom",
      "label": "CUSTOM",
      "kind": "CUSTOM",
      "action": "redact",
      "replacement": "[CUSTOM]",
      "start": 29,
      "end": 42,
      "index": 2
    }
  ],
  "findings_truncated": false,
  "usage": {
    "input_tokens": 41
  }
}

Manage policy

GET /v1/shield/policy
PUT /v1/shield/policy
PATCH /v1/shield/policy

Reads or updates the organisation’s Shield policy. All three methods require Manage Shield policy via API (can_manage_shield = true) enabled on the calling API key under Models → API keys; otherwise the gateway answers with status code 403 (insufficient_key_permissions). In addition, a key pinned to shield_mode: "on" cannot weaken the organisation’s current policy (403 insufficient_key_permissions).

Request fields on PUT and PATCH

FieldContent
mode"default_off", "default_on" or "enforced" (aliases "off" for "default_off" and "advisory" for "default_on", as well as field names shield_policy and mask_policy, are also accepted).
rulesObject with keys secrets, pii, ner, custom ("off" | "redact" | "block") and injection ("off" | "block").
custom_patternsArray of 0 to 100 strings of 2 to 256 characters each: plain strings for case-insensitive literal substrings, or re:-prefixed linear-time regular expressions (no look-around/backreferences, non-empty).
  • PUT /v1/shield/policy vs. PATCH /v1/shield/policy: when rules is supplied on PUT, any omitted rule key resets to its Shield default (secrets: "block", pii: "redact", ner: "redact", injection: "block", custom: "redact"). On PATCH, supplied rule keys are merged over the current rules and omitted rule keys remain unchanged; when custom_patterns is supplied, it replaces the stored list in full (omitting custom_patterns keeps the stored list unchanged).
  • Validation: setting enforced while all five effective rules are "off" is rejected with status code 422 (shield_policy_invalid).
  • One-way transition from legacy masking: calling PUT /v1/shield/policy or PATCH /v1/shield/policy permanently transitions a pre-existing legacy-masking organisation onto Noirdoc Shield (legacy_mask_active: false).

Response fields

FieldContent
modeActive default setting: "default_off", "default_on" or "enforced"
shield_policyMirrors mode ("default_off", "default_on" or "enforced")
rulesEffective actions across all five filters (secrets, pii, ner, injection, custom)
custom_patternsStored custom blocklist entries (string[])
price_per_1m_input_tokens_eur"0.40"
legacy_mask_activetrue while a pre-existing organisation is still operating on legacy masking during the transition period, otherwise false
legacy_mask_sunset_atISO-8601 timestamp "2026-11-30T23:59:59Z" while the gateway’s legacy transition window is active (including after transition), else null
Shell
curl -X PATCH https://api.noirdoc.de/v1/shield/policy \
  -H "Authorization: Bearer $NOIRDOC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "default_on",
    "rules": {
      "pii": "redact",
      "custom": "block"
    },
    "custom_patterns": [
      "Projekt Helios",
      "Mandat Nordstern",
      "Vertrag Kronos",
      "re:AZ-[0-9]{4}-[0-9]{5}"
    ]
  }'
Python
import os
import httpx

key = os.environ["NOIRDOC_API_KEY"]
patterns = [
    "Projekt Helios",
    "Mandat Nordstern",
    "Vertrag Kronos",
    "re:AZ-[0-9]{4}-[0-9]{5}",
]

response = httpx.patch(
    "https://api.noirdoc.de/v1/shield/policy",
    headers={"Authorization": f"Bearer {key}"},
    json={
        "mode": "default_on",
        "rules": {"pii": "redact", "custom": "block"},
        "custom_patterns": patterns,
    },
)
response.raise_for_status()
policy = response.json()
TypeScript
const url = "https://api.noirdoc.de/v1/shield/policy";
const patterns = [
  "Projekt Helios",
  "Mandat Nordstern",
  "Vertrag Kronos",
  "re:AZ-[0-9]{4}-[0-9]{5}",
];

const response = await fetch(url, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.NOIRDOC_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    mode: "default_on",
    rules: { pii: "redact", custom: "block" },
    custom_patterns: patterns,
  }),
});
const policy = await response.json();
JSON
{
  "mode": "default_on",
  "shield_policy": "default_on",
  "rules": {
    "secrets": "block",
    "pii": "redact",
    "ner": "redact",
    "injection": "block",
    "custom": "block"
  },
  "custom_patterns": [
    "Projekt Helios",
    "Mandat Nordstern",
    "Vertrag Kronos",
    "re:AZ-[0-9]{4}-[0-9]{5}"
  ],
  "price_per_1m_input_tokens_eur": "0.40",
  "legacy_mask_active": false,
  "legacy_mask_sunset_at": "2026-11-30T23:59:59Z"
}

Errors on this endpoint

Status Code Meaning
400 invalid_request_error A parameter on /v1/shield/inspect or /v1/shield/policy is invalid (such as an empty input or one over 256 KB, more than 64 entries, an unknown rule or an invalid custom pattern), or the JSON body is nested too deeply for Shield extraction.
400 invalid_request_tags The X-Noirdoc-Tags header or the tag name in the path under /v1/tags is empty, contains more than 5 tags or does not match the format ^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,63}$.
401 invalid_api_key The key is missing, invalid or deactivated, or the organisation is not active.
402 insufficient_credit The organisation's credit is used up or lower than the estimated maximum cost of the request (including Noirdoc Shield, also on BYOK routes when Shield is active).
402 key_budget_exhausted This key's budget is used up for the current period or does not cover the estimated cost of the request.
402 tag_budget_exhausted The budget of a tag named in X-Noirdoc-Tags is used up for the current period or does not cover the estimated cost of the request; message names the tag.
403 shield_policy_enforced The organisation's Shield policy is enforced, and the request to /v1/shield/inspect tries to weaken an active rule or omit custom patterns via policy.
403 tag_not_found The named tag does not exist. On requests with X-Noirdoc-Tags, automatic tag creation is turned off (status code 403); on /v1/tags/{name}, the gateway answers with status code 404.
403 tag_disabled A tag named in X-Noirdoc-Tags is deactivated.
403 tag_limit_exceeded The organisation has reached the maximum of 1,000 request tags.
403 insufficient_key_permissions The API key lacks permission for /v1/tags (can_manage_tags) or /v1/shield/policy (can_manage_shield), a key pinned to shield_mode: on tries to weaken the Shield policy or a tag's shield_mode, or the tag requests broader models, providers or compliance rules than the calling key itself holds.
413 shield_input_too_large The text to inspect in this request exceeds the Shield service limit of 4 MiB of encoded text in a single call.
422 shield_policy_invalid The submitted Shield policy is inconsistent, such as enforced with all five protection filters set to off, or mixing rules/custom_patterns with flat rule keys.
422 shield_config_invalid The organisation's stored Shield configuration is invalid (for example enforced without any active protection filter, or a pattern that fails compilation); the gateway did not forward the request.
429 starter_rate_limit_exceeded The organisation is still using only its starter credit and has reached the limit of 5 requests per minute or 25 requests per day (UTC) for managed providers. The response contains Retry-After with the wait in seconds.
503 service_overloaded The gateway is temporarily at capacity. The response contains Retry-After with 5 seconds.
503 shield_unavailable The Noirdoc Shield inspection service is temporarily unreachable; the gateway never forwards a request that requires inspection uninspected (fail-closed). The response contains Retry-After with 5 seconds.