Privacy with Veil

Turn on Veil for a project so the AI Gateway finds personal details in prompts and masks, redacts, tokenises or blocks them before they leave for a provider.

Updated 4 November 2026

Veil is Dekka's engine for finding personal information in text. When a project turns it on, the AI Gateway sends the text of every request to Veil before any provider sees it, and applies the project's policy to what Veil finds. A Ghana Card PIN can be refused outright, a phone number masked to 024 *** 4567, and an email address replaced with a placeholder that's put back in the model's reply. The provider receives only what the policy allows.

Turn it on

Open AI Gateway, then Routing, choose the project, and find Privacy with Veil below the routing rules. Choose a mode and save.

Mode What happens
Off Prompts reach the provider as your app sends them. This is the default.
Detect Veil reports what it finds in the request log and the text goes unchanged. Use it to see what your prompts contain before you change anything.
Protect Veil applies the policy before the text leaves. A request the policy blocks never reaches a provider.

Veil scans every message in a request, including your system prompt and earlier replies, because a previous reply can contain values Veil restored. Try it runs sample text through your settings, saved or not, and shows what the provider would receive. Nothing you try is stored.

Choose a policy

The built-in default-protection policy blocks credentials and Ghana Card PINs, masks phone numbers and redacts every other detail Veil finds as a typed placeholder such as [EMAIL_ADDRESS].

For anything else, write a custom policy in YAML. Each rule matches entity types or categories and chooses an action, and the default action covers everything no rule matches. The name and revision are recorded with every request, so you can tell which version of a policy handled it.

policy.yaml
version: 1
name: support-desk
revision: "1"
default_action: redact
rules:
  - match: { entity_types: [GH_GHANA_CARD_PIN] }
    action: block
  - match: { entity_types: [GH_MOBILE_NUMBER] }
    action: mask
  - match: { entity_types: [EMAIL_ADDRESS] }
    action: tokenize
  - match: { categories: [CREDENTIAL] }
    action: block

The actions are detect (report and leave it), allow, mask, redact, tokenize (a numbered placeholder such as [EMAIL_ADDRESS_1]) and block. A block always wins over any other rule. Veil checks a custom policy when you save it, and any error appears under the editor with its path, such as rules.0.action.

Stricter rules for some providers

A provider can have its own policy and its own choice of what happens when Veil can't scan. Use it to block more before prompts reach a provider outside Ghana, for example, while keeping a lighter policy for others. When a request falls back to another provider, the gateway applies that provider's policy, and reuses the first scan when the policies are the same.

When Veil can't scan

If Veil is unreachable or doesn't answer within two seconds, the project's enforcement decides:

  • Refuse the request answers 503 privacy_scan_unavailable, and nothing reaches a provider. This is the default.
  • Send it with a warning sends the request as your app wrote it and adds X-Dekka-Veil-Warning: scan_unavailable to the response.

Detect mode never refuses a request, since it only reports.

Restoring tokenised values

With Restore tokenised values in replies on, placeholders the policy creates are put back in the model's reply, so your app sees ama@example.com where the model wrote [EMAIL_ADDRESS_1]. The mapping between placeholders and values exists in memory for that one request and is never logged or stored.

Streamed replies are restored as they arrive. The gateway holds back at most one placeholder's length of text, so a placeholder split across two chunks is still replaced.

Scanning replies

With Scan replies too on, Veil also scans each reply and the request log records how many personal details it found. A non-streamed reply's count is in the X-Dekka-Veil-Response-Findings header. A streamed reply is scanned after it finishes, so the count appears only in the log, and the reply itself is never changed.

Images and audio

Veil reads text only, so a project that uses Veil refuses images in chat and audio for transcription with 422 privacy_media_blocked, unless you choose Allow with a warning. Allowed requests carry X-Dekka-Veil-Warning: media_not_scanned. Image prompts and text to speak are scanned like any other text, and transcripts can be scanned as replies.

Headers

Header What it says
X-Dekka-Veil protected, detected or off
X-Dekka-Veil-Findings How many personal details Veil found in the request
X-Dekka-Veil-Response-Findings How many it found in the reply, when replies are scanned
X-Dekka-Veil-Warning scan_unavailable or media_not_scanned

What a refusal looks like

{
  "error": {
    "code": "blocked_by_privacy_policy",
    "message": "This project's privacy policy blocks the request, because it contains details the policy doesn't allow to leave.",
    "details": {
      "policy": "default-protection@2026-11-04",
      "by_entity": { "GH_GHANA_CARD_PIN": 1 },
      "by_action": { "block": 1 }
    }
  }
}

A refusal names the kinds of detail and how many, never the values.

What the request log keeps

Each request on the Requests page shows whether Veil protected, detected or blocked it, and which kinds of detail it found. The log keeps the mode, the policy and its revision, counts by entity type and by action, and how long the scan took. It never keeps the prompt, the reply, the values Veil found or the restore map.

With the API

The settings are part of a project's routing rules, and have their own endpoints too.

curl -X PUT https://api.dekka.cloud/v1/projects/$PROJECT_ID/ai/privacy \
  -H "Authorization: Bearer $DEKKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "protect", "preset": "default-protection", "enforcement": "closed", "restore_in_responses": true}'

POST /v1/projects/{id}/ai/privacy/preview with {"text": "…", "provider": "openai"} returns what the provider would receive, with the findings, and stores nothing.