/allow/*, with versioned read endpoints under /v1/allow/*. The base URL is https://api.visiqlabs.com.
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 theallow_...key returned once byPOST /allow/agents. Harness keys are full-power on the operational endpoints (evaluate, bundle, telemetry, execution events, decision polling, agent self-registration, the mode stream) and route-confined: calling any management endpoint with one returns403 {"error": "harness_key_not_permitted"}. - Management keys — general automation credentials governed by explicit permission grants. They can call every endpoint on this page. Management keys are launching soon: they are visible in the dashboard under Settings → API Keys, but creating one is not yet enabled. Until then, drive the management endpoints from the dashboard, which authenticates with your session.
rules:evaluate (or the legacy allow:write); read endpoints require rules:read (or the legacy allow:read); 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 carryX-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:page (default 1) and limit (default 50, max 100) query parameters.
Evaluation
The endpoints your SDK or harness calls at runtime. All of them accept a harness key.POST /allow/evaluate
Evaluate an action against your rule set and return an authorization decision. The SDK evaluates most actions locally from the cached rule bundle — this endpoint handles human-approval flows, uncovered-scenario fallback, and remote evaluation. Scope:rules:evaluate or allow:write
Request body:
An
agent_id the platform has never seen is auto-provisioned in monitor mode (observe-only) and the evaluation proceeds — there is no 404 for unregistered agents. The new agent appears on the dashboard Agents page, ready to be promoted to enforce.
The
mask decision is allow-with-transform: the action proceeds, but the harness must first redact the named arguments. The response carries the directives to apply:
field, pattern, replacement, mode (full | partial | email | custom), keepFirst, keepLast, maskChar, and keepPattern. A mask decision whose rule resolves to no usable directives is downgraded to deny server-side — the platform never returns a mask that masks nothing.
The approval_required decision holds the action while a human approves or rejects it (see Human-in-the-Loop). The approval window is capped at 120 seconds — an agent never blocks longer than 2 minutes. hitl_fallback tells the harness what to do if no human responds in time: deny (the default, fail-closed) or mask (proceed with the accompanying arg_redaction_rules applied).
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 403 Forbidden (insufficient scope), 429 Too Many Requests, 500 Internal Server Error
GET /allow/rules/bundle
Fetch the compiled rule bundle for local evaluation. The SDK caches this bundle and revalidates it in the background, so decisions on the hot path never wait on the network. Query parameters:agent_id is required — calling this endpoint without it returns 400 Invalid query parameters. A first bundle pull for an unknown agent_id auto-provisions the agent in monitor mode, exactly like POST /allow/evaluate.
Response:
Response headers:
Status codes:
200 OK, 304 Not Modified, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
GET /v1/allow/decisions/:id
Poll the status of a decision by its ID. The SDK polls this every 2 seconds while a human approval is pending, until the decision resolves or the 120-second window lapses. Scope:rules:read or allow:read
Path parameter: :id — UUID of the decision (from a POST /allow/evaluate response)
Response:
hitl field is null for decisions that did not require approval.
Status codes: 200 OK, 400 Bad Request (invalid UUID), 401 Unauthorized, 404 Not Found, 500 Internal Server Error
POST /allow/telemetry
Submit a batch of locally-evaluated decisions. The SDK calls this after local bundle evaluation so every decision — local or remote — lands in the audit trail, and uncovered scenarios surface for review. Request body:decision_id (UUID, client-generated), agent_id, target_app, action, decision (permit | deny | approval_required | mask), reason, and evaluated_at (ISO 8601). Optional: rule_id, rule_code, enforced, agent_mode, raw_event, evaluation_mode (local | remote), latency_ms, and context (max 32 KB serialized). Decisions without a rule_id are uncovered scenarios and create engineer-category review items.
Items are validated individually: valid items are ingested, malformed ones are rejected per-item and reported back — one bad item never discards the rest of the batch. Only a batch where every item is invalid returns 400.
Response (202):
202 Accepted, 400 Bad Request (envelope invalid or all items rejected), 401 Unauthorized, 500 Internal Server Error
POST /allow/execution-events
Record the post-decision execution outcome for a permitted action, closing the feedback loop on the audit trail. Idempotent: a second submission for the samedecision_id returns 409 Conflict.
Request body:
Response:
{ "updated": true }
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found (decision not found), 409 Conflict (already recorded), 500 Internal Server Error
POST /allow/agents/register
Registration handshake: the SDK calls this once at startup (fire-and-forget) to report the agent’s captured environment. Idempotent — it provisions the agent if this arrives before the first evaluate call. Scope:rules:evaluate or allow:write (harness keys); management keys additionally need the agent-management permission
Request body:
agent_id are optional. kind is sdk (framework harness, the default) or cli_harness (CLI harness). When ip is omitted, the server records the request’s source IP. Owner and approval-routing fields are deliberately not accepted here — those are set by humans via the dashboard or the agent management API.
Response: { "ok": true, "agent_id": "billing-agent" }
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
GET /allow/agents/me/stream
Server-Sent Events stream of mode changes for one agent, so the SDK flips enforcement instantly when you toggle a mode in the dashboard — no waiting for the next bundle poll. Query parameters:agent_id (required)
Events:
mode_snapshot— sent immediately on connect:{ "agent_mode": "enforce" | "monitor" | "off" }mode_changed— sent whenever the agent’s mode changes: same payloadping— heartbeat every 25 seconds to keep idle proxies from closing the connection
200 OK (stream), 400 Bad Request, 401 Unauthorized, 404 Not Found (unknown agent), 500 Internal Server Error
Agents
Manage the agent registry. These are management endpoints — a harness key receives403 harness_key_not_permitted here.
GET /allow/agents
List registered agents, newest first. Query parameters:page (default 1), limit (default 50, max 100)
Response:
200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
POST /allow/agents
Register a new agent. Returns the plaintext API key exactly once — store it securely; it cannot be retrieved again. Request body:
Response (201): the agent object (as in the list response) plus
api_key:
201 Created, 400 Bad Request, 401 Unauthorized, 409 Conflict (agent_id already registered), 500 Internal Server Error
GET /allow/agents/:id
Get a single agent by its row UUID. The response includes the machine-reported environment (agent_os, agent_hostname, agent_ip, agent_username) captured via POST /allow/agents/register, and never includes key material.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
PUT /allow/agents/:id
Update an agent. Only provided fields change; at least one field is required. Request body: any subset ofname, description, mode, owner_email (nullable), hitl_pathway (nullable), trust_tier (nullable), categories, no_coverage (open | closed, or null to clear — see the POST field table), and business_function — one of the twelve business-function identifiers (finance_accounting, hr, engineering, it_security, legal, privacy_compliance, sales, marketing, operations, customer_support, procurement, generic), or null to clear.
name or business_function pins it as human-owned — the platform’s AI naming and classification never overwrite a value you set. A mode change is broadcast immediately to any live GET /allow/agents/me/stream subscriber.
Response: the updated agent object.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
POST /allow/agents/:id/regenerate-naming
Re-run AI naming for one agent now, from its observed and normalized action schemas. An explicit opt-in: it hands name ownership back to the AI even if the name was previously user-set. A naming failure returns200 with status: "skipped" — it never errors the agent.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
DELETE /allow/agents/:id
Delete an agent from the registry. Response:{ "deleted": true }
Deleting an agent does not revoke API keys. Revoke the agent’s key separately under Settings → Harness Keys.
200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
Rules
Rule management. 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 /allow/rules
List action rules, sorted by priority descending — the same order the engine evaluates them in (first match wins). Query parameters:page (default 1), limit (default 50, max 100)
Response:
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 /allow/rules
Create a rule. 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 effect (permit, deny, approval, mask) is derived from the decisions in the policy body. Prefer POST /allow/rules/compile — the compiler drafts and validates the policy against the live evaluation engine.rego_source.
Status codes: 201 Created, 400 Bad Request, 401 Unauthorized, 409 Conflict (a rule with this name already exists), 500 Internal Server Error
POST /allow/rules/compile
Compile a natural-language description into a rule. The compiler reads your existing rules for context, drafts the policy source, and validates it against the live evaluation engine. Rate limit: 10 compile requests per minute per organization — exceeding it returns429.
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:
?stream=true): a text/event-stream of JSON events —
{"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
GET /allow/rules/:id
Get a single rule by UUID, includingrego_source.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
PUT /allow/rules/:id
Update a rule. Same fields asPOST /allow/rules, all optional; at least one is required. Only provided fields change.
Response: the updated rule object.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
DELETE /allow/rules/:id
Delete a rule permanently. Response:{ "deleted": true }
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
Human-in-the-Loop Queue
Review items land here in two categories:enduser items are live approval gates (a rule routed the action to a human — the agent is waiting), while engineer and the other categories are coverage-gap reviews (the action was already decided by a no-coverage default; the item asks whether a rule should exist). The two categories take different response bodies.
These are management endpoints — approvals normally arrive as Slack, Teams, or email prompts, or through the dashboard’s Escalations page. A harness key receives 403 harness_key_not_permitted here.
GET /allow/hitl/queue
List queue items. Defaults tostatus=pending, ordered oldest-first (FIFO for approvers); non-pending queries return newest-first.
Query parameters:
Response:
200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
POST /allow/hitl/queue
Create an approval item directly, for a client that resolved a retrievalescalate decision locally and needs to queue the human gate without re-evaluating. Writes the decision record and the queue item together — never one without the other.
Request body:
Response (201): the created queue item.
Status codes:
201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found (unknown agent), 500 Internal Server Error
POST /allow/hitl/queue/:id
Respond to a pending item. The request body depends on the item’s category.enduser items (live approval gates) — approve or reject:
All other categories (coverage-gap reviews) — dismiss, or link the rule you created to cover the gap:
Sending the wrong body shape for the item’s category returns
400.
Response:
200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 409 Conflict (item already resolved — the current status is returned), 500 Internal Server Error
Audit Log
GET /v1/allow/audit-log
Query the decision audit log. Every decision — local and remote, permit and deny — is recorded, including monitor-mode observations.Tamper evidence comes from the signed record pipeline, not from the query API: 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 any decision server-side via
GET /record/envelopes/:id/verify.rules:read or allow:read (management keys / dashboard session — harness keys cannot query the log)
Query parameters:
Response:
200 OK, 400 Bad Request, 401 Unauthorized, 403 Forbidden (insufficient scope), 429 Too Many Requests, 500 Internal Server Error
Settings
GET /allow/settings
Get your organization’s action-governance settings. If none exist yet, a defaults row is created and returned (201) — this endpoint always returns a valid settings object.
Response:
Status codes:
200 OK, 201 Created (first access, defaults created), 401 Unauthorized, 500 Internal Server Error
PUT /allow/settings
Update settings. Validated field ranges are as documented above — in particularhitl_timeout_seconds must be 30–120 (a higher value is rejected with 400, and the evaluation path clamps to 120 regardless).
Request body:
200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error