Skip to main content
Every VisIQ REST endpoint shares the same conventions for errors, versioning, and pagination. This page is the single reference the per-facet API references point back to. The base URL for all of them is https://api.visiqlabs.com.

Validation errors

A request that fails schema validation returns 400 Bad Request with a machine-readable body. The error field is always a short string; a details field carries the underlying diagnostics.
The error string names what failed to parse:
The details payload has one of three concrete shapes depending on the endpoint — all are derived from the same underlying validator, so all pinpoint the offending field(s):
  • Flattened (the unified POST /evaluate and the /orchestrate/* endpoints): { "formErrors": [...], "fieldErrors": { "field": ["message"] } }.
  • Raw issues (the unified /rules CRUD): an array of issue objects, each { "code": "...", "path": ["field"], "message": "..." }.
  • Mapped (the /record/* endpoints): an array of { "field": "path.to.field", "message": "..." }.
Path-parameter errors (Invalid rule ID, etc.) and Invalid JSON body carry no details.
Every write endpoint validates its entire body before touching the database (input is validated with a strict schema first), so a 400 means nothing was persisted.

Versioned vs unversioned paths

Two path conventions coexist, and which one an endpoint uses is deliberate.

/v1/* — the versioned read API

Cross-cutting and query endpoints live under a /v1 prefix: the decision audit-log reads (/v1/allow/audit-log, /v1/recall/audit-log, /v1/record/audit-log), the cognition reads (/v1/allow/cognition/sessions, /v1/allow/cognition/events), and decision polling (/v1/allow/decisions/:id). The prefix marks the stable, URL-versioned public surface. Other /v1 routes serve the dashboard or the partner program and are not part of the API-key surface.

Unversioned — the governance planes

The governance surfaces are mounted at the root without a version segment: /allow/*, /recall/*, /orchestrate/*, /rules/*, /evaluate, and /record/*. They are kept unversioned for product isolation, and their evolution is governed by the SDK wire contract below, not by a URL version.
Practically: a versioned read endpoint keeps its /v1 path stable and adds fields additively; a governance-plane endpoint evolves under the bundle-dialect contract. The OEM Partner API is a third, separate surface with its own dated version header and a 12-month deprecation window — see Versioning & deprecation.

List pagination

List endpoints accept page (default 1) and limit query parameters and return a paginated envelope. The default and maximum limit, and the name of the page-size field in the response, vary by surface: Both fields hold the same value — the effective page size — under a different key. total is an accurate server-side count over the (vendor-scoped) query.

Error codes

Every JSON error response carries a stable, machine-readable code next to the human-readable error string. Branch on code; show error to people. The field is additive, so existing clients that read only error are unaffected.
When error is already a snake_case identifier (for example rate_limited or insufficient_scope), code repeats it. Otherwise code is the default for the HTTP status: Any other status uses error as its code. An endpoint that sets its own code keeps it.

Idempotent writes

The write endpoints below accept an optional idempotency key, so a client can retry a request after a timeout or a dropped connection without creating a second rule, agent or approval response. Send the key in the Idempotency-Key header (the legacy X-Idempotency-Key header is also read). A request without a key behaves exactly as it always has. How a key behaves:
  • Format. 1 to 256 characters: letters, digits, - and _. A UUID v4 is a good choice. An invalid key returns 400, and so does sending both headers with different values.
  • Scope. A key belongs to the credential that sent it (the API key, or the signed-in user) within your organization, and it lives for 24 hours. Another key never receives your stored response.
  • Replay. Repeating a request with the same key, method, path, query string and body within 24 hours returns the stored status and body with the header X-Idempotent-Replayed: true, and the operation does not run again. The replay also carries the original Location, Content-Location, ETag, Link, Deprecation and Sunset headers; it never carries cookies.
  • Mismatch. Reusing a key with a different method, path, query string or body returns 422.
  • In flight. If the first request is still running, a repeat waits briefly and then returns 409. Retry after a short delay. A request that has held its key for more than 2 minutes without finishing is treated as abandoned, and the next request with that key runs instead.
  • Failures are not stored. A 5xx or 429 response releases the key, so a retry with the same key runs the request again. Other 4xx responses are stored and replayed.
  • Store outage. If the idempotency store is unavailable, a request that carries a key is refused with 503 and Retry-After, rather than running without replay protection.
Every idempotency error carries one of these codes in code:
The evaluation, rule-bundle and telemetry endpoints (/evaluate, /allow/evaluate, /recall/evaluate, /rules/bundle and the SDK ingest routes) do not take part. Evaluation is already safe to repeat, and those paths must keep serving a running agent even when the idempotency store is down.

Authentication & rate limits

All endpoints authenticate with a Bearer credential and are rate-limited per key (default 600 requests / 60 seconds). Credentials come in two audiences — operational harness keys confined to the SDK routes, and management keys governed by permission grants. The full model, error codes, and the permission ↔ endpoint matrix live in Managing API Keys.

Stability policy

The platform is pre-1.0 — there is no frozen wire-compatibility guarantee yet, and the SDK surface may change across a minor release until v1.0 GA. Two properties hold regardless of version, because a governance product cannot be casual about them:
1

Additive-only, readers ship first

The control plane never emits a bundle construct — a new outcome verb, field, or operation facet — that the reading SDK cannot already enforce. The SDK that understands a construct is published before the plane emits it. Write your clients as tolerant readers: ignore response fields you don’t recognize rather than failing on them.
2

Fail closed on the unrecognized

An SDK treats any construct it does not recognize as must-understand and resolves it to the fail-closed outcome for a confirmed-enforcing agent — never fail open. A version skew can degrade coverage but never safety.
For the full posture — the pre-1.0 stance, the invariants, and the compatibility window that takes effect at v1.0 — see SDK versioning & compatibility. For the separately-versioned partner surface, see OEM Partner API versioning.