Documentation Index
Fetch the complete documentation index at: https://docs.visiqlabs.com/llms.txt
Use this file to discover all available pages before exploring further.
All ALLOW endpoints are mounted under /allow/*. The base URL is https://api.visiqlabs.com.
Authentication
ALLOW uses two authentication schemes depending on the endpoint:
- SDK endpoints — API key authentication via
Authorization: Bearer <api_key>. The API key is the ak_live_... / allow_... key associated with your agent, obtained when registering the agent via POST /allow/agents.
- Admin endpoints — Session authentication via a vendor session token. Used for managing rules, agents, settings, and reviewing the HITL queue and audit log.
All endpoints under /allow/* require authentication. Requests without a valid credential receive 401 Unauthorized.
SDK Endpoints
These endpoints are called directly by the ALLOW SDK and do not require a session — they accept the agent API key.
POST /allow/evaluate
Evaluate an action against the vendor’s rule set and return an authorization decision. This is the fallback path used when no cached rule bundle is available or when a request results in approval_required (HITL).
The SDK primarily evaluates rules locally from the cached bundle — this endpoint is called for uncovered scenarios, HITL resolution polling, and when local evaluation is disabled.
Authentication: Bearer API key (Authorization: Bearer ak_live_...)
Request body:
{
"agent_id": "my-agent",
"target_app": "stripe.com",
"action": "POST /v1/charges",
"context": {
"amount": 5000,
"currency": "usd"
}
}
| Field | Type | Required | Description |
|---|
agent_id | string | Yes | Identifier of the agent making the request |
target_app | string | Yes | Hostname or app identifier being called |
action | string | Yes | HTTP method, or method + path |
context | object | No | Additional key-value context for rule matching |
Response:
{
"decision_id": "550e8400-e29b-41d4-a716-446655440000",
"decision": "permit",
"reason": "Matched rule: allow-stripe-reads"
}
| Field | Type | Description |
|---|
decision_id | string (UUID) | Unique ID for this decision, used for HITL polling and audit |
decision | permit | deny | approval_required | The authorization outcome |
reason | string | Human-readable explanation of the decision |
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found (agent not registered), 500 Internal Server Error
GET /allow/decisions/:id
Poll the status of an authorization decision by its ID. Used by the SDK to wait for HITL approval — the SDK polls every 2 seconds until the decision resolves from approval_required to permit or deny.
Authentication: Bearer API key
Path parameter: :id — UUID of the decision (from a previous POST /allow/evaluate response)
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "my-agent",
"target_app": "stripe.com",
"action": "POST /v1/charges",
"context": { "amount": 5000 },
"decision": "permit",
"reason": "Approved by engineer@example.com",
"rule_id": null,
"hitl_result": "approved",
"hitl_responded_at": "2026-04-13T10:35:00Z",
"hitl_responded_by": "engineer@example.com",
"created_at": "2026-04-13T10:30:00Z",
"hitl": {
"id": "hitl-uuid",
"status": "approved",
"category": "enduser",
"expires_at": "2026-04-13T10:35:00Z",
"responded_at": "2026-04-13T10:35:00Z",
"responded_by": "engineer@example.com",
"created_at": "2026-04-13T10:30:00Z"
}
}
The hitl field is null for non-HITL decisions.
Status codes: 200 OK, 400 Bad Request (invalid UUID), 401 Unauthorized, 404 Not Found, 500 Internal Server Error
GET /allow/rules/bundle
Fetch the compiled rule bundle for local evaluation by the SDK. The bundle is a JSON object containing all enabled rules for the vendor, sorted by priority descending.
Authentication: Bearer API key
Request headers:
| Header | Description |
|---|
If-None-Match | ETag from a previous response. Server returns 304 Not Modified if the bundle has not changed |
Response:
{
"version": "a3b4c5d6e7f8...",
"rules": [
{
"id": "rule-uuid",
"name": "Allow Stripe reads",
"effect": "allow",
"resource_type": "stripe.com",
"conditions": [
{ "field": "action", "operator": "in", "value": ["GET", "HEAD"] }
],
"priority": 10
}
]
}
Response headers:
| Header | Value |
|---|
ETag | "<sha256-version>" (quoted, per RFC 7232) |
Cache-Control | private, max-age=60 |
Status codes: 200 OK, 304 Not Modified, 401 Unauthorized, 500 Internal Server Error
POST /allow/telemetry
Submit a batch of decision telemetry events from the SDK. Called by the SDK after local evaluation to persist audit log entries and surface uncovered scenarios.
Authentication: Bearer API key
Request body:
{
"decisions": [
{
"decision_id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "my-agent",
"target_app": "stripe.com",
"action": "GET /v1/customers",
"decision": "permit",
"reason": "Matched rule: allow-stripe-reads",
"evaluated_at": "2026-04-13T10:30:00.000Z",
"rule_id": "rule-uuid"
}
]
}
Maximum 100 decisions per request. The rule_id field is omitted for uncovered scenarios (no matching rule). Uncovered decisions automatically create engineer-category HITL queue entries.
Response:
{
"received": 5,
"uncovered": 1
}
Status codes: 202 Accepted, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
Admin Endpoints
These endpoints require session authentication and are used for managing your ALLOW configuration, reviewing decisions, and responding to HITL items.
Rules
GET /allow/rules
List rules for the authenticated vendor, paginated.
Query parameters: page (default 1), limit (default 50, max 100)
Response:
{
"rules": [
{
"id": "rule-uuid",
"name": "Allow Stripe reads",
"description": "Permit read-only Stripe API calls",
"natural_language": "Allow my billing agent to read from Stripe",
"priority": 10,
"enabled": true,
"created_at": "2026-04-13T10:00:00Z",
"updated_at": "2026-04-13T10:00:00Z"
}
],
"total": 12,
"page": 1,
"limit": 50,
"pages": 1
}
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
POST /allow/rules
Create a new rule.
Request body:
{
"name": "Allow Stripe reads",
"description": "Permit read-only Stripe API calls",
"rego_source": "package allow\ndefault allow = false\nallow { input.action == \"GET\" }",
"natural_language": "Allow my billing agent to read from Stripe",
"priority": 10,
"enabled": true
}
| Field | Type | Required | Description |
|---|
name | string | Yes | Rule name (max 255 chars) |
description | string | No | Description (max 1000 chars) |
rego_source | string | Yes | Rego policy source (see note below) |
natural_language | string | No | Natural language description used for AI compilation (max 2000 chars) |
priority | number | No | Evaluation priority. Default 0 |
enabled | boolean | No | Whether the rule is active. Default true |
The rego_source field stores the Rego policy source. The effect (allow, deny, hitl) is derived from keywords in the Rego source. Use the POST /allow/rules/compile endpoint to generate Rego from a plain-English description.
Response: The created rule object. rego_source is included.
Status codes: 201 Created, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
GET /allow/rules/:id
Get a single rule by UUID.
Response: Rule object including rego_source.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
PUT /allow/rules/:id
Update a rule. Only provided fields are updated.
Request body: Same shape as POST /allow/rules but all fields optional. At least one field must be provided.
Response: 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:
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
POST /allow/rules/compile
Compile a natural language description into a Rego rule using AI. The agent reads your existing rules for context, drafts the Rego source, validates the syntax, and optionally saves the rule to the database.
Request body:
{
"prompt": "Allow my billing agent to read from Stripe but block all write operations",
"nodeRef": "optional-graph-node-reference"
}
| Field | Type | Required | Description |
|---|
prompt | string | Yes | Plain-English rule description (max 4000 chars) |
nodeRef | string | No | Optional rule graph node reference for context |
Response:
{
"rego_source": "package allow\ndefault allow = false\nallow { ...",
"explanation": "This rule permits GET and HEAD requests to stripe.com for the billing-agent..."
}
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 422 Unprocessable Entity (AI could not produce a valid rule), 500 Internal Server Error
Agents
GET /allow/agents
List registered agents for the authenticated vendor.
Query parameters: page (default 1), limit (default 50, max 100)
Response:
{
"agents": [
{
"id": "agent-row-uuid",
"agent_id": "my-agent",
"name": "Billing Agent",
"description": "Handles subscription billing operations",
"mode": "enforce",
"created_at": "2026-04-13T10:00:00Z",
"updated_at": "2026-04-13T10:00:00Z"
}
],
"total": 3,
"page": 1,
"limit": 50,
"pages": 1
}
Status codes: 200 OK, 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:
{
"agent_id": "my-agent",
"name": "Billing Agent",
"description": "Handles subscription billing operations",
"mode": "audit"
}
| Field | Type | Required | Description |
|---|
agent_id | string | Yes | Logical identifier used in SDK config (max 255 chars) |
name | string | Yes | Display name (max 255 chars) |
description | string | No | Description (max 1000 chars) |
mode | enforce | audit | off | No | Evaluation mode. Default audit |
api_key | string | No | Custom API key (min 16 chars). Auto-generated if omitted |
Response:
{
"id": "agent-row-uuid",
"agent_id": "my-agent",
"name": "Billing Agent",
"description": "Handles subscription billing operations",
"mode": "audit",
"api_key": "allow_a1b2c3d4e5f6...",
"created_at": "2026-04-13T10:00:00Z",
"updated_at": "2026-04-13T10:00:00Z"
}
The api_key field is only present in the POST response. Subsequent GET responses omit it entirely. If lost, you must delete and re-create the agent.
Status codes: 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.
Response: Agent object without api_key.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
PUT /allow/agents/:id
Update an agent’s name, description, or mode.
Request body:
{
"name": "Updated Agent Name",
"mode": "enforce"
}
Response: Updated agent object.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
DELETE /allow/agents/:id
Delete an agent. This also invalidates the agent’s API key.
Response:
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
Audit Log
GET /allow/audit-log
Query the immutable authorization decision audit log. All decisions — local and remote, permit and deny — are recorded here.
Query parameters:
| Parameter | Type | Description |
|---|
agent_id | string | Filter by agent logical ID |
target_app | string | Filter by target application |
decision | permit | deny | approval_required | Filter by decision outcome |
hitl_result | approved | rejected | timeout | Filter by HITL resolution |
start_date | ISO 8601 datetime | Lower bound on created_at |
end_date | ISO 8601 datetime | Upper bound on created_at |
page | number | Page index (default 1) |
limit | number | Records per page (default 50, max 100) |
Response:
{
"entries": [
{
"id": "decision-uuid",
"agent_id": "my-agent",
"target_app": "stripe.com",
"action": "POST /v1/charges",
"context": { "amount": 5000 },
"decision": "permit",
"reason": "Matched rule: allow-stripe-reads",
"rule_id": "rule-uuid",
"hitl_result": null,
"hitl_responded_at": null,
"hitl_responded_by": null,
"created_at": "2026-04-13T10:30:00Z"
}
],
"total": 843,
"page": 1,
"limit": 50,
"pages": 17
}
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error
HITL Queue
GET /allow/hitl/queue
List pending HITL items for the authenticated vendor. Returns only items with status: pending, ordered oldest-first.
Query parameters: page (default 1), limit (default 50, max 100)
Response:
{
"items": [
{
"id": "hitl-uuid",
"decision_id": "decision-uuid",
"agent_id": "my-agent",
"target_app": "stripe.com",
"action": "POST /v1/charges",
"context": { "amount": 5000 },
"category": "enduser",
"status": "pending",
"ai_recommended_rule": null,
"notified_via": [],
"expires_at": "2026-04-13T10:35:00Z",
"responded_at": null,
"responded_by": null,
"created_at": "2026-04-13T10:30:00Z"
}
],
"total": 2,
"page": 1,
"limit": 50,
"pages": 1
}
Status codes: 200 OK, 401 Unauthorized, 500 Internal Server Error
POST /allow/hitl/queue/:id
Approve or reject a pending HITL item. Only items with status: pending can be responded to.
Request body:
{
"decision": "approved",
"responded_by": "engineer@example.com"
}
| Field | Type | Required | Description |
|---|
decision | approved | rejected | Yes | The human’s decision |
responded_by | string | Yes | Identifier of the human responding (email or name) |
Response:
{
"id": "hitl-uuid",
"decision_id": "decision-uuid",
"status": "approved",
"responded_at": "2026-04-13T10:32:00Z",
"responded_by": "engineer@example.com"
}
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 409 Conflict (item already resolved), 500 Internal Server Error
Execution Events
POST /allow/execution-events
Record the post-decision execution outcome for a previously issued authorization decision. Called by the SDK after the permitted action completes (or fails) to close the feedback loop on the audit log.
This endpoint is idempotent — if an execution result has already been recorded for the given decision_id, it returns 409 Conflict.
Authentication: Bearer API key
Request body:
{
"decision_id": "550e8400-e29b-41d4-a716-446655440000",
"result": "success",
"details": "Stripe charge created successfully (ch_1234)"
}
| Field | Type | Required | Description |
|---|
decision_id | string (UUID) | Yes | ID of the authorization decision (from a previous POST /allow/evaluate response) |
result | string | Yes | Execution outcome: success, failure, or error |
details | string | No | Optional details about the execution outcome (max 2000 chars) |
Response:
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found (decision not found), 409 Conflict (execution result already recorded), 500 Internal Server Error
Settings
GET /allow/settings
Get the vendor’s ALLOW settings. If no settings row exists yet, one is created with defaults and returned. This endpoint always returns a valid settings object.
Response:
{
"id": "settings-uuid",
"no_coverage_default": "ask",
"autopilot_enabled": false,
"hitl_timeout_seconds": 300,
"notification_channels": [],
"created_at": "2026-04-13T10:00:00Z",
"updated_at": "2026-04-13T10:00:00Z"
}
| Field | Type | Description |
|---|
no_coverage_default | approve | deny | ask | What happens when no rule matches in enforce mode |
autopilot_enabled | boolean | Whether AI auto-creates draft rules for uncovered scenarios |
hitl_timeout_seconds | number | HITL wait timeout in seconds (30–86400) |
notification_channels | array | Configured notification channels |
Status codes: 200 OK, 201 Created (first access, defaults created), 401 Unauthorized, 500 Internal Server Error
PUT /allow/settings
Update the vendor’s ALLOW settings. Performs an upsert — safe to call even if settings have not been initialized.
Request body: Any subset of the settings fields. At least one field must be provided.
{
"no_coverage_default": "deny",
"autopilot_enabled": false,
"hitl_timeout_seconds": 600
}
Response: Updated settings object.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error