---
title: "Detect and mask PII in a document"
method: POST
path: "/v1/ai/guardrails/pii-detection"
tags: ["guardrails:pii-detection"]
---

# Detect and mask PII in a document

`POST /v1/ai/guardrails/pii-detection`

## Request

- Base URL: `https://api.intura.co` (the document declares no server; this is the origin it was published from)
- URL: `https://api.intura.co/v1/ai/guardrails/pii-detection`
- Auth: none declared

## Request body

- MaskRequest: Text to de-identify.
  - `text` string, required: The document to scan. Bounded by the module's MAX_CHARS; anything past the limit is neither scanned nor returned, so a `masked_text` shorter than what was submitted means the tail was dropped.
  - `thresholds` object, nullable: Move the cut for this one call: `min_score` (default 0.5), the lowest score a model span may have and still be masked. Pattern hits are never filtered by it. The console's playground is where to find the value that suits your documents; this field is where to put it.
  - `mask` boolean: Return the masked document. Set false to detect only — the span list still comes back, `result.masked_text` does not. Use it when you need to know whether PII is present without moving the document.

## Response `200`

Successful Response

- MaskResponse
  - `status` string: Always `success` on a 2xx
  - `code` integer: Mirrors the HTTP status code
  - `data` MaskResponseData, required: `data` for POST /v1/ai/guardrails/pii-detection.
    - `label` string, required: `pii_found` — at least one span was detected. `clean` — none was.
    - `score` number, required: The number this endpoint measured and decided on, 0..1, rounded to 4 dp — P(injection), P(scam), how strongly the frame reads as the card. Compare it against `thresholds` and you have `label`. This is the only number to threshold on; what it means on a given route is documented there.
    - `thresholds` object: The cuts this answer was decided by, after your saved settings and anything sent in `thresholds` on the request. Published on every response so a verdict can be compared with an older one: a score means nothing without the bar it was held to. Empty on an endpoint with nothing to tune.
    - `result` MaskResultData: `result` for a masking call.
      - `masked_text` string, nullable: The document with every detected span replaced by its typed placeholder. Everything else is byte-identical to what was submitted. Null when the request passed `mask: false`.
      - `entities` EntitySpan[]: Every span found, in document order. Types, offsets and scores only — the raw values are deliberately absent, since returning them would undo the point of the endpoint.
        - `type` string, required: Entity type, e.g. PERSON, NATIONAL_ID, PHONE.
        - `placeholder` string, required: What replaced it in `masked_text`, e.g. [NAME].
        - `start` integer, required: Start offset in the *original* submitted text.
        - `end` integer, required: End offset (exclusive) in the original submitted text.
        - `score` number, required: How sure the guard is about this span. 1.0 for a format-verified value (Luhn-checked card, structurally valid NIK). A model span appears here only if it reached `thresholds.min_score`.
      - `n_entities` integer, required: How many spans were found.
      - `counts` object: Spans per entity type.
      - `risk` number: How much of a problem this document is, 0..1 — which is a different question from `score`. It rises with the number of distinct entity types and with their sensitivity: one national ID outranks one phone number, and an ID beside a name outranks either alone. 0 when nothing was found.
      - `languages` string[]: The languages this text was scanned in. Every supported language when none could be identified.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/intura/apis/intura-ai-api.md) - [All operations](https://skmtc.dev/intura/apis/intura-ai-api/llms.txt) - [OpenAPI document](https://skmtc.dev/intura/apis/intura-ai-api/revisions/7cb6af4c6390?raw)
