# auth.md

You are an agent. This document tells you how to authenticate to **VisIQ** so you
can call the VisIQ governance API on behalf of the human who runs you. There are
two ways to get a credential: **(A)** a human provisions an API key for you in the
dashboard (Step 1), or **(B)** you **self-register** through the `agent_auth`
device-flow and a human approves you (Step 1b). Read top to bottom.

> Honest scope note: VisIQ issues **human-provisioned bearer API keys**, and
> **issuance always requires a human to approve** — there is no anonymous or
> instant credential, and no privilege is granted without approval. VisIQ does not
> operate a full OAuth authorization server for the automation API (there is no
> `/oauth2/token` JWT-bearer exchange); the `agent_auth` flow below issues an API
> key **directly** after the human-approved claim. Discovery metadata lives at
> `/.well-known/oauth-authorization-server` (the `agent_auth` block) and
> `/.well-known/oauth-protected-resource`. User sign-in to the web app is OIDC via
> Supabase; see `/.well-known/openid-configuration`.

## Audience — who this is for

This applies to AI agents and automation that call the **VisIQ backend
automation API**:

- **API base URL (resource):** `https://api.visiqlabs.com`
- **What it does:** action-call governance (ALLOW), context/retrieval governance
  (RECALL), and the signed audit trail (RECORD). Most agents reach it indirectly
  through the VisIQ SDK (`visiq()`), which wraps your agent and calls this API
  for you.
- **Human dashboard (not for agents):** `https://app.visiqlabs.com`
- **Developer docs:** `https://docs.visiqlabs.com`

If you are an end-user agent simply governing your own tool calls, you normally
do not call this API directly — install the SDK and let it carry the credential.
The steps below are for agents that call the API directly or that need to know
how the credential is obtained.

## Step 1 — Provisioning (how the credential is created)

VisIQ uses **human-provisioned API keys**, not machine self-registration. A
person with access to a VisIQ account must create the key:

1. Sign in to the dashboard: `https://app.visiqlabs.com` (email/password or
   Google sign-in).
2. Go to **Connectors → API Keys**: `https://app.visiqlabs.com/connectors/api-keys`
3. Create a key, choosing the audience (see Step 2). The full key value is shown
   **once** at creation time — copy it then; it is stored only as a hash and
   cannot be retrieved again.

If you already have a key this way, skip to Step 2. If you do not, you can either
ask your operator to create one (above) or **self-register** (Step 1b). Either
way, a usable key is only ever issued after a human approves — there is no
endpoint that mints a usable credential without human approval.

## Step 1b — Self-registration via `agent_auth` (device flow)

Instead of a human creating the key up front, you can register yourself and have
a human approve you. This is the flow advertised by the `agent_auth` block of
`/.well-known/oauth-authorization-server`. Issuance still requires human approval.

1. **Register** — `POST https://app.visiqlabs.com/api/agent/identity`

   ```json
   { "type": "anonymous", "agent_name": "my-agent" }
   ```

   or assert a verified email:

   ```json
   { "type": "identity_assertion", "assertion_type": "verified_email",
     "assertion": "you@example.com", "agent_name": "my-agent" }
   ```

   By default this requests a **harness** key (least privilege — see Step 2).
   To request the **full product surface** instead, ask for a management key,
   optionally with an explicit permission list:

   ```json
   { "type": "anonymous", "agent_name": "my-agent",
     "requested_key_type": "management",
     "requested_permissions": ["allow_rules:view", "allow_rules:create",
                               "allow_agents:view", "allow_audit_log:view"] }
   ```

   Omitting `requested_permissions` requests the default product grant (rules,
   agents, audit/outcomes, action schemas, HITL responses, settings — but never
   account administration: a device-flow key cannot manage API keys, the team,
   or billing). Your operator sees exactly what you requested, may narrow it,
   and can never grant beyond their own permissions.

   The response is a device-flow ceremony:

   ```json
   {
     "registration_id": "...",
     "claim_token": "vqac_...",
     "claim": {
       "user_code": "WXYZ-2345",
       "verification_uri": "https://app.visiqlabs.com/agent/claim",
       "verification_uri_complete": "https://app.visiqlabs.com/agent/claim?user_code=WXYZ-2345",
       "expires_in": 1800,
       "interval": 5
     }
   }
   ```

2. **Have your operator approve you.** Prefer showing them
   `verification_uri_complete` — a single clickable link with the code already
   embedded (RFC 8628 one-click); the code survives the sign-in/MFA detour and
   the approval page auto-loads your pending registration. Fall back to the bare
   `verification_uri` plus the typed `user_code` if a one-click link is not
   usable. They sign in to VisIQ, open the URL, review the requested key
   audience and permissions, and approve — which mints a governed API key bound
   to their account (harness by default; management if you requested it and they
   agreed).

3. **Poll for your credential** — `POST https://app.visiqlabs.com/api/agent/identity/claim`
   every `interval` seconds with `{ "claim_token": "vqac_..." }`. While the human
   has not approved yet you get `400 { "error": "authorization_pending" }`. Once
   approved you get the credential **exactly once**:

   ```json
   { "token_type": "api_key", "api_key": "vq_prod_...",
     "agent_id": "...", "api_base_url": "https://api.visiqlabs.com",
     "key_type": "harness", "permissions": null }
   ```

   `key_type` tells you which audience was actually granted; for a management
   key, `permissions` is the exact grant list your credential holds.

   Store `api_key` securely (it is shown once) and use it as a Bearer token
   (Step 3).

4. **Revoke** — `POST https://app.visiqlabs.com/api/agent/identity/revoke` with
   `{ "token": "vq_prod_..." }`, or `{ "claim_token": "vqac_..." }`. Either input
   fully revokes the credential: `claim_token` cancels a still-pending
   registration AND revokes an already-issued key (the key stops authenticating
   immediately), not just the pending ticket. Your operator can also revoke it in
   the dashboard under API Keys.

Claim tickets expire after 30 minutes; if the ceremony lapses, register again.

## Step 2 — Pick a method (key audience)

VisIQ issues two kinds of keys. Choose based on what the agent needs to do:

1. **Harness key** — for an SDK-wrapped agent governing its own actions. It is
   confined to the harness operational route allowlist (evaluate ALLOW/RECALL,
   poll its own decisions, emit RECORD). It is **not** a management credential
   and cannot create or edit rules, agents, or other keys. Prefer this for
   agents — it is the least-privilege option.
2. **Management key** — the full-product credential. It drives everything a
   signed-in user can do within its permission grant, on BOTH API surfaces:
   the automation API (`https://api.visiqlabs.com` — rules CRUD, agents CRUD,
   ALLOW/RECALL/RECORD audit logs, HITL queue + respond, settings, delegation
   grants) and the web app's JSON API (`https://app.visiqlabs.com/api/*` —
   the same features plus dashboard reads and action-schema views). Obtain one
   from your operator (dashboard → Settings → Automation Keys) or by
   self-registering with `"requested_key_type": "management"` (Step 1b).
   Management keys are permission-scoped: the key carries an explicit RBAC
   grant list (e.g. `allow_rules:create`, `allow_hitl:respond`) enforced
   exact-match and fail-closed on every route.

Supported capability scopes (a key holds a subset; `full_access` grants all):

- `allow:read` — read ALLOW decisions / audit log
- `allow:write` — evaluate actions + manage ALLOW rules/agents
- `recall:read` — read RECALL decisions / audit log / rule bundle
- `recall:write` — evaluate retrievals + manage RECALL rules
- `record:read` — read RECORD records / audit trail
- `record:write` — ingest RECORD envelopes
- `full_access` — every scope (server-to-server master key)

## Step 3 — Use the credential

Send the API key as a **Bearer token** on every request to the API base URL.
VisIQ enforces this on every request (fail-closed); there is no anonymous access
to the governance API.

```http
POST /allow/evaluate HTTP/1.1
Host: api.visiqlabs.com
Authorization: Bearer <YOUR_VISIQ_API_KEY>
Content-Type: application/json
```

If you use the VisIQ SDK, set these and the SDK attaches the header for you:

```bash
VISIQ_API_KEY=vq_prod_...            # the key from Step 1 (shown once)
VISIQ_ENDPOINT=https://api.visiqlabs.com
VISIQ_AGENT_ID=my-agent              # the identity this agent governs under
```

A missing or malformed `Authorization` header is rejected with `401`.

## Step 4 — Revocation

You can revoke your **own** key — no human and no special permission required.
Broader key management (rotating, or revoking a *different* key) stays with the
operator.

### Revoke your own key (self-revocation)

A valid key may **always** revoke the exact key it authenticated with. No
permission is needed, and no other key can be targeted: the credential is
resolved from your own bearer token, so there is no request input that could
name someone else's key. Use this to cleanly end your own session, or to retire
a credential you believe is compromised.

- **Automation API** (works for a harness *or* a management key):

  ```http
  POST /allow/self/revoke HTTP/1.1
  Host: api.visiqlabs.com
  Authorization: Bearer <your-key>
  ```

  On success: `200 { "revoked": true, "api_key_id": "...", "key_type": "harness" | "management" }`.

- **Web JSON API** (same effect, management-plane surface):
  `POST https://app.visiqlabs.com/api/allow/self/revoke`, same `Authorization` header.

- **Via MCP:** call the `revoke_self` tool (no arguments) on the Platform MCP
  server.

It is **irreversible** and **idempotent**: after it succeeds the key stops
authenticating immediately, so it must be your **final** call with that key. A
key that is already revoked, unknown, or expired returns `401`.

### Cancel or revoke a device-flow credential

To tear down a credential you obtained through the `agent_auth` flow (Step 1b) —
including cancelling a registration that has not been approved yet — call
`POST https://app.visiqlabs.com/api/agent/identity/revoke` with
`{ "token": "vq_prod_..." }` (an issued key) or `{ "claim_token": "vqac_..." }`
(the pending ticket, which also revokes an already-issued key).

### Operator-side rotation & revocation

Your operator can rotate or revoke **any** key from the dashboard at
`https://app.visiqlabs.com/connectors/api-keys`. A revoked or rotated-out key
stops authenticating immediately; requests then return `401`. If your calls
begin failing with `401`, stop and ask the operator for a fresh key — do not
retry the dead credential in a loop.

## Notes for agents

- Treat the API key as a secret. Never log it, embed it in client-side code, or
  send it to any host other than `https://api.visiqlabs.com` and
  `https://app.visiqlabs.com`.
- Request the least privilege you need: prefer a harness key when you only
  govern your own tool calls; request a management key (and only the
  permissions you need) when you must operate the product — view/author rules,
  register/manage agents, read decision outcomes and audit logs, inspect action
  schemas, respond to HITL approvals, or tune settings.
- Machine-readable metadata: `/.well-known/oauth-authorization-server`
  (`agent_auth` block, including `key_types_supported`) and
  `/.well-known/api-catalog`.
- **Prefer MCP?** With a management key you can operate VisIQ through the
  authenticated Platform MCP server at
  `https://app.visiqlabs.com/api/mcp` (Streamable HTTP) instead of raw REST —
  pass the key as `Authorization: Bearer <key>` and call tools like
  `list_rules`, `create_rule`, `register_agent`, `get_decision_outcomes`,
  `list_action_schemas`, and `respond_hitl`. Start with the `whoami` tool to
  see your key's permissions, and when you are done you can retire the key with
  the `revoke_self` tool (same self-revocation as above). Both MCP servers are
  listed at `/.well-known/mcp/server-cards.json`.

_Last reviewed: 2026-07-14_
