Skip to main content
Retrieval-governance endpoints are mounted under /recall/*, with the versioned audit-log read at /v1/recall/audit-log. The base URL is https://api.visiqlabs.com. Action governance and retrieval governance share a single rule engine — each rule declares which facets it applies to. The endpoints on this page are the retrieval facet: they govern what your agents can see (retrieved documents, tool results, rendered prompts), with decisions allow, deny, redact, and escalate.

Authentication

All endpoints require a Bearer credential: Authorization: Bearer <key>. Requests without a valid credential receive 401 Unauthorized. Two credential audiences exist:
  • Harness keys — the operational credential your SDK or harness runs with. Either a vq_prod_... / vq_test_... key minted in the dashboard under Settings → Harness Keys, or the allow_... key returned once by POST /allow/agents. On the retrieval surface the operational allowlist covers exactly POST /recall/evaluate and GET /recall/rules/bundle — calling any other endpoint on this page with a harness key returns 403 {"error": "harness_key_not_permitted"}.
  • Management keys — general automation credentials governed by explicit permission grants. They can call every endpoint on this page. Mint one in the dashboard under Settings → API Keys, scoped to the exact permission grants it needs; or drive the management endpoints from the dashboard, which authenticates with your session.
Scoped keys are additionally checked against their granted scopes. Evaluation requires rules:evaluate (or the legacy recall:write); reads require rules:read (or the legacy recall:read); rule mutations require rules:write (or the legacy recall:write); full_access satisfies everything. A key without the required scope receives 403 {"error": "insufficient_scope"} listing the required and granted scopes.

Rate limiting

Every API-key request passes a per-key sliding-window rate limit (default 600 requests per 60 seconds). Responses carry X-RateLimit-Limit and X-RateLimit-Remaining headers; exceeding the window returns 429 with a Retry-After header and body {"error": "rate_limited", "detail": "API key rate limit exceeded.", "retryAfter": <seconds>}.

List responses

Every list endpoint on this page returns the same envelope:
Pagination is controlled by page (default 1) and limit (default 50, max 100) query parameters.

Evaluation

The endpoints your SDK or harness calls at runtime. Both accept a harness key. The SDK evaluates most retrievals locally from the cached rule bundle — the server and the SDK run the same policy interpreter over the same rule source, so local and remote decisions agree by construction.

POST /recall/evaluate

Evaluate a retrieval against your suppression rules and return a decision. Scope: rules:evaluate or recall:write Request body:
Agent attributes are server-authoritative. The platform looks up agent_id in the agent registry and hydrates its assigned trust tier, categories, and business function into rule input (input.agent.*). An assigned trust tier overrides the request’s trust_tier — the body value is only a fallback for agents with no assigned tier, so varying it in a request cannot spoof a tier-gated rule. Unlike the action facet’s evaluate endpoint, this endpoint does not auto-provision unknown agents.
For an agent in monitor mode, a non-allow decision is normalized to allow before it is recorded and returned, with reason_code: "MONITOR_MODE" and the engine’s original reason preserved behind a Monitor: prefix.
Response:
Decision semantics:
  • allow — the content passes to the agent unchanged.
  • deny — the content is suppressed; the agent never sees it.
  • redact — the content proceeds with the accompanying redaction_rules applied.
  • escalate — hold the content for human review; if no human approves in time, fall back per hitl_fallback.
Redaction directives. Each entry in redaction_rules may include field, pattern, replacement, mode (full | partial | email | custom), keepFirst, keepLast, maskChar, and keepPattern. full replaces the whole match (the default); partial keeps the first/last characters; email keeps the first character of the local part plus the domain; custom keeps every keepPattern match and masks the rest. A redact decision from a rule whose directives resolve to nothing passes content through unchanged — unless the rule is flagged fail-closed, in which case the decision is downgraded to deny server-side. The escalate decision tells your harness to hold the content for human review — this endpoint does not itself notify approvers; register the approval through the human-in-the-loop flow (see Human-in-the-Loop). hitl_fallback tells the harness what to do when no human responds in time: deny (the default, fail-closed) or mask (proceed with the accompanying redaction_rules applied). Reason codes:
The response carries no receipt ID — tamper evidence is asynchronous. For organizations with artifact signing enabled, each decision emits a record envelope that receives an Ed25519 receipt off the request path, is Merkle-batched under a KMS-signed root with an RFC 3161 timestamp, and lands in a hash-chained checkpoint log. Verify a decision’s record envelope server-side via GET /record/envelopes/:id/verify (:id is the record envelope’s UUID, not the decision_id returned here).
Minimum-necessary at rest: the decision is evaluated against the real values, but the persisted copies of resource_metadata and query are masked — first by the matched rule’s redaction directives, then by an always-on value-shape redaction floor. The raw original is discarded by default; organizations can opt in to retaining it encrypted, revealable only via the retained-original endpoint.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 403 Forbidden (insufficient scope), 429 Too Many Requests, 500 Internal Server Error Example:

GET /recall/rules/bundle

Fetch your enabled retrieval rules as one bundle for local evaluation. Clients can cache this bundle and revalidate it with If-None-Match, so retrieval decisions on the hot path never wait on the network. (The VisIQ SDK consumes the unified GET /rules/bundle, which carries both facets.) Request headers: Response:
Response headers: Status codes: 200 OK, 304 Not Modified, 401 Unauthorized, 403 Forbidden (insufficient scope), 500 Internal Server Error

Rules

Rule management. These are management endpoints — a harness key receives 403 harness_key_not_permitted here. The recommended authoring path is the dashboard’s rule editor (natural-language compile, visual condition builder, and a Simulate panel that checks a new rule against your recent traffic — rules projected to inhibit more than 5% of it are blocked). These endpoints are the programmatic equivalent.

GET /recall/rules

List retrieval rules, sorted by priority descending — the same order the engine evaluates them in (first match wins). Permission: recall_rules:view Query parameters: page (default 1), limit (default 50, max 100) Response:
The list omits rego_source; fetch a single rule to read the policy source. Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error

POST /recall/rules

Create a retrieval rule. Permission: recall_rules:create Request body:
rego_source holds the rule’s policy source in the platform’s Rego-subset condition language (equality, inequality, set membership, startswith/endswith/contains, regex, counts, and numeric comparisons). The decision (allow, deny, redact, escalate) is derived from the policy body. Prefer POST /recall/rules/compile — the compiler drafts and validates the policy against the live evaluation engine.
Response (201): the created rule object, including rego_source. Status codes: 201 Created, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error

POST /recall/rules/compile

Compile a natural-language description into a retrieval rule. The compiler reads your existing rules for context, drafts the policy source, and validates it against the live evaluation engine. Permission: recall_rules:create Rate limit: 10 compile requests per minute per organization — exceeding it returns 429. Query parameters: stream=true — stream the compile over Server-Sent Events instead of a single JSON response (also triggered by Accept: text/event-stream). Request body:
Response:
Streaming response (?stream=true): a text/event-stream of JSON events —
On failure the stream emits {"type":"error","message":"..."} and closes. Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 422 Unprocessable Entity (the compiler could not produce a valid policy — refine the prompt), 429 Too Many Requests, 500 Internal Server Error, 503 Service Unavailable (rule compilation is not configured in this environment)

GET /recall/rules/:id

Get a single rule by UUID, including rego_source. Permission: recall_rules:view Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error

PUT /recall/rules/:id

Update a rule. Same fields as POST /recall/rules, all optional; at least one is required. Only provided fields change. trust_tier, surface, and principal_exclusions accept null to clear. Permission: recall_rules:update Response: the updated rule object. Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error

DELETE /recall/rules/:id

Delete a rule permanently. Permission: recall_rules:delete Response: { "deleted": true } Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error

Emergency Bypass

Temporarily suspend one rule’s enforcement during an incident — see Emergency Bypass for when and why. Bypasses auto-expire; expiry is checked at evaluation time, so there is no revocation lag. Both activation and deactivation are written to the audit log with reason code EMERGENCY_BYPASS.

POST /recall/rules/:id/bypass

Activate an emergency bypass on a rule. While active, any request that reaches the bypassed rule in the priority cascade returns allow with reason code EMERGENCY_BYPASS — even requests the rule’s conditions would not have matched — and lower-priority rules never run for it. Higher-priority rules keep enforcing. Permission: recall_rules:bypass Request body:
Response: the full rule object with the bypass fields populated:
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 409 Conflict (a bypass is already active on this rule), 500 Internal Server Error

DELETE /recall/rules/:id/bypass

Deactivate an active bypass before it expires. Permission: recall_rules:bypass Response: the full rule object with the bypass fields cleared. Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 409 Conflict (no active bypass on this rule), 500 Internal Server Error

Audit Log


GET /v1/recall/audit-log

Query the retrieval decision audit log. Every decision made through this endpoint — allow and deny, including monitor-mode observations and bypass activations — is recorded. (Locally evaluated SDK decisions surface through the runtime telemetry pipeline instead.) The log is append-only; no writes are accepted via this API. Permission: recall_audit_log:view
Tamper evidence comes from the signed record pipeline, not from the query API: for organizations with artifact signing enabled, each decision emits a record envelope that receives an asynchronous Ed25519 receipt, is Merkle-batched under a KMS-signed root with an RFC 3161 timestamp, and lands in a hash-chained checkpoint log. Verify a decision’s record envelope server-side via GET /record/envelopes/:id/verify.
Scope: rules:read or recall:read (management keys / dashboard session — harness keys cannot query the log) Query parameters: Rows recorded under monitor mode carry reason_code: "MONITOR_MODE"; it is not yet accepted as a filter value. Response:
metadata.query and metadata.resource_metadata are the masked persisted copies (rule directives plus the always-on redaction floor); metadata.floor_detectors names any floor detectors that fired, and metadata.decision_id is the underlying decision’s ID for the retained-original reveal below. receipt_id is a legacy column and is null on new rows — receipts flow through the record pipeline instead. Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 403 Forbidden (insufficient scope), 429 Too Many Requests, 500 Internal Server Error

Retained Originals


GET /recall/decisions/:id/unmask

Reveal the retained, encrypted original payload for one retrieval decision. Persisted copies are masked at rest; when your organization has enabled retained-original storage (off by default), the raw resource_metadata and query are additionally stored AES-256-GCM-encrypted, and this endpoint decrypts them. Permission: payloads:unmask This is a management endpoint gated on the payloads:unmask permission — only explicitly-authorized roles can ever see the original values, and every reveal is logged with who requested it. Harness keys receive 403. Path parameter: :id — the decision UUID (decision_id from POST /recall/evaluate, or metadata.decision_id on an audit-log row) Response:
Status codes: 200 OK, 400 Bad Request (invalid UUID), 401 Unauthorized, 403 Forbidden, 404 Not Found (unknown decision, or retention is disabled for your organization), 500 Internal Server Error