API endpoints reference
Package: piighost-api
piighost-api serve builds one thread pipeline from its configuration. Every route below goes through that pipeline. Request and response bodies are JSON. The OpenAPI schema is served at /schema/openapi.json, with a Swagger UI at /schema/swagger. PIIGhostClient calls the pipeline routes, see Remote client.
Authentication
The server loads its keys at start from every environment variable whose name starts with API_KEY_. A protected route then requires the header Authorization: Bearer <key>.
| Routes | Key required |
|---|---|
GET /, GET /health, GET /v1/labels | never |
/v1/detect, /v1/anonymize, /v1/anonymize/corrected, /v1/deanonymize, /v1/threads/..., /schema/... | when keys are loaded |
/openai/v1/..., /anthropic/v1/... | never, the caller's credentials are relayed to the upstream |
A missing or malformed header answers 401 with Missing or malformed Authorization header. An unknown key answers 401 with Invalid API key. When no key loads, the server refuses to start, unless PIIGHOST_ALLOW_ANONYMOUS is set. In that case, no route asks for a key. The variables are listed in Server CLI.
Status codes
| Status | When |
|---|---|
200 | a GET or DELETE route succeeds |
201 | a POST pipeline route succeeds |
400 | the body fails validation, a proxy body is not a JSON object, or a proxy request names no upstream and none is configured |
401 | a protected route gets no valid key |
404 | the route does not exist, under the proxy prefixes too |
413 | the body exceeds PIIGHOST_MAX_BODY_BYTES |
429 | the client exceeds PIIGHOST_RATE_LIMIT, with RateLimit-* headers |
500 | the pipeline raises, a guard flagging a value left in clear or an unknown role among others |
502 | a proxy route cannot reach its upstream |
A proxy route otherwise answers with the upstream's status.
Shared objects
Entity
| Field | Type | Description |
|---|---|---|
label | string | The entity label, such as PERSON |
placeholder | string | The token that replaces the entity, empty on /v1/detect |
detections | list of Detection | Every occurrence of the entity in the text |
Detection
| Field | Type | Description |
|---|---|---|
text | string | The value as it appears in the text |
label | string | The detection label |
start_pos | integer | Start offset in the text |
end_pos | integer | End offset in the text, exclusive |
confidence | float | Detector score, 1.0 for a regex or an exact match |
Service routes
GET /
{"name": "piighost-api", "version": "...", "docs": "/schema/swagger"}version is the installed piighost-api version.
GET /health
{"status": "ok", "detector": "composite"}detector is the type of the configuration's [detector] section. GET / and GET /health are exempt from PIIGHOST_RATE_LIMIT.
GET /v1/labels
{"name": "piighost/fr-default:e6990159", "detector": "regex", "labels": ["CREDIT_CARD", "EMAIL", "EU_VAT", "FR_IBAN", "FR_NIR", "FR_PHONE", "FR_SIREN", "FR_SIRET", "IBAN", "IPV4", "SWIFT_BIC", "URL"]}| Field | Type | Description |
|---|---|---|
name | string or null | The configuration's name |
detector | string | The type of [detector] |
labels | list of strings | Every label [detector] can emit, sorted |
The labels are collected from the [detector] section alone, the guard's detector excluded.
Detector type | Labels |
|---|---|
regex | the keys of patterns, plus those of every catalog group in catalogs, read from the catalog through its disk cache |
gliner2, spacy, transformers, llm | labels, or its keys when it maps model labels to canonical ones |
exact | the labels of values |
composite | the union of its detectors |
chunked | those of its detector |
| any other | none |
Pipeline routes
POST /v1/detect
Runs the detector and the linker on a text and returns the entities, without placeholders. The thread memory is neither read nor written, and the override, overlap, expansion and guard stages do not run.
| Request field | Type | Default |
|---|---|---|
text | string | required |
thread_id | string | "default", accepted and unused |
For the text Write to jane.doe@example.com, the response reads:
{"entities": [{"label": "EMAIL", "placeholder": "", "detections": [{"text": "jane.doe@example.com", "label": "EMAIL", "start_pos": 9, "end_pos": 29, "confidence": 1.0}]}]}POST /v1/anonymize
De-identifies a message in a thread, with tokens consistent across the thread.
| Request field | Type | Default |
|---|---|---|
text | string | required |
thread_id | string | required |
role | "user" or "assistant" | "user" |
| Response field | Type | Description |
|---|---|---|
anonymized_text | string | The text with each value replaced by its placeholder |
entities | list of Entity | The entities of this message that received a token |
role tells who wrote the message, and therefore who introduced the values it brings in. A value first written by the assistant gets no token and stays in clear.
POST /v1/anonymize/corrected
De-identifies a message again from a corrected detection set, for a human review step. The corrected set goes through the configured override, then replaces the message's detections in the thread memory. Detection does not run again.
| Request field | Type | Default |
|---|---|---|
text | string | required |
detections | list of objects with text, label, start, end, confidence | required |
thread_id | string | required |
A corrected detection names its offsets start and end, not start_pos and end_pos. The response is {"anonymized_text": "..."}.
POST /v1/deanonymize
Restores every placeholder the thread issued, in any text, a model reply included. A token the thread never issued stays as it stands.
| Request field | Type | Default |
|---|---|---|
text | string | required |
thread_id | string | required |
The response is {"text": "..."}.
GET /v1/threads/{thread_id}/tokens
Returns the thread's placeholder-to-value map, for a client that restores a stream itself.
{"tokens": {"<<PERSON:1>>": "Jane Doe", "<<EMAIL:1>>": "jane.doe@example.com"}}DELETE /v1/threads/{thread_id}
Erases the thread from the memory and reports what was dropped. A thread that does not exist reports zero.
{"messages": 1, "detections": 2}OpenAI-compatible proxy
Prefix: /openai/v1
| Request header | Effect |
|---|---|
X-PIIGhost-Upstream | Base URL of the upstream, PIIGHOST_OPENAI_UPSTREAM when absent |
X-PIIGhost-Thread-Id | Fixed thread kept after the request. When absent, each request gets a fresh thread, forgotten once the reply is restored |
Only Authorization, Content-Type, x-api-key, anthropic-version and anthropic-beta are relayed to the upstream.
| Route | De-identified in the request | Restored in the reply |
|---|---|---|
POST /chat/completions | messages[].content, a string or the text of each part, and every string inside messages[].tool_calls[].function.arguments | choices[].message.content and choices[].message.tool_calls[].function.arguments, or choices[].delta.content when streamed |
POST /completions | prompt, suffix | choices[].text |
POST /embeddings | input | nothing |
POST /moderations | input | nothing |
GET /models, GET /models/{model} | relayed as is | relayed as is |
POST /images/generations, /images/edits, /images/variations | relayed as is | relayed as is |
POST /audio/speech, /audio/transcriptions, /audio/translations | relayed as is | relayed as is |
- A successful JSON reply is restored. Any other reply is relayed as is, with the upstream status. The upstream's response headers are not relayed.
- Query parameters reach the upstream on the routes relayed as is only.
- A streamed
chat/completionsrequest is answered201before the upstream answers, so an upstream error arrives inside the stream body. - The upstream timeout is 60 seconds.
Anthropic-compatible proxy
Prefix: /anthropic/v1
| Request header | Effect |
|---|---|
X-PIIGhost-Upstream | Base URL of the upstream, PIIGHOST_ANTHROPIC_UPSTREAM when absent |
X-PIIGhost-Thread-Id | Fixed thread kept after the request. When absent, each request gets a fresh thread, forgotten once the reply is restored |
Every request header is relayed except the hop-by-hop ones (Connection, Keep-Alive, Proxy-Authenticate, Proxy-Authorization, TE, Trailer, Transfer-Encoding, Upgrade), Host, Content-Length, Accept-Encoding and any X-PIIGhost-* header. Query parameters are relayed.
| Route | De-identified in the request | Restored in the reply |
|---|---|---|
POST /messages | messages[].content, a string or its text, tool_use and tool_result blocks, plus system when PIIGHOST_ANTHROPIC_ANONYMIZE_SYSTEM is on | the text, tool_use and tool_result blocks of content, or the text_delta and input_json_delta of each content_block_delta event when streamed |
POST /messages/count_tokens | same as /messages | nothing, the count is relayed |
- Every string inside a
tool_useinput is rewritten. The other blocks, an image or a document among them, are relayed untouched, and so are thetoolsdefinitions. - The guidance note set by
PIIGHOST_ANTHROPIC_PLACEHOLDER_NOTEis prepended after de-identification. Depending onPIIGHOST_ANTHROPIC_NOTE_PLACEMENT, it goes at the head of the system prompt or of the first user message. - The reply keeps the upstream status. It also keeps the upstream's response headers,
retry-afterandanthropic-ratelimit-*included, except the length, encoding, connection and content-type ones. - A streamed request the upstream refuses is answered with the upstream status as a plain response. An accepted one streams with
200. - The upstream timeout is 60 seconds.
See also
- Server CLI: the
serveoptions and every environment variable. - Deploy a de-identification API: a first server, step by step.
- OpenAI-compatible proxy and Anthropic-compatible proxy: the proxies in use.