Error codes
| Status | code | When | What to do |
|---|---|---|---|
200 | , | Success. Verdict in the body. | Read verdict and act accordingly. |
400 | APP_ID_REQUIRED | X-Antidote-App-Id header missing. | Provision an App and pass its UUID. |
400 | APP_NOT_FOUND | App UUID does not belong to the caller’s workspace. | Check the App ID and the workspace. |
400 | APP_TOKEN_REQUIRED / APP_TOKEN_INVALID | App requires a signed token and one wasn’t supplied or doesn’t verify. | Issue a fresh token via …/apps/{id}/tokens. |
400 | CUSTOM_PARAM_REQUIRED | A required custom param is missing on a scan call. | Include it in custom_params. |
400 | , | Malformed request, or streaming requested on a non‑streamed route. | Fix the body. See detail. |
401 | , | Missing or invalid credentials. | Check the X-API-Key header. |
402 | QUOTA_EXCEEDED | Workspace license‑call quota exceeded. | Body includes usage snapshot and upgrade_url. Wait until reset or upgrade. |
403 | , | Caller lacks the required permission scope. | Add runtime_security.scan (or .view / .manage) to the API key. |
404 | , | Feature not licensed for this workspace. | Module is invisible to unlicensed tenants. |
410 | APP_ARCHIVED | App was deleted or archived. | Switch to a live App. |
422 | , | Schema validation failed. | See detail for the array of field errors. |
423 | APP_DISABLED | App has status="disabled". | Re‑enable in the dashboard. |
429 | , | Per‑second rate limit hit. | Back off. Limits are per workspace, not per key. |
429 | APP_QUOTA_EXCEEDED | App’s hourly or daily event cap reached. | Back off until Retry-After, or raise max_events_per_hour / _per_day on the App. |
503 | , | Firewall disabled, starting up, or metering store unavailable. | Retry with backoff. If persistent, check /health and contact support. |
Rate limits
Three layers stack: per‑second limits, per‑App quotas, and license quota.Per‑second rate limits
| Endpoint | Limit |
|---|---|
/scan/input, /scan/output, /scan/tool-call | 20 / second / workspace. |
/scan/batch | 4 / second / workspace. |
/config PUT | 6 / minute / workspace. |
429 with no code field and
a Retry-After header.
Per‑App event quotas
Setmax_events_per_hour and / or max_events_per_day on the App.
When hit, the response is:
HTTP 429code: "APP_QUOTA_EXCEEDED"Retry-After: 60(hourly) or3600(daily)
License quota
Each call consumes one unit from the workspace’s monthly call quota. Batch items count individually. The 402 response body:Fail‑closed behaviour
If the firewall service is down, scan endpoints fail with503. Your
application should treat this as deny‑by‑default. Letting prompts
through unscored is exactly what the firewall is meant to prevent.
The /health endpoint reports liveness; integrate it into your
readiness probe if you operate the firewall in self‑hosted mode.
FAQ
Why does `block` return 200 instead of 4xx?
Why does `block` return 200 instead of 4xx?
The verdict is information your application needs:
blocked_reason, uuid, the score, the matching phrase.
Returning 200 with the verdict in the body keeps that contract
clean. Non‑200 codes are reserved for protocol failures (auth,
rate limit, quota) where the client genuinely cannot retrieve a
verdict.What's the difference between `redacted_text` and the original?
What's the difference between `redacted_text` and the original?
redacted_text has any detected PII replaced with <CATEGORY>
markers. On verdict: allow, redacted_text equals the
original. On redact, forward redacted_text to your model.
On block, neither field should be sent; surface a refusal to
your user.Can I turn off PII detection?
Can I turn off PII detection?
Not directly, but you can set the redact threshold very high
(e.g. 1.1) so the API surface stays consistent without ever
producing a
redact verdict from injection score. Custom PII
rules can be removed individually via the custom‑rules endpoint.How do I tell if my prompt was de‑fanged before scanning?
How do I tell if my prompt was de‑fanged before scanning?
Check
injection.meta.normalized in the response. When the
firewall canonicalised the input (NFKC, zero‑width strip,
Cyrillic confusables undo, leet undo), this flag is true and
injection.meta.phrase_hits shows what matched.Where do I see what was blocked?
Where do I see what was blocked?
The dashboard’s Runtime Security tab shows verdict
timelines, the event log, and the drift surface. Or hit
/analytics and /events programmatically. See
Observability.Do I need a separate App per environment?
Do I need a separate App per environment?
Recommended. Cloning a production App into a
staging_… clone
gives you separate quotas, separate event streams, and
config‑version history that doesn’t pollute production drift
charts. Use POST …/apps/{id}/clone to seed it.Can one App serve multiple workspaces?
Can one App serve multiple workspaces?
No. Apps are workspace‑scoped, that’s the boundary both the API
key and the App‑Id header check. Cross‑workspace traffic is
rejected as
APP_NOT_FOUND regardless of credentials.What if I want to migrate from the Scan API to the proxy?
What if I want to migrate from the Scan API to the proxy?
Run both for a few days. Point a fraction of traffic at the
proxy via a feature flag and watch the verdict mix on the
dashboard for both paths. Once the proxy mix matches what the
scan API produced, flip the rest.

