Skip to main content

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"
  }
}
FieldTypeRequiredDescription
agent_idstringYesIdentifier of the agent making the request
target_appstringYesHostname or app identifier being called
actionstringYesHTTP method, or method + path
contextobjectNoAdditional key-value context for rule matching
Response:
{
  "decision_id": "550e8400-e29b-41d4-a716-446655440000",
  "decision": "permit",
  "reason": "Matched rule: allow-stripe-reads"
}
FieldTypeDescription
decision_idstring (UUID)Unique ID for this decision, used for HITL polling and audit
decisionpermit | deny | approval_requiredThe authorization outcome
reasonstringHuman-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:
HeaderDescription
If-None-MatchETag 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:
HeaderValue
ETag"<sha256-version>" (quoted, per RFC 7232)
Cache-Controlprivate, 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
}
FieldTypeRequiredDescription
namestringYesRule name (max 255 chars)
descriptionstringNoDescription (max 1000 chars)
rego_sourcestringYesRego policy source (see note below)
natural_languagestringNoNatural language description used for AI compilation (max 2000 chars)
prioritynumberNoEvaluation priority. Default 0
enabledbooleanNoWhether 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:
{ "deleted": true }
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"
}
FieldTypeRequiredDescription
promptstringYesPlain-English rule description (max 4000 chars)
nodeRefstringNoOptional 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"
}
FieldTypeRequiredDescription
agent_idstringYesLogical identifier used in SDK config (max 255 chars)
namestringYesDisplay name (max 255 chars)
descriptionstringNoDescription (max 1000 chars)
modeenforce | audit | offNoEvaluation mode. Default audit
api_keystringNoCustom 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:
{ "deleted": true }
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:
ParameterTypeDescription
agent_idstringFilter by agent logical ID
target_appstringFilter by target application
decisionpermit | deny | approval_requiredFilter by decision outcome
hitl_resultapproved | rejected | timeoutFilter by HITL resolution
start_dateISO 8601 datetimeLower bound on created_at
end_dateISO 8601 datetimeUpper bound on created_at
pagenumberPage index (default 1)
limitnumberRecords 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"
}
FieldTypeRequiredDescription
decisionapproved | rejectedYesThe human’s decision
responded_bystringYesIdentifier 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)"
}
FieldTypeRequiredDescription
decision_idstring (UUID)YesID of the authorization decision (from a previous POST /allow/evaluate response)
resultstringYesExecution outcome: success, failure, or error
detailsstringNoOptional details about the execution outcome (max 2000 chars)
Response:
{
  "updated": true
}
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"
}
FieldTypeDescription
no_coverage_defaultapprove | deny | askWhat happens when no rule matches in enforce mode
autopilot_enabledbooleanWhether AI auto-creates draft rules for uncovered scenarios
hitl_timeout_secondsnumberHITL wait timeout in seconds (30–86400)
notification_channelsarrayConfigured 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