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
/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
| Field | Required | Content |
|---|---|---|
input | yes | Non-empty string or array of 1 to 64 non-empty strings, at most 256 KB (262,144 bytes) of UTF-8 in total. |
role | no | "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. |
policy | no | Optional 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).
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}"
]
}
}'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()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:
| Field | Content |
|---|---|
id | Unique inspection identifier of the form "shld_..." |
model | "noirdoc-shield-v1" |
action | Overall outcome across all segments: "pass", "redact" or "block" |
flagged | true whenever at least one finding matched or action is not "pass", otherwise false |
output | Sanitised text matching the shape of input (string or string[]) with [LABEL] placeholders applied |
findings | Array of matches with category (secrets, pii, ner, injection, custom), label, kind, action, replacement ("[<LABEL>]"), start, end and index |
findings_truncated | true 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.
{
"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
/v1/shield/policy /v1/shield/policy /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
| Field | Content |
|---|---|
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). |
rules | Object with keys secrets, pii, ner, custom ("off" | "redact" | "block") and injection ("off" | "block"). |
custom_patterns | Array 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/policyvs.PATCH /v1/shield/policy: whenrulesis supplied onPUT, any omitted rule key resets to its Shield default (secrets: "block",pii: "redact",ner: "redact",injection: "block",custom: "redact"). OnPATCH, supplied rule keys are merged over the current rules and omitted rule keys remain unchanged; whencustom_patternsis supplied, it replaces the stored list in full (omittingcustom_patternskeeps the stored list unchanged).- Validation: setting
enforcedwhile 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/policyorPATCH /v1/shield/policypermanently transitions a pre-existing legacy-masking organisation onto Noirdoc Shield (legacy_mask_active: false).
Response fields
| Field | Content |
|---|---|
mode | Active default setting: "default_off", "default_on" or "enforced" |
shield_policy | Mirrors mode ("default_off", "default_on" or "enforced") |
rules | Effective actions across all five filters (secrets, pii, ner, injection, custom) |
custom_patterns | Stored custom blocklist entries (string[]) |
price_per_1m_input_tokens_eur | "0.40" |
legacy_mask_active | true while a pre-existing organisation is still operating on legacy masking during the transition period, otherwise false |
legacy_mask_sunset_at | ISO-8601 timestamp "2026-11-30T23:59:59Z" while the gateway’s legacy transition window is active (including after transition), else null |
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}"
]
}'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()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();{
"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. |