/rules/*, with the single evaluation call at /evaluate. The base URL is https://api.visiqlabs.com.
One rules collection spans every operation. Each rule declares an operations[] array (its stored applies_to) drawn from action, retrieval, and delegation, and the engine evaluates one event against the operations it carries.
The per-facet management endpoints —
/allow/rules (action) and /recall/rules (retrieval) — are additive, permanent compatibility aliases over the same rules table, not deprecated surfaces. A rule created here appears there and vice versa; issued keys keep their existing grants. Use whichever surface fits your integration.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. On this surface a harness key covers exactly
GET /rules/bundleandPOST /evaluate; calling the rule-management endpoints (GET/POST /rules,GET/PUT/DELETE /rules/:id) 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.
allow_rules:view / :create / :update / :delete), so a key or role that already manages action rules manages unified rules unchanged. Evaluation is scope-gated: POST /evaluate accepts the operation-native rules:evaluate or the legacy allow:write / recall:write; full_access satisfies everything.
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
The list endpoint returns the standard envelope:page (default 1) and limit (default 50, max 100) query parameters.
Evaluation
POST /evaluate
The single SDK-facing evaluation call for every governed event. The request declares which operations the event performs, and the response carries the union decision vocabulary — so one contract spans every facet. This is a pure projection onto the same plane handlers the per-facet evaluate endpoints use: there is exactly one decision implementation. Scope:rules:evaluate, allow:write, or recall:write
Request body (operation-native form):
A
delegation operation projects onto the action facet (it therefore requires target_app and action), which runs the delegation-facet policy check — the orchestrate.delegate authority transfer. This is the stateless policy leg; the grant/token lifecycle lives on the delegation endpoints.A legacy
kind-discriminated form is still accepted and behaves identically: { "kind": "action", ... } or { "kind": "retrieval", ... }. The operations[] form is canonical.
The union decision vocabulary:
Hybrid events. When
operations includes both action and retrieval, both facets are evaluated and the response carries a per-operation results[] plus an overall decision equal to the most restrictive facet outcome (fail-closed combine):
200 OK, 400 Bad Request (invalid body), 401 Unauthorized, 403 Forbidden (insufficient scope), 429 Too Many Requests, 500 Internal Server Error
GET /rules/bundle
Fetch the compiled unified rule bundle for local evaluation — every enabled rule whoseapplies_to overlaps the action ∪ retrieval facets, plus the agent’s mode and attributes. This is the bundle the tagged SDK runtime pulls; it caches it and revalidates with If-None-Match so decisions on the hot path never wait on the network.
Permission: allow_rules:view (a harness key passes the audience gate here)
Query parameters:
Request headers:
Response:
A shut-down agent (operator kill-switch) receives a terminal bundle — enforce mode, zero rules, all-deny no-coverage — so an SDK that honours the
shutdown flag stops immediately, and one that ignores it still denies everything.ETag (quoted SHA-256), Cache-Control: private, max-age=60
Status codes: 200 OK, 304 Not Modified, 400 Bad Request (missing/invalid agent_id), 401 Unauthorized, 404 Not Found (unknown agent after provisioning), 500 Internal Server Error
Rules
Management endpoints — a harness key receives403 harness_key_not_permitted here. One collection over every operation; the applies_to array on each rule decides which facets it governs.
GET /rules
List rules, priority-descending, optionally filtered to a facet. Permission:allow_rules:view
Query parameters: page (default 1), limit (default 50, max 100), operations (optional CSV of action,retrieval,delegation — returns only rules whose applies_to overlaps the set)
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 /rules
Create a rule. Theoperations[] array is stored as the rule’s applies_to.
Permission: allow_rules:create
Request body:
Response (201): the created rule object, including
rego_source.
Status codes: 201 Created, 400 Bad Request (invalid JSON or body), 401 Unauthorized, 409 Conflict (a rule with this name already exists), 500 Internal Server Error
GET /rules/:id
Get a single rule by UUID, includingrego_source.
Permission: allow_rules:view
Status codes: 200 OK, 400 Bad Request (invalid rule ID), 401 Unauthorized, 404 Not Found, 500 Internal Server Error
PUT /rules/:id
Update a rule. Every field fromPOST /rules is accepted and optional; at least one is required (an empty body returns 400 No fields to update). Only provided fields change; supplying operations rewrites applies_to.
Permission: allow_rules:update
Response: the updated rule object.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 409 Conflict (duplicate name), 500 Internal Server Error
DELETE /rules/:id
Delete a rule permanently. Permission:allow_rules:delete
Response: { "deleted": true }
Status codes: 200 OK, 400 Bad Request (invalid rule ID), 401 Unauthorized, 404 Not Found, 500 Internal Server Error
Errors & conventions
Every endpoint on this page follows the platform-wide REST conventions — the validation-error body shape, the/v1/ vs unversioned split, and the API stability policy. See REST API conventions.