Content detection
Run the CID222 detection pipeline over any text with POST /api/v1/guardrails/detect, and read the entities, actions and masked text it returns.
- Version: 0.4
- Role: admin_user, normal_user, viewer
- Type: reference
CID222 exposes its detection engine directly, so you can scan any text for PII, secrets, toxicity and prompt injection without sending it to a model. The same engine runs on every chat request.
Detect content
POST /api/v1/guardrails/detect
Runs the full detection pipeline — PII, secrets, toxicity, jailbreak and injection — over a block of text and returns the entities found, the decided action, and a masked copy of the text.
This is the only surface on the gateway that carries an /api/v1 prefix. Every other endpoint sits
at the root, for example /chat/completions and /models.
The endpoint returns 200, not 201. It accepts either a JWT or a gateway API key
(Authorization: Bearer cid_key_<64 hex>). A request that arrives unauthenticated from loopback or
an RFC1918 address is admitted with no tenant context, which is how the bundled red-team service
reaches it; per-tenant filter overrides do not apply to those calls.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Text to analyse, 1–50,000 characters |
check_type | string | No | prompt (default) or response |
hap_version | number | No | Toxicity model version, 1 or 2 (default 2) |
filter | string | No | Accepted by the DTO and then discarded — see the note below |
Warning
filter is validated and then dropped: the controller does not pass it to the detection service,
so naming a filter here changes nothing. Scope a filter's behaviour with a per-tenant filter
override instead.
Unknown fields are stripped silently rather than rejected, because the gateway runs a global
validation pipe with whitelist: true. A misspelled field name produces no error.
Example request
Response
The response reports the overall action, a count, the list of detected entities, and a masked copy of the input.
normalization.applied lists the evasion decoders that ran on the input — rot13, base64,
leetspeak, unicode, case — and is an empty array when none applied. normalization.originalText
is always an empty string: the controller blanks it so the raw input is never echoed back.
Warning
Raw values are never returned. detectedEntities[].value carries the entity's placeholder, or
[REDACTED] when the detector produced none. The original sensitive text does not appear
anywhere in the response.
Detected entity fields
| Field | Type | Description |
|---|---|---|
type | string | Detection category, for example EMAIL, PERSON_NAME, violence, jailbreak |
value | string | Placeholder such as [EMAIL], or [REDACTED] — never the raw value |
action | string | allow, flag, mask or reject |
confidence | number | 0–1. Regex detections are fixed at 0.95 |
source | string | Which detector produced it, see the table below |
filterName | string | The detector or filter-group identity, not the individual rule |
start, end | number | Character offsets in the input; absent for whole-text safety detections |
source takes one of these values:
source | Emitted by |
|---|---|
regex | Regex patterns loaded from the input-filter rows |
ml | The ONNX NER model |
hap_detector | The 13-label toxicity model |
jailbreak_detector | The ONNX jailbreak and injection model |
injection_scan | The deterministic SQLi and XSS scan |
code_rule_detector | Code Safety regex rules |
code_ml_detector | The Code Safety auditor, one entry per reported CWE |
mrz | The passport MRZ parser, on the image path |
identity_layout | The identity-document layout extractor, on the image path |
filterName reports the detector or the filter group, not the rule that fired. A regex detection
reports Regex Detector and carries the seed rule's name internally; a toxicity detection reports
the filter group, for example Content Safety (HAP Model - 13 Categories).
Detection actions
| Action | Meaning |
|---|---|
allow | No action. The content is clean or below threshold |
flag | The detection is logged; the content passes through unchanged |
mask | The entity is replaced with its placeholder and the request continues |
reject | The content is blocked |
Note
When several entities match, the strongest action wins: reject beats mask, mask beats flag, flag beats allow.
On the chat surface a rejection is not an HTTP error. The stream emits
{"type":"content_rejected"} for a blocked prompt or {"type":"output_content_rejected"} for a
blocked reply, then [DONE].
PII entity types
The seeded PII filter group covers 17 rules. The ONNX NER model emits 21 native labels; the regex layer adds Turkish formats and validators the model does not carry. Where both fire on the same span, the detections are merged.
| Type | Description | Detected by | Default action |
|---|---|---|---|
EMAIL | Email addresses | Regex, NER | mask |
PHONE | Phone numbers — US, international, Turkish mobile and landline | Regex (6 patterns), NER | mask |
PERSON_NAME | Person names | NER, plus one regex for uppercase names on Turkish ID cards | mask |
SSN | US Social Security number | Regex, us.ssn checksum, NER | mask |
TC_KIMLIK, ID_NUMBER | Turkish national ID | Regex (5 patterns including OCR-tolerant), tr.tckimlik checksum, NER | mask |
ID_DOCUMENT_NO | Turkish ID card document and serial number | Regex | mask |
CREDIT_CARD | Card numbers | Regex, Luhn plus brand and length check | mask |
IBAN, ACCOUNT_NUMBER | IBAN and Turkish domestic account numbers | Regex, ISO 13616 mod-97 | mask |
VKN, TAX_ID | Turkish tax number | Regex, tr.vkn checksum | mask |
PASSPORT | Passport numbers — US, generic EU, Turkish | Regex, NER | mask |
IP_ADDRESS | IPv4 and IPv6 | Regex, NER | mask |
LICENSE_PLATE | Turkish vehicle plates | Regex, province-range check, NER | mask |
CRYPTO_ADDRESS | Crypto wallet addresses | NER only | mask |
LOCATION, STREET_ADDRESS, CITY, ZIPCODE | Addresses, cities, regions, postal codes | NER, plus a Turkish postal-code regex | flag |
ORGANIZATION | Company and institution names | NER only | flag |
DATE_TIME | Dates and times | NER only | flag |
URL | URLs | NER only | flag |
Note
Default actions come from the shipped seed rows. An administrator changes them per rule, and a per-tenant or per-group override can change the action for one department without touching the rule.
Secret and credential detection
A separate DLP filter group covers secrets and credentials. Nine of its rules are regex-only.
| Type | Covers | Default action |
|---|---|---|
API_KEY | OpenAI, Anthropic, AWS access and secret keys, GitHub, Slack, Google, CID inspection keys, and a generic pattern — 10 patterns in total | mask |
password_assignment, db_password, secret_assignment | Inline passwords and secret assignments | mask |
rsa_private_key, private_key_generic, openssh_private_key | RSA and OpenSSH private keys | mask |
postgres_connection, mysql_connection, mongodb_connection, redis_connection | Database connection URIs | mask |
jwt_token, bearer_token, google_refresh_token, refresh_token_assignment | JWTs, bearer tokens and refresh tokens | mask |
webhook_secret, stripe_webhook | Webhook signing secrets | mask |
mac_colon, mac_hyphen | MAC addresses | mask |
mrn_labeled, mrn_format | Medical record numbers | mask |
cve_id | CVE identifiers | flag |
Warning
Only the API-key rule declares a model label, so only it reports type: "API_KEY". The other DLP
rules declare none, so their type is the regex pattern name shown above, while value
carries the placeholder — [PASSWORD], [RSA_PRIVATE_KEY], [MRN], and so on. Match on the
placeholder if you need a stable category.
Safety categories
Three detectors classify content beyond PII: a 13-label toxicity model, a 2-label attack model, and a deterministic injection scan.
| Label | Detector | Covers | Default action |
|---|---|---|---|
violence | Toxicity | Unlawful violence toward people or animals | reject |
crime | Toxicity | Non-violent crime — financial, property, drug | reject |
sexual_crime | Toxicity | Non-consensual sexual acts, trafficking | reject |
child_exploitation | Toxicity | Child sexual abuse material | reject |
defamation | Toxicity | Reputation-damaging falsehoods | reject |
dangerous_advice | Toxicity | Dangerous specialised advice | reject |
weapons | Toxicity | Weapons of mass destruction, illegal arms | reject |
hate | Toxicity | Hate speech targeting protected attributes | reject |
self_harm | Toxicity | Suicide, self-harm, eating disorders | reject |
cyber_crimes | Toxicity | Hacking, malware, denial of service | reject |
privacy | Toxicity | Tracking, doxxing, identity theft | flag |
intellectual_property | Toxicity | Piracy, plagiarism, counterfeiting | flag |
sexual_content | Toxicity | Adult sexual content | flag |
jailbreak | Attack model | Role-play attacks, system-prompt extraction | reject |
injection | Attack model | Prompt-injection attempts | reject |
sql_injection | Injection scan | SQL injection, detected deterministically | reject |
xss | Injection scan | Cross-site scripting, detected deterministically | reject |
The toxicity and attack models each cover eight languages: en, tr, de, fr, es, ar, it
and nl. The injection scan is language-independent — it parses the string rather than classifying
it — and a low attack-intent score from the attack model demotes its verdict from reject to flag.
Toxicity thresholds are per label, in three levels (block, flag, log-only), and ship tuned per label rather than at one global value. Attack-model thresholds are likewise per label and three levels. Both sets are baked into their service image and have no environment override, so changing them means rebuilding the image.
Confidence scores
Every detection carries a confidence between 0 and 1. Regex detections are fixed at 0.95, because
a compiled pattern either matched or did not. Model detections carry the model's own score.
A detection is boosted by 0.1, clamped at 1.0, when a context keyword registered for its entity
type appears within 50 characters of the span. The boost is context-keyword driven; it is not
awarded for two detectors agreeing.
A validated entity passes only if its final confidence reaches 0.6. A detection that arrives with
no confidence at all is assumed to be 0.8.
Note
Accuracy and latency depend on text length, language, detector and filter configuration. See performance and accuracy before quoting a number.
Query stored detections
GET /admin/detections and GET /admin/detections/stats read what the pipeline recorded. Both
require the admin_user or auditor role and the detections.view capability, and both are
scoped to the tenants the caller can already read.
| Parameter | Type | Description |
|---|---|---|
page, limit | number | Pagination. Defaults 1 and 10; limit is capped at 100 |
entity_type | string | Filter by detection category, for example EMAIL |
action_taken | string | masked, rejected or flagged |
message_role | string | user, assistant or system |
tenant_id | string | Filter by tenant |
group_id | string (UUID) | Narrow to one tenant group's members; a group the caller does not own answers 404 |
session_id | string | Filter by session |
start_date, end_date | string | ISO 8601 date range |
search | string | Free-text search |
The response is paginated:
Related
- The content safety pipeline — what runs, in what order, and what decides the action.
- PII detection — entity coverage, validators and masking.
- Chat API — how a detection surfaces on the stream.
Last updated on