> ## 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.

# REST API conventions

> Conventions shared across every VisIQ REST endpoint — the validation-error body, the /v1 vs unversioned path split, list pagination, and the API stability policy.

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.

```json theme={null}
{
  "error": "Invalid request body",
  "details": { "...": "..." }
}
```

The `error` string names what failed to parse:

| `error`                                                                 | When                                                          |
| ----------------------------------------------------------------------- | ------------------------------------------------------------- |
| `Invalid request body`                                                  | A request body failed schema validation                       |
| `Invalid query parameters`                                              | A query string failed schema validation                       |
| `Invalid JSON body`                                                     | The body was not parseable JSON at all                        |
| `Invalid rule ID` / `Invalid record ID` / `Invalid checkpoint sequence` | A path parameter was not the expected shape (e.g. not a UUID) |

<Note>
  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`.
</Note>

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.

<CardGroup cols={2}>
  <Card title="/v1/* — the versioned read API" icon="hashtag">
    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`), decision polling (`/v1/allow/decisions/:id`), and resources like credentials, events, and notifications. The prefix marks the stable, URL-versioned public surface.
  </Card>

  <Card title="Unversioned — the governance planes" icon="shield-halved">
    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.
  </Card>
</CardGroup>

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](/partners/oem-versioning).

***

## 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:

| Surface                                    | Max `limit` | Response envelope                 |
| ------------------------------------------ | ----------- | --------------------------------- |
| Action, retrieval, unified rules           | `100`       | `{ data, total, page, pageSize }` |
| Record (records, sub-resources, audit log) | `200`       | `{ data, total, page, limit }`    |

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.

***

## 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](/automation/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:

<Steps>
  <Step title="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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

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](/versioning). For the separately-versioned partner surface, see [OEM Partner API versioning](/partners/oem-versioning).
