Warp

Governance

Governance

Built for the regulatory demands of Korean enterprises — prompt bodies are never stored, personal data is masked before it leaves the gateway, and both spend and data are governed through three budget tiers (organization, team/department, key) plus organization-specific guardrails (custom PII patterns, banned words, model policy). Configure it through the admin API or the governance console in the dashboard.

ZDR — Zero Data Retention

The gateway stores no prompt or response bodies in its database. The usage ledger and the audit trail record metadata only:

  • ts · keyId · orgId · endpoint
  • model · provider · tier
  • promptTokens · completionTokens · costUsd · savedUsd
  • cacheHit · latencyMs · status

Internal debug logging follows the same policy — the content, messages, input, and prompt fields are replaced with "[ZDR]" right before anything is logged. What we export for observability is metadata only.

PII masking

Masking runs when the effective guardrail policy has detectors.pii enabled — set it on the organization (governance.guardrails), on the key (guardrails), or from the governance console. It is off by default: a key with no guardrail policy masks nothing. Once enabled, the five categories below are replaced with per-type tokens before the request leaves for the upstream provider.

TypePatternReplacement token
Resident registration number (RRN)YYMMDD-NNNNNNN — with date validation[MASKED:RRN]
Credit card13–19 digits (spaces/dashes allowed), only when the Luhn checksum passes[MASKED:CARD]
Mobile phone01[016789]-XXXX-XXXX, including the +82 international format[MASKED:PHONE]
EmailStandard email format[MASKED:EMAIL]
Passport numberM/S + 8 digits[MASKED:PASSPORT]
example
Input: My number is 010-1234-5678 and my email is kim@acme.co.kr
Sent:  My number is [MASKED:PHONE] and my email is [MASKED:EMAIL]
  • The masking order is fixed as RRN → CARD → PHONE → EMAIL → PASSPORT to prevent false positives (for example, a phone-number-shaped run inside a card number).
  • Per-key on/off — controlled by the piiMasking flag on the key record, and can be turned off per request with warp.no_pii_mask.
  • The number of masked items is reported in the pii_masked response metadata.
  • Masking is one-way — the gateway keeps no mapping for restoring the original text (ZDR).

Organization-specific patterns and banned words

Beyond the five defaults, an organization can register its own policies under governance.guardrails on the org record. When guardrails is set on a key, the key takes precedence (key override → organization policy → gateway default).

FieldDetails
pii.entitiesIndividual on/off for the five defaults — e.g. { "EMAIL": false } excludes only email from masking
pii.customCustom regex patterns (up to 64) — { "name": "EMPLOYEE_ID", "pattern": "EMP-\\d{6}" }. Matched spans are replaced with [MASKED:EMPLOYEE_ID]. Invalid or pathologically slow patterns are rejected with 400 at registration time.
bannedWordsA banned-word list (up to 500, case-insensitive substring match) — each entry follows the policy mode by default, and its action can individually set block/mask/log-only. An action: "block" entry always blocks even when the policy is mask/shadow.

Model policy

Restrict which models can be used per organization or key — register blockedModels/allowedModels under the org's governance.models or the key's modelPolicy (the key wins, blocks win over allows, and an empty allow list permits everything). Entries accept catalog model ids, namespace wildcards like openai/*, and warp virtual ids.

  • Directly requesting a blocked model is rejected with 403 (model_blocked) — this applies across every surface: chat, embeddings, images, and audio.
  • warp/auto routing drops blocked models from the candidate set and routes around them to allowed models — if an entire tier is blocked, it escalates to the tier above.
  • GET /v1/models returns only the list reflecting the key's effective policy — blocked models do not appear in the catalog.

Tiered guardrail stack

The PII masking above is stage 1 of the guardrail stack. Before a request leaves for the upstream, it passes through the detectors below in fastest-first order — the cheap regex stages catch most of it, and the expensive stages run only when needed. Per-detector on/off is controlled by the key's guardrails.detectors policy (see the API reference).

StageDetectorLatencyDetails
1Structured PII detectors.piisub-msRegex — RRN, card (Luhn), phone, email, passport (same as the table above)
2Secrets / entropy detectors.secrets~1–2msAPI-key and cloud-token patterns plus high-entropy strings (leaked credentials)
3Presidio NER detectors.presidioexternal callUnstructured PII (names, addresses, organizations) — an external Presidio instance, enabled only when PRESIDIO_URL is set
4Prompt-injection heuristics detectors.injectionsub-msScreens for jailbreak and instruction-override patterns — the integration point for Prompt Guard 2

The three enforcement modes

Separate from what gets detected, how a detection is handled is set by the key's guardrails.mode.

ModeNameBehavior
blockValidationRejects the request immediately on a violation — only a summary of the violated categories is returned, never the body.
maskMutationAutomatically masks the detected spans with [MASKED:*] tokens, then forwards to the upstream (one-way, not reversible).
shadowShadowPasses the request through unchanged and only records the detections (audit only) to the Audit Trail — ideal for a dry run before rolling out a policy.

In any mode, the per-request result is reported in the guardrails.findings response metadata and the x-warp-guardrail-findings header — it carries only counts and categories, never the original text (ZDR).

Streaming output guardrails

When guardrails.streamOutput is enabled on a key, the model's output is inspected too — SSE chunks are collected in a sliding buffer and validated incrementally, so even a resident registration number or secret split across token boundaries is caught. On a confirmed violation, the stream is halted immediately with finish_reason: "content_filter".

Audit Trail

Every guardrail decision is recorded to an immutable, append-only metadata log — only the mode, per-category detection counts, whether it was blocked, and the request id are stored; prompt and response bodies are not (ZDR). Query it with GET /admin/audit (see the API reference).

BYOK — Bring Your Own Key

Use the upstream contracts your organization already holds (OpenAI, Anthropic, and so on) as-is. Register a key per provider, and calls to that provider use your organization's key instead of the platform's shared key — across chat, embeddings, image and audio.

The simplest way is the console BYOK tab (owner only, one field per provider). Keys are stored at the organization level; to point a single API key at a different account, set a per-provider override under byokKeys on that key record via the admin API. Stored keys are encrypted at rest and never returned on read — you only ever see which providers are configured.

The platform fee still applies to BYOK. A request served on your own key still draws the 5% platform fee from your credits on its usage, and is blocked with 402 when your balance is exhausted. That usage is priced at Warp's catalog rates — if you have negotiated different rates or a subscription with the provider, the fee is still computed from the catalog price, which may differ from your actual provider bill.

Registering through the admin API (master key required; see the API reference for the full schema):

terminal
curl -X POST https://api.warp.inc/admin/keys \
  -H "Authorization: Bearer $WARP_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orgId": "org_abc123",
    "name": "prod-backend",
    "piiMasking": true,
    "zdr": true,
    "byokKeys": {
      "openai": "sk-proj-…",
      "anthropic": "sk-ant-…"
    }
  }'

Budget policy — organization, team (department), key

Budgets are tallied by UTC calendar month and enforced at three levels: organization → team (department) → key. null means unlimited. A key joins a team via teamId, and team spend is recorded against its membership at request time — moving a key to another team leaves past spend with the original team.

ScopeBehavior when exceeded
Organization budgetA hard cap — always rejected with 402 (budget_exceeded). It takes precedence regardless of the team and key policies.
Team (department) budgetFollows the team's overBudgetBehavior "reject" denies with 402, "downgrade" forces a switch to Tier 3. When the team and key both exceed at once, the stricter decision wins.
Key budgetFollows the key's overBudgetBehavior "reject" denies with 402, "downgrade" forces the tier to 3 to keep serving at low cost.
  • downgrade applies on every path. The forced tier acts as a spend cap, so a request that names a specific model above that cap re-routes through the forced tier instead of keeping the named model — otherwise only clients with a hardcoded model id would escape the downgrade.
  • Embeddings have no downgrade concept — exceeding the budget only ever rejects with 402.