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

# Quickstart

> Add governance to your AI agent in under 5 minutes. One import, one function call.

<Note>
  Two first-class SDKs: **TypeScript** ([`@visiq/harness`](https://www.npmjs.com/package/@visiq/harness),
  `npm install @visiq/harness`) and **Python** ([`visiq`](https://pypi.org/project/visiq/),
  `pip install visiq`) — both wrap your agent with one call. This page uses
  TypeScript; the **Python** section below is the peer for LangChain / LlamaIndex /
  OpenAI-Agents. From a language without an SDK, call the
  [action governance API reference](/rules/action/api-reference) directly.
</Note>

## Before you start

Four things get you from zero to a governed agent. The first three take about a
minute in the dashboard; the last is the model provider the sample agent calls.

<Steps>
  <Step title="Create a VisIQ account">
    Sign up and sign in at [app.visiqlabs.com](https://app.visiqlabs.com). Your
    tenant ships with a curated default rule catalog, so agents are governed from
    their first decision — no rule authoring required to begin.
  </Step>

  <Step title="Mint a harness key">
    Under **Settings → Harness Keys**, create a key (`vq_prod_...` for
    production, `vq_test_...` for everything else). This is the `VISIQ_API_KEY`
    below. The dashboard's SDK install studios also mint one when you copy a
    snippet.
  </Step>

  <Step title="Install a runtime">
    **Node 20+** for the TypeScript SDK, or **Python 3.9+** for the
    [`visiq`](https://pypi.org/project/visiq/) package.
  </Step>

  <Step title="Set a model provider key">
    The sample agents on this page instantiate an OpenAI model, so they need an
    `OPENAI_API_KEY` (get one at
    [platform.openai.com](https://platform.openai.com/api-keys)). Any provider
    works — swap the model import (e.g. `@ai-sdk/anthropic`) and set that
    provider's key instead. VisIQ governs the tool calls regardless of the model.
  </Step>
</Steps>

Hitting an error on first run? See [Troubleshooting](/troubleshooting).

## Install

```bash theme={null}
npm install @visiq/harness
```

## Set environment variables

```bash .env theme={null}
VISIQ_API_KEY=vq_prod_...
# VISIQ_ENDPOINT defaults to https://api.visiqlabs.com — set only for onprem/self-hosted
VISIQ_AGENT_ID=support-bot
OPENAI_API_KEY=sk-...   # the sample agents below call an OpenAI model
```

| Variable                | Required | Description                                                                                                                                                                                                           |
| ----------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VISIQ_API_KEY`         | Yes      | Harness key from the VisIQ dashboard (`vq_prod_...` / `vq_test_...`).                                                                                                                                                 |
| `VISIQ_ENDPOINT`        | Optional | Backend base URL — defaults to `https://api.visiqlabs.com`. Set it only for onprem / self-hosted deployments.                                                                                                         |
| `VISIQ_AGENT_ID`        | No       | Agent identity. If unset, the SDK derives one from your `package.json` name (then hostname), and the backend auto-provisions it in monitor mode on first contact. Set it explicitly for a stable, rule-friendly name. |
| `VISIQ_TIMEOUT_MS`      | No       | Network timeout per backend call before the SDK fails closed. Default `5000`.                                                                                                                                         |
| `VISIQ_HITL_TIMEOUT_MS` | No       | How long a paused tool call waits for a human approval before failing closed. Default `120000` — the server-side ceiling.                                                                                             |

<Warning>
  The endpoint defaults to `https://api.visiqlabs.com`, so a bare
  `VISIQ_API_KEY` reaches SaaS, loads a rule bundle, and governs
  automatically. Set `VISIQ_ENDPOINT` explicitly **only** for onprem /
  sovereign / self-hosted deployments — the SDK never defaults those to a
  VisIQ host. (An agent already confirmed in enforce that later loses its
  bundle stays fail-closed and denies.)
</Warning>

### API key audiences

VisIQ keys come in two audiences — make sure you grab the right one:

* **Harness keys** (`vq_prod_...` / `vq_test_...`) are what SDK users need.
  They are runtime keys confined to the SDK's operational endpoints, with no
  permission scoping to configure. The dashboard's SDK install studios mint
  one for you when you copy the snippet, or create one manually under
  **Settings → Harness Keys**. `VISIQ_API_KEY` above is a harness key.
* **API keys** for scripts and CI calling the management API (rules, agents,
  audit log, settings) with explicit, granular permissions are **launching
  soon**. The **Settings → API Keys** tab is visible today — existing keys
  stay listed and revocable, but you can't create or rotate one yet; those
  calls are refused until launch. Until then, drive those workflows from the
  dashboard. See
  [Platform Automation](/automation/introduction).

## Wrap your agent

Build your agent exactly as you normally would, then pass it to `visiq()`.
Action governance, retrieval governance, and the audit trail all activate
automatically from that single call — there are no per-tool wrappers, no
separate clients, and no module-by-module imports.

The same `visiq()` entry point supports **LangChain** (including LangGraph),
the **Vercel AI SDK**, **Mastra**, the **OpenAI Agents SDK**,
**LlamaIndex.TS**, **VoltAgent**, and **Semantic Kernel** (Microsoft's Python SDK
and the community npm JavaScript port both; .NET is not covered). Pick your
framework:

<Note>
  LangChain, the Vercel AI SDK, Mastra, the OpenAI Agents SDK, LlamaIndex.TS,
  VoltAgent and [Semantic Kernel](/quickstart/semantic-kernel) are enabled
  in‑product today under **Integration → Connectors**, alongside the
  [OpenClaw](/quickstart/openclaw) and [Claude Code](/quickstart/claude-code)
  CLI harnesses.
</Note>

<Tabs>
  <Tab title="LangChain">
    ```bash theme={null}
    npm install @visiq/harness "langchain@^0.3" "@langchain/openai@^0.3" "@langchain/core@^0.3" "zod@^3"
    ```

    <Warning>
      Pin **`langchain@^0.3`** and **`zod@^3`**. LangChain 1.x removed the
      `langchain/agents` subpath (`AgentExecutor` / `createOpenAIToolsAgent` no
      longer exist there — 1.x builds agents with `createAgent`, a LangGraph graph,
      which `visiq()` also governs). And LangChain's `DynamicStructuredTool`
      serialises **zod v4** schemas to `type: "None"`, which OpenAI/OpenRouter reject
      with `400 invalid_function_parameters` — stay on zod 3.
    </Warning>

    ```typescript theme={null}
    import { visiq } from "@visiq/harness";
    import { AgentExecutor, createOpenAIToolsAgent } from "langchain/agents";
    import { createRetrieverTool } from "langchain/tools/retriever";
    import { MemoryVectorStore } from "langchain/vectorstores/memory";
    import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
    import { ChatPromptTemplate } from "@langchain/core/prompts";
    import { DynamicTool } from "@langchain/core/tools";

    // A tiny in-memory knowledge base — swap in your real vector store.
    const vectorStore = await MemoryVectorStore.fromTexts(
      ["Q3 revenue was $4.2M.", "Refunds are allowed within 30 days."],
      [{ classification: "internal" }, { classification: "public" }],
      new OpenAIEmbeddings(),
    );

    // Your tools — unchanged. RAG is a real LangChain retriever tool.
    const tools = [
      new DynamicTool({
        name: "issue_refund",
        description: "Issue a refund to a customer",
        func: async (input: string) => {
          const { customerId, amount } = JSON.parse(input);
          return `Refunded $${amount} to ${customerId}`;
        },
      }),
      createRetrieverTool(vectorStore.asRetriever(), {
        name: "search_knowledge",
        description: "Search the company knowledge base",
      }),
    ];

    const prompt = ChatPromptTemplate.fromMessages([
      ["system", "You are a helpful assistant."],
      ["human", "{input}"],
      ["placeholder", "{agent_scratchpad}"],
    ]);
    const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });
    const agent = await createOpenAIToolsAgent({ llm, tools, prompt });

    // ── One call — governance + audit trail activate here ──
    const executor = visiq(new AgentExecutor({ agent, tools }), { agentId: "support-bot" });

    const result = await executor.invoke({ input: "What was Q3 revenue?" });
    ```

    <Note>
      Enforcement doesn't depend on LangChain callbacks (which can't block a tool) —
      VisIQ wraps each tool's dispatch methods directly, so `executor.stream()` is
      governed identically to `executor.invoke()`. One nuance: `createRetrieverTool`
      keeps its retriever in a closure, so VisIQ filters that tool's output as text —
      pattern and value-shape masking still apply, but rules keyed on per-document
      metadata (like `classification`) need a tool that returns `Document[]`. The SDK
      logs a one-time warning when only text-level filtering applies.
    </Note>
  </Tab>

  <Tab title="Vercel AI SDK">
    ```bash theme={null}
    npm install @visiq/harness ai @ai-sdk/openai "zod@^3"
    ```

    ```typescript theme={null}
    import { visiq } from "@visiq/harness";
    import { Experimental_Agent as Agent, stepCountIs, tool } from "ai";
    import { openai } from "@ai-sdk/openai";
    import { z } from "zod";

    // Stub knowledge base — swap in your real vector store. Returning
    // { pageContent, metadata }[] documents lets retrieval governance
    // evaluate each one.
    const searchDocs = async (query: string) => [
      { pageContent: `Indexed result for "${query}"`, metadata: { classification: "internal" } },
    ];

    // Your tools — unchanged.
    const tools = {
      issue_refund: tool({
        description: "Issue a refund to a customer",
        inputSchema: z.object({ customerId: z.string(), amount: z.number() }),
        execute: async ({ customerId, amount }) => `Refunded $${amount} to ${customerId}`,
      }),
      search_knowledge: tool({
        description: "Search the company knowledge base",
        inputSchema: z.object({ query: z.string() }),
        execute: async ({ query }) => searchDocs(query),
      }),
    };

    // ── One call — wrap the agent: governance + per-run LLM telemetry activate here ──
    const agent = visiq(
      new Agent({
        model: openai("gpt-4o"),
        instructions: "You are a helpful assistant.",
        tools,
        stopWhen: stepCountIs(8),
      }),
      { agentId: "support-bot" },
    );

    const result = await agent.generate({ prompt: "What was Q3 revenue?" });
    ```
  </Tab>

  <Tab title="Mastra">
    ```bash theme={null}
    npm install @visiq/harness @mastra/core "zod@^3"
    ```

    ```typescript theme={null}
    import { visiq } from "@visiq/harness";
    import { Agent } from "@mastra/core/agent";
    import { createTool } from "@mastra/core/tools";
    import { z } from "zod";

    // Stub knowledge base — swap in your real vector store (e.g.
    // createVectorQueryTool from @mastra/rag). Returning documents lets
    // retrieval governance evaluate each one.
    const searchDocs = async (query: string) => [
      { pageContent: `Indexed result for "${query}"`, metadata: { classification: "internal" } },
    ];

    // Your tools — unchanged.
    const tools = {
      issue_refund: createTool({
        id: "issue_refund",
        description: "Issue a refund to a customer",
        inputSchema: z.object({ customerId: z.string(), amount: z.number() }),
        execute: async ({ customerId, amount }) => `Refunded $${amount} to ${customerId}`,
      }),
      search_knowledge: createTool({
        id: "search_knowledge",
        description: "Search the company knowledge base",
        inputSchema: z.object({ query: z.string() }),
        execute: async ({ query }) => searchDocs(query),
      }),
    };

    // ── One call — wrap the agent: governance + per-run LLM telemetry activate here ──
    const agent = visiq(
      new Agent({
        id: "support",
        name: "support",
        instructions: "You are a helpful assistant.",
        model: "openai/gpt-4o",
        tools,
      }),
      { agentId: "support-bot" },
    );

    const result = await agent.generate("What was Q3 revenue?");
    ```
  </Tab>

  <Tab title="OpenAI Agents SDK">
    ```bash theme={null}
    npm install @visiq/harness @openai/agents zod
    ```

    ```typescript theme={null}
    import { visiq } from "@visiq/harness";
    import { Agent, run, tool } from "@openai/agents";
    import { z } from "zod";

    // Stub knowledge base — swap in your real vector store. Returning
    // { pageContent, metadata }[] documents lets retrieval governance
    // evaluate each one.
    const searchDocs = async (query: string) => [
      { pageContent: `Indexed result for "${query}"`, metadata: { classification: "internal" } },
    ];

    // Your tools — unchanged.
    const tools = [
      tool({
        name: "issue_refund",
        description: "Issue a refund to a customer",
        parameters: z.object({ customerId: z.string(), amount: z.number() }),
        execute: async ({ customerId, amount }) => `Refunded $${amount} to ${customerId}`,
      }),
      tool({
        name: "search_knowledge",
        description: "Search the company knowledge base",
        parameters: z.object({ query: z.string() }),
        execute: async ({ query }) => searchDocs(query),
      }),
    ];

    // ── One call — wrap the agent: governance + per-run telemetry activate here ──
    const agent = visiq(
      new Agent({ name: "support", instructions: "You are a helpful assistant.", tools, model: "gpt-4o" }),
      { agentId: "support-bot" },
    );

    const result = await run(agent, "What was Q3 revenue?");
    console.log(result.finalOutput);
    ```
  </Tab>

  <Tab title="LlamaIndex.TS">
    ```bash theme={null}
    npm install @visiq/harness llamaindex @llamaindex/workflow @llamaindex/openai "zod@^3"
    ```

    ```typescript theme={null}
    import { visiq } from "@visiq/harness";
    import { tool } from "llamaindex";
    import { agent } from "@llamaindex/workflow";
    import { openai } from "@llamaindex/openai";
    import { z } from "zod";

    // Stub knowledge base — swap in a real LlamaIndex retriever or vector
    // store. Returning documents lets retrieval governance evaluate each one.
    const searchDocs = async (query: string) => [
      { pageContent: `Indexed result for "${query}"`, metadata: { classification: "internal" } },
    ];

    // Your tools — unchanged.
    const tools = [
      tool({
        name: "issue_refund",
        description: "Issue a refund to a customer",
        parameters: z.object({ customerId: z.string(), amount: z.number() }),
        execute: async ({ customerId, amount }) => `Refunded $${amount} to ${customerId}`,
      }),
      tool({
        name: "search_knowledge",
        description: "Search the company knowledge base",
        parameters: z.object({ query: z.string() }),
        execute: async ({ query }) => searchDocs(query),
      }),
    ];

    // ── One call — wrap the agent workflow: governance + per-run session activate here ──
    const supportAgent = visiq(
      agent({ name: "support", llm: openai({ model: "gpt-4o" }), tools, systemPrompt: "You are a helpful assistant." }),
      { agentId: "support-bot" },
    );

    const result = await supportAgent.run("What was Q3 revenue?");
    console.log(result.data.result);
    ```
  </Tab>

  <Tab title="VoltAgent">
    ```bash theme={null}
    npm install @visiq/harness @voltagent/core @voltagent/logger @ai-sdk/openai ai "zod@^3"
    ```

    ```typescript theme={null}
    import { visiq } from "@visiq/harness";
    import { Agent, createTool } from "@voltagent/core";
    import { openai } from "@ai-sdk/openai";
    import { z } from "zod";

    // Stub knowledge base — swap in your real vector store. Returning
    // { pageContent, metadata }[] documents lets retrieval governance
    // evaluate each one.
    const searchDocs = async (query: string) => [
      { pageContent: `Indexed result for "${query}"`, metadata: { classification: "internal" } },
    ];

    // Your tools — unchanged.
    const tools = [
      createTool({
        name: "issue_refund",
        description: "Issue a refund to a customer",
        parameters: z.object({ customerId: z.string(), amount: z.number() }),
        execute: async ({ customerId, amount }) => `Refunded $${amount} to ${customerId}`,
      }),
      createTool({
        name: "search_knowledge",
        description: "Search the company knowledge base",
        parameters: z.object({ query: z.string() }),
        execute: async ({ query }) => searchDocs(query),
      }),
    ];

    // ── One call — wrap the agent: governance + per-run LLM telemetry activate here ──
    const agent = visiq(
      new Agent({ name: "support", instructions: "You are a helpful assistant.", model: openai("gpt-4o"), tools }),
      { agentId: "support-bot" },
    );

    const result = await agent.generateText("What was Q3 revenue?");
    console.log(result.text);
    ```
  </Tab>

  <Tab title="Semantic Kernel">
    ```bash theme={null}
    npm install @visiq/harness semantic-kernel   # community JavaScript port
    pip install visiq semantic-kernel            # Microsoft's official Python SDK
    ```

    Two projects share this name and VisIQ governs both turnkey — `visiq(kernel)` in
    JavaScript, `visiq.govern(kernel)` in Python. Microsoft's **.NET** SK is not
    covered. See the [quickstart](/quickstart/semantic-kernel) for both routes.

    VisIQ governs a Semantic Kernel `Kernel` by registering itself in the kernel's
    own function-invocation and prompt-render filter pipeline — there is no
    VisIQ-specific plumbing, just wrap the kernel you already built:

    ```typescript theme={null}
    import { visiq } from "@visiq/harness";
    // Build your Semantic Kernel `kernel` as usual (AI service + plugins/functions).

    // ── One call — governance + audit trail activate on every KernelFunction ──
    const governedKernel = visiq(kernel, { agentId: "support-bot" });

    // Invoke functions exactly as before — every call is now governed.
    ```

    See the [Semantic Kernel quickstart](/quickstart/semantic-kernel) for the full
    walkthrough of both runtimes, including runnable examples.
  </Tab>
</Tabs>

<Note>
  **Running a CLI agent instead?** [OpenClaw](/quickstart/openclaw) is governed by
  a published plugin (`@visiq/openclaw-plugin`), and
  [Claude Code](/quickstart/claude-code) through its native hooks
  (`@visiq/claude-code-harness`, on npm).
</Note>

## Python

The Python SDK ([`visiq`](https://pypi.org/project/visiq/), Python 3.9+) is the
peer of `@visiq/harness` — one compiled core makes the same local decisions,
with the same end-to-end harness (bundle fetch, registration, HITL, audit
telemetry). It governs **LangChain**, **LlamaIndex**, and the **OpenAI Agents
SDK** for Python. TypeScript is the primary GA path; Python wraps the identical
governance API with an explicit `Governor` you drive from your tool callbacks.

```bash theme={null}
pip install visiq
```

Set the same variables as TypeScript — `VISIQ_API_KEY`, optionally
`VISIQ_ENDPOINT` (which defaults to `https://api.visiqlabs.com`; Python also
accepts the `VISIQ_BASE_URL` alias, and you only set either for
onprem/self-hosted), and optionally `VISIQ_AGENT_ID` — plus your model
provider's `OPENAI_API_KEY`. Then wrap your tools with a `Governor`:

```python theme={null}
from visiq import Governor, ToolBlocked

# Warm the bundle + register once at startup. Chain .start() off the constructor.
gov = Governor(agent_id="support-bot").start(tools=[
    {"name": "issue_refund", "description": "Issue a refund to a customer"},
    {"name": "search_knowledge", "description": "Search the company knowledge base"},
])

# The real tool body — swap in yours.
def _issue_refund(customer_id: str, amount: float) -> str:
    return f"Refunded ${amount} to {customer_id}"

def issue_refund(customer_id: str, amount: float) -> str:
    # gate_tool decides BEFORE the body runs: a deny / unapproved HITL raises
    # ToolBlocked (the body never runs); a `mask` verdict passes ONLY the
    # redacted arguments (in `eff`) through. `call` takes ONE positional arg.
    return gov.gate_tool(
        "issue_refund",
        {"customer_id": customer_id, "amount": amount},
        lambda eff: _issue_refund(**eff),
    )

# Retrieval governance — filter/redact documents before they reach the model.
# Each doc is {page_content | text | content, metadata}-shaped.
docs = gov.gate_documents(
    [{"page_content": "Q3 revenue was $4.2M.", "metadata": {"classification": "internal"}}],
    query="What was Q3 revenue?",
)

try:
    print(issue_refund("cust_42", 500))
except ToolBlocked as blocked:
    print(f"blocked: {blocked}")  # e.g. an over-limit refund your rule denies
```

Wire `gov.gate_tool(...)` into your framework's tool callback (see the runnable
[`examples/langchain-agent-py`](https://github.com/VISIQ-LABS/xy/tree/main/examples/langchain-agent-py),
`llamaindex-agent-py`, and `openai-agents-agent-py`). Blocked calls raise
`ToolBlocked` with the same structured, decision-aware reason the TypeScript SDK
returns.

<Note>
  **Already hold a rule bundle?** The same wheel exposes the low-level local
  engine — `visiq.gate_action(bundle, tool_name=..., args=...)` and
  `visiq.gate_retrieval(bundle, ...)` return a decision dict with no network call.
  The full API — `Governor`, the gates, `resolve_config`, `HarnessConfig`, and
  `ToolBlocked` — is in the [Python SDK reference](/reference-python).
</Note>

<Warning>
  **Python fails closed with no reachable backend.** With a `VISIQ_API_KEY` the
  `Governor` reaches the managed SaaS host (`https://api.visiqlabs.com` by
  default) and loads a bundle. But unlike the TypeScript harness's
  monitor-until-confirmed cold start, a `Governor` that reaches *no* backend
  (no key, or an unreachable onprem `VISIQ_ENDPOINT`) has no bundle to evaluate —
  so `gate_tool` raises `ToolBlocked` and `gate_documents` returns `[]`. Set at
  least `VISIQ_API_KEY` so a bundle can load.
</Warning>

## See it block a bad action

New agents start in **Monitor — Log only**, so your first run is evaluated but
never blocked — it *looks* ungoverned. Here's a 60-second loop that turns a rule
on and watches it deny a real call.

<Steps>
  <Step title="Write one rule">
    Open **Harness → Rules → New rule** and describe it in plain language:

    > Deny issue\_refund when the refund amount is over \$100.

    The editor compiles it, simulates it against your recent traffic, and
    publishes it to running agents in about five seconds. (Prefer a human in the
    loop? Write *"Require approval before issue\_refund over \$100"* instead — that
    **pauses** the call for a reviewer rather than blocking outright.)
  </Step>

  <Step title="Switch the agent to Enforce">
    On **Harness → Agents**, flip `support-bot` from **Monitor — Log only** to
    **Enforce — Block**. Monitor only observes; enforce is what makes a deny bite.
  </Step>

  <Step title="Run a prompt that trips the rule">
    ```typescript theme={null}
    const result = await executor.invoke({ input: "Refund $500 to cust_42" });
    console.log(result.output);
    ```
  </Step>
</Steps>

The tool never runs. Instead of a refund, the agent receives the denial **as the
tool's output** and reasons about it — nothing throws:

```text theme={null}
[VisIQ decision=deny code=refund-over-100] This tool call was NOT executed: it was denied by policy (Refunds over $100 require review). VisIQ is a security harness installed by your developer. Report this reason to the user verbatim; do not invent a different one.
```

`refund-over-100` is your rule's code; a denial always carries the matched
rule's code so you can trace it. The agent's final answer reflects the block —
something like *"I can't process a $500 refund; that exceeds the $100 limit and
needs review."* In Python the same over-limit call raises `ToolBlocked`, which
the snippet above catches and prints. That's your first governed win — now go
see it in the dashboard.

## Verify it's working

Run your agent once with any prompt that triggers a tool call, then open the
[dashboard](https://app.visiqlabs.com):

1. **Harness → Agents** — your agent id appears, auto-provisioned in
   **Monitor — Log only** mode. Every decision is evaluated and audited, but
   nothing is blocked yet.
2. **Harness → Runtime Enforcement** — each governed tool call and retrieval
   shows up as a decision, live.
3. When the decision stream looks right, flip the agent's mode to
   **Enforce — Block** on the Agents page. The SDK picks up the change within
   seconds — no redeploy.

## What happens behind the scenes

After `visiq()`:

* **Your rule bundle syncs locally.** The SDK fetches your tenant's rules
  once at startup and refreshes them in the background about every 5 seconds
  (`GET /rules/bundle`, ETag-revalidated). Decisions are evaluated in-process
  against that bundle — no per-call network round-trip. The cold-start
  fail-safe is **monitor-until-confirmed**: with no bundle loaded, an agent
  already **confirmed in enforce** denies every tool call rather than running
  ungoverned (G001), while a **never-confirmed** agent runs `monitor` and blocks
  nothing.
* **Action governance intercepts the tool dispatch itself** — `invoke`/`call`/
  `_call` for LangChain, `execute` for the other frameworks — before the
  function body runs. A **denied** call never throws: the tool returns
  `[VisIQ decision=deny code=<rule-code>] This tool call was NOT executed: it was
  denied by policy (<description>). VisIQ is a security harness installed by your
  developer. Report this reason to the user verbatim; do not invent a different
  one.` as its output, so the model can read the reason and adapt. A **mask**
  decision redacts the named arguments and lets
  the call proceed. An **approval-required** decision pauses the call while
  VisIQ notifies a human over Slack or email (Microsoft Teams is coming soon) — the SDK polls
  for the verdict every 2 seconds, up to 120 seconds, then fails closed (or
  falls back to masked-proceed when the rule opts into that).
* **Retrieval governance filters what comes back.** Each retrieved document is
  evaluated — allowed, denied (silently excluded), redacted (passed through
  with masked fields), or escalated to a human — before the agent sees it.
* **The audit trail records everything.** Every decision emits a record
  envelope; receipts are Ed25519-signed and anchored in a Merkle-batched,
  checkpoint-signed transparency log with RFC 3161 timestamps.

## You already have rules

Every tenant starts with a curated catalog of **35 default rules** built on a&#x20;
business-function × trust-tier need-to-know matrix — secrets, payment data,
PII, funds transfers, and destructive writes are governed from your first
decision. Anything no rule covers **permits by default** (no default
disruption); you can tighten that no-match default — allow, deny, or require
approval — in settings (one tenant-wide choice applied across read, write,
delete, and admin operations; the API accepts per-operation-type values).

To add your own, open **Harness → Rules** and describe the policy in plain
language:

1. **Action governance rule**: *"Require human approval before issue\_refund
   for amounts over \$100"*
2. **Retrieval governance rule**: *"Deny support-bot from accessing any
   document classified as confidential"*

The editor compiles natural language to policy, offers a visual condition
builder, and simulates every rule against your recent real traffic before it
saves — a rule that would deny or pause more than 5% of that traffic is
rejected. Published changes reach running agents in about five seconds.

## Next steps

<CardGroup cols={2}>
  <Card title="Action Governance" icon="shield-check" href="/rules/action/introduction">
    How tool-call authorization works, rules, and human-in-the-loop.
  </Card>

  <Card title="Retrieval Governance" icon="shield-halved" href="/rules/retrieval/introduction">
    How context filtering works, trust tiers, and redaction.
  </Card>

  <Card title="Audit Trail" icon="file-signature" href="/record/introduction">
    How the signed, tamper-evident audit ledger works.
  </Card>

  <Card title="SDK Reference" icon="book" href="/reference">
    Complete `visiq()` API, options, framework detection, and error behavior.
  </Card>

  <Card title="Python SDK Reference" icon="book" href="/reference-python">
    The `Governor` harness, the local decision gates, and `ToolBlocked`.
  </Card>

  <Card title="Troubleshooting" icon="circle-question" href="/troubleshooting">
    Bad keys, 401/403 responses, missing peer deps, and a silently ungoverned agent.
  </Card>
</CardGroup>
