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

## Install

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

## Set environment variables

```bash .env theme={null}
VISIQ_API_KEY=vq_prod_...
VISIQ_ENDPOINT=https://api.visiqlabs.com
VISIQ_AGENT_ID=support-bot
```

| Variable                | Required | Description                                                                                                                                                                                                           |
| ----------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VISIQ_API_KEY`         | Yes      | Harness key from the VisIQ dashboard (`vq_prod_...` / `vq_test_...`).                                                                                                                                                 |
| `VISIQ_ENDPOINT`        | Yes      | Backend base URL — `https://api.visiqlabs.com` for production. There is **no built-in default**.                                                                                                                      |
| `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>
  There is no default endpoint. If `VISIQ_ENDPOINT` is unset, the SDK never
  loads a rule bundle, so its mode is never confirmed and it cold-starts in
  `monitor` — nothing is blocked, and nothing is actually governed. Set it
  explicitly. (An agent already confirmed in enforce that 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**. Pick your framework:

<Note>
  All of these frameworks 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@^3"
    ```

    ```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
    ```

    VisIQ governs a Semantic Kernel `Kernel` through its built-in
    `functionInvocationFilters` / `promptRenderFilters` extension points — 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.
    ```
  </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/)) 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.

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

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

# Same env vars as TS: VISIQ_API_KEY (vq_prod_… / vq_test_…), VISIQ_ENDPOINT, VISIQ_AGENT_ID
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"},
])

def issue_refund(customer_id: str, amount: float) -> str:
    # `gate_tool` evaluates BEFORE the body runs: a deny / un-approved HITL
    # raises ToolBlocked (the body never runs); a `mask` verdict passes ONLY the
    # redacted arguments through to your function.
    return gov.gate_tool(
        "issue_refund", {"customer_id": customer_id, "amount": amount},
        lambda eff: _real_issue_refund(**eff),
    )

# Retrieval governance — filter/redact documents before they reach the model:
docs = gov.gate_documents(retrieved_docs, query=user_query)
```

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.

## 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 <rule-code>] Action blocked: <description>. (VisIQ is a security harness
  installed by your developer.)` 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, Microsoft Teams, or email — 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 **29 default rules** built on a
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>
</CardGroup>
