> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usetrident.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript SDK API Reference (@vouch-ai/sdk)

> Complete API reference for the @vouch-ai/sdk package: trident.init(), trident.scan(), trident.selfReport(), and configuration options.

The `@vouch-ai/sdk` package exports a single `trident` object with three methods — `init()`, `scan()`, and `selfReport()` — plus a zero-code register module at `@vouch-ai/sdk/register`. This page documents every parameter, return value, and supported environment variable.

***

## `trident.init(options?)`

```typescript theme={null}
import { trident } from "@vouch-ai/sdk";

trident.init(options?: VouchInitOptions): void
```

Initialises Trident tracing for the current Node.js process. Call this **once**, before you create any LLM client, at the top of your application entry point. Repeat calls are silently ignored (the function is idempotent).

`init()` lazy-loads the OpenLLMetry instrumentation layer, patches all supported LLM clients already in memory, and begins shipping OTel spans to your Trident project. When `redactPII` is enabled (the default), it also installs a span processor that scrubs PII from span attributes before the HTTP export request leaves your process.

### Parameters

<ParamField body="projectPk" type="string" optional>
  Project public key. Falls back to the `TRIDENT_PROJECT_PUBLIC_KEY` or `VOUCH_PROJECT_PUBLIC_KEY` environment variable. **Required** — either pass it here or set the env var.
</ParamField>

<ParamField body="projectSk" type="string" optional>
  Project secret key. Falls back to `TRIDENT_PROJECT_SECRET_KEY` or `VOUCH_PROJECT_SECRET_KEY`. **Required** — either pass it here or set the env var.
</ParamField>

<ParamField body="agentId" type="string" optional>
  A logical identifier for this agent (e.g. `"prod-rag-bot"`). Appears in the Trident dashboard's connection panel and scopes all spans to a named agent. Falls back to `TRIDENT_AGENT_ID` / `VOUCH_AGENT_ID`.
</ParamField>

<ParamField body="agentUrl" type="string" optional>
  Public HTTP endpoint where this agent receives prompts (e.g. `"https://api.example.com/api/chat"`). When set, the SDK registers the URL with Trident so the Red Team page can auto-target your agent for pen-testing without you having to type an endpoint. Falls back to `TRIDENT_AGENT_URL`, then `VOUCH_AGENT_URL`.

  If neither the option nor the env var is set, the SDK attempts to sniff the URL from hosted-platform env vars (Vercel `VERCEL_URL`, Fly `FLY_APP_NAME`, Render, Railway, Heroku, Koyeb, Cloudflare Workers, AWS App Runner, etc.) and then falls back to observing `http.Server.prototype.listen` combined with `PUBLIC_URL` / `BASE_URL`. Disable this behaviour entirely with `sniffAgentUrl: false`.
</ParamField>

<ParamField body="agentPath" type="string" optional>
  Path the agent's HTTP handler is mounted at (e.g. `"/api/chat"`). Used when constructing a full URL from a sniffed hostname. Defaults to `"/"`. Falls back to `TRIDENT_AGENT_PATH` / `VOUCH_AGENT_PATH`.
</ParamField>

<ParamField body="sniffAgentUrl" type="boolean" default="true" optional>
  Set to `false` to disable the `http.Server.prototype.listen` hook and all platform-env sniffing. The SDK will only register a URL when `agentUrl` or `TRIDENT_AGENT_URL` / `VOUCH_AGENT_URL` is explicitly provided.
</ParamField>

<ParamField body="endpoint" type="string" default="https://app.tryvouch.ai" optional>
  Override the Trident host. Useful for on-premise deployments. Falls back to `TRIDENT_ENDPOINT` / `VOUCH_ENDPOINT`.
</ParamField>

<ParamField body="appName" type="string" optional>
  OTel resource `service.name`. Defaults to `agentId`, then `"vouch-app"`.
</ParamField>

<ParamField body="disableBatch" type="boolean" default="false" optional>
  Emit each span immediately via `SimpleSpanProcessor` instead of batching with `BatchSpanProcessor`. Useful for short-lived CLI scripts where the process exits before the batch flushes.
</ParamField>

<ParamField body="redactPII" type="boolean | { rules?: PiiRule[] }" default="true" optional>
  Controls edge PII redaction. `true` uses the built-in rule set (emails, card numbers, SSNs, AWS keys, JWTs, API keys, IBANs, IPs, phone numbers). Pass `{ rules: [...] }` to supply a custom `PiiRule[]`. `false` disables redaction entirely and lets raw span attributes reach Trident.
</ParamField>

<ParamField body="traceloopOptions" type="Record<string, unknown>" optional>
  Extra options forwarded verbatim to the underlying `Traceloop.initialize()` call. Use this for advanced OpenLLMetry configuration not exposed by the Trident wrapper.
</ParamField>

### Return value

`void`. The function does not return a promise — instrumentation is synchronous.

### Example

```typescript theme={null}
import { trident } from "@vouch-ai/sdk";

trident.init({
  projectPk: process.env.TRIDENT_PROJECT_PUBLIC_KEY,
  projectSk: process.env.TRIDENT_PROJECT_SECRET_KEY,
  agentId: "prod-rag-bot",
  agentUrl: "https://api.example.com/api/chat",
  disableBatch: false,
  redactPII: true,
});
```

### Throws

Throws `Error` if both `projectPk` and `projectSk` are unresolvable (neither passed explicitly nor available in the environment).

***

## `trident.scan(input, options?)`

```typescript theme={null}
async function scan(
  input: ScanInput,
  options?: ScanOptions,
): Promise<ScanResult>
```

Submits untrusted text to Trident's server-side firewall and returns a pass/block verdict. Use `scan()` as the gate before any LLM call that carries input you don't fully control — user messages, scraped web pages, RAG document chunks, MCP tool outputs.

The server applies two checks in order: the project's per-tenant guardrail bank (auto-populated from confirmed findings), then the upstream LLM-Guard firewall. The full round-trip has an 8-second timeout, matching the server's own firewall timeout.

### Parameters

<ParamField body="input.prompt" type="string" required>
  The untrusted text to evaluate. Maximum 8 KB — the server rejects larger payloads.
</ParamField>

<ParamField body="input.agentId" type="string" optional>
  Optional agent identifier to attach to any finding the firewall later generates. Inherits the value from `init()` if omitted.
</ParamField>

<ParamField body="options.endpoint" type="string" optional>
  Override the Trident base URL for this call. Defaults to the value passed to `init()`.
</ParamField>

<ParamField body="options.projectPk" type="string" optional>
  Override the project public key for this call.
</ParamField>

<ParamField body="options.projectSk" type="string" optional>
  Override the project secret key for this call.
</ParamField>

### Return value

Returns `Promise<ScanResult>`, which is a discriminated union:

**On success (`ok: true`):**

<ResponseField name="ok" type="true">
  Indicates the HTTP request succeeded and a verdict was returned.
</ResponseField>

<ResponseField name="is_valid" type="boolean">
  `true` means the prompt passed all checks and is safe to forward to your LLM. `false` means the prompt should be blocked.
</ResponseField>

<ResponseField name="scanners" type="Record<string, unknown>">
  Per-scanner verdicts from the LLM-Guard firewall, present when no tenant rule fired. The shape varies by scanner (prompt injection score, toxicity score, etc.).
</ResponseField>

<ResponseField name="source" type="string">
  Which subsystem produced the verdict: `"trident.tenantRule"` when a project-level deny-list rule matched, or `"trident.firewall"` when the result came from the upstream firewall.
</ResponseField>

<ResponseField name="matched_rule" type="object" optional>
  Present only when `source` is `"trident.tenantRule"`. Contains `id`, `label`, `kind`, `scope` (`"project"` | `"org"`), `snippet`, and `severity`.
</ResponseField>

<ResponseField name="latencyMs" type="number">
  Server-reported latency for the firewall evaluation in milliseconds.
</ResponseField>

**On failure (`ok: false`):**

<ResponseField name="ok" type="false" />

<ResponseField name="error" type="string">
  Human-readable error message (HTTP status + body excerpt, or network error text).
</ResponseField>

### Example

```typescript theme={null}
import { trident } from "@vouch-ai/sdk";

trident.init({ agentId: "support-bot" });

async function handleUserMessage(userMessage: string) {
  const verdict = await trident.scan({ prompt: userMessage });

  if (!verdict.ok) {
    console.error("Scan failed:", verdict.error);
    // Decide whether to fail open or closed based on your risk posture.
    return "Sorry, I can't process your request right now.";
  }

  if (!verdict.is_valid) {
    console.warn("Prompt blocked by", verdict.source, verdict.matched_rule);
    return "I'm unable to help with that.";
  }

  // Safe to forward to the LLM.
  const response = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: [{ role: "user", content: userMessage }],
  });
  return response.choices[0].message.content;
}
```

***

## `trident.selfReport(input, options?)`

```typescript theme={null}
async function selfReport(
  input: SelfReportInput,
  options?: SelfReportOptions,
): Promise<{ ok: true; findingId: string } | { ok: false; error: string }>
```

Lets your agent report its own anomalies to Trident as first-class findings. Your agent has more context about what went wrong than any external monitor — `selfReport()` ships that observation to Trident with a `source: SELF_REPORT` tag so it routes through the same Slack, GitHub, Jira, and webhook channels as automatically detected findings.

Fire-and-forget by default: the function returns immediately and does not block the agent's request path on a network round-trip to Trident. Pass `await: true` in the input if you need to confirm delivery.

### Parameters (`SelfReportInput`)

<ParamField body="kind" type="string" required>
  Short kebab-case category. Validated server-side as `^[a-z][a-z0-9-]*$`. Surfaces as the finding category on the Findings page and is the primary field for grouping.

  Well-known kinds Trident's Sentinel agent understands:

  * `tool-call-failure` — a tool returned an error or timed out
  * `silent-tool-failure` — a tool returned empty/nil and the agent continued
  * `refusal-spike` — the agent refused a request it normally handles
  * `hallucinated-policy` — the agent cited a policy or fact that doesn't exist
  * `context-loss` — the agent forgot something stated earlier in the conversation
  * `infinite-loop` — the agent repeated the same action N times
  * `data-exfil-suspected` — outbound traffic to an unexpected destination
  * `prompt-injection` — a guardrail detected a prompt injection pattern
  * `lethal-trifecta` — untrusted input + sensitive data + exfiltration leg

  Use any other kebab-case slug for project-specific patterns.
</ParamField>

<ParamField body="message" type="string" required>
  Human-readable summary of the anomaly, up to 4,000 characters. Shown in the finding card and Slack alert.
</ParamField>

<ParamField body="agentId" type="string" optional>
  Which agent is reporting. Defaults to the value passed to `init()` or `TRIDENT_AGENT_ID`.
</ParamField>

<ParamField body="severity" type="&#x22;LOW&#x22; | &#x22;MEDIUM&#x22; | &#x22;HIGH&#x22; | &#x22;CRITICAL&#x22;" default="&#x22;MEDIUM&#x22;" optional>
  Finding severity. Defaults to `"MEDIUM"` — agents rarely have full context to assign `CRITICAL` unilaterally.
</ParamField>

<ParamField body="traceId" type="string" optional>
  OTel trace ID to anchor the finding to a specific request.
</ParamField>

<ParamField body="metadata" type="SelfReportMetadataConventions & Record<string, unknown>" optional>
  Structured context rendered inline in Trident's finding evidence panel. The following conventional keys receive rich treatment in the UI — use whichever apply:

  | Key                | Type               | Description                                                      |
  | ------------------ | ------------------ | ---------------------------------------------------------------- |
  | `input`            | `string`           | Triggering input text (user message, scraped page, tool result). |
  | `output`           | `string`           | The agent's response or the action it took.                      |
  | `tool`             | `string`           | Tool/function name in use when the issue occurred.               |
  | `errorCode`        | `number \| string` | HTTP or app-level status code.                                   |
  | `latencyMs`        | `number`           | Latency of the failing call in milliseconds.                     |
  | `consecutiveCount` | `number`           | How many times this happened in a row.                           |
  | `requestId`        | `string`           | Identifier of the user-facing request being served.              |
  | `userId`           | `string`           | Affected end-user ID. **Hash before sending.**                   |
  | `userImpact`       | `string`           | Plain-English description of the user-visible impact.            |
  | `affectedTraces`   | `string[]`         | Related trace IDs.                                               |
  | `recovered`        | `boolean`          | Whether the agent self-healed (retry succeeded, etc.).           |
  | `fallback`         | `string`           | Any fallback the agent took (e.g. switched to a backup model).   |

  Extra keys are stored verbatim.
</ParamField>

<ParamField body="await" type="boolean" default="false" optional>
  Block on the network round-trip and surface errors. When `false` (the default) the SDK fires the request and ignores the response so the agent's own request path cannot be slowed by a Trident call. The 5-second timeout applies in both modes.
</ParamField>

### Options (`SelfReportOptions`)

<ParamField body="endpoint" type="string" optional>
  Override the Trident base URL for this call.
</ParamField>

<ParamField body="projectPk" type="string" optional>
  Override the project public key for this call.
</ParamField>

<ParamField body="projectSk" type="string" optional>
  Override the project secret key for this call.
</ParamField>

### Return value

`Promise<{ ok: true; findingId: string } | { ok: false; error: string }>`.

On the fire-and-forget path (`await: false`) the promise resolves immediately with `{ ok: true, findingId: "" }` — the `findingId` is only populated when `await: true` and the server responded successfully.

### Examples

<Tabs>
  <Tab title="Fire-and-forget (default)">
    ```typescript theme={null}
    import { trident } from "@vouch-ai/sdk";

    // In your tool-call error handler:
    try {
      await callExternalApi();
    } catch (err) {
      // Don't await — let the agent's response continue immediately.
      void trident.selfReport({
        kind: "tool-call-failure",
        message: `createBooking returned 503 five times in a row`,
        severity: "HIGH",
        metadata: {
          tool: "createBooking",
          errorCode: 503,
          consecutiveCount: 5,
        },
      });

      return "I wasn't able to complete your booking. Please try again.";
    }
    ```
  </Tab>

  <Tab title="Await confirmation">
    ```typescript theme={null}
    const result = await trident.selfReport(
      {
        kind: "prompt-injection",
        message: "Guardrail caught injection pattern in scraped page",
        severity: "HIGH",
        traceId: currentSpan.spanContext().traceId,
        metadata: {
          input: scrapedContent.slice(0, 500),
          tool: "webScraper",
        },
        await: true, // block until Trident confirms receipt
      },
    );

    if (result.ok) {
      console.log("Finding created:", result.findingId);
    } else {
      console.error("selfReport failed:", result.error);
    }
    ```
  </Tab>
</Tabs>

***

## `@vouch-ai/sdk/register` — zero-code auto-init

The register module is a side-effect-only import that calls `init()` using credentials from environment variables. Use it to enable Trident traces with no source code changes.

```typescript theme={null}
// Not for direct use in source — load via Node's --import flag:
// node --import @vouch-ai/sdk/register dist/server.js
```

### How to use it

<Tabs>
  <Tab title="node --import">
    ```bash theme={null}
    node --import @vouch-ai/sdk/register dist/server.js
    ```
  </Tab>

  <Tab title="NODE_OPTIONS">
    ```bash theme={null}
    # Works with Next.js, Vercel, and any process spawner that
    # respects NODE_OPTIONS.
    NODE_OPTIONS="--import @vouch-ai/sdk/register" next start
    ```
  </Tab>
</Tabs>

### Environment variables read by the register module

| Variable                                | Description                           |
| --------------------------------------- | ------------------------------------- |
| `TRIDENT_PROJECT_PUBLIC_KEY`            | Project public key (**required**).    |
| `TRIDENT_PROJECT_SECRET_KEY`            | Project secret key (**required**).    |
| `TRIDENT_AGENT_ID`                      | Agent identifier (optional).          |
| `TRIDENT_ENDPOINT` / `TRIDENT_BASE_URL` | Override the Trident host (optional). |
| `VOUCH_PROJECT_PUBLIC_KEY`              | Back-compat alias for the public key. |
| `VOUCH_PROJECT_SECRET_KEY`              | Back-compat alias for the secret key. |
| `VOUCH_AGENT_ID`                        | Back-compat alias for the agent ID.   |
| `VOUCH_ENDPOINT` / `VOUCH_BASE_URL`     | Back-compat alias for the endpoint.   |

<Note>
  The register module is a **silent no-op** if `TRIDENT_PROJECT_PUBLIC_KEY` or `TRIDENT_PROJECT_SECRET_KEY` are missing. It never throws and never crashes your app. In non-production environments (`NODE_ENV !== "production"`) it prints a `console.warn` so you notice the missing configuration. Set `TRIDENT_REGISTER_DEBUG=1` to force the warning in production.
</Note>

***

## Environment variables

The SDK reads the following environment variables. Explicitly passed options always take precedence.

| Variable                     | Used by                                | Description                                                       |
| ---------------------------- | -------------------------------------- | ----------------------------------------------------------------- |
| `TRIDENT_PROJECT_PUBLIC_KEY` | `init`, `scan`, `selfReport`, register | Project public key. **Preferred name.**                           |
| `TRIDENT_PROJECT_SECRET_KEY` | `init`, `scan`, `selfReport`, register | Project secret key. **Preferred name.**                           |
| `VOUCH_PROJECT_PUBLIC_KEY`   | `init`, `scan`, `selfReport`, register | Back-compat alias.                                                |
| `VOUCH_PROJECT_SECRET_KEY`   | `init`, `scan`, `selfReport`, register | Back-compat alias.                                                |
| `TRIDENT_AGENT_ID`           | `init`, `selfReport`, register         | Logical agent identifier.                                         |
| `VOUCH_AGENT_ID`             | `init`, `selfReport`, register         | Back-compat alias.                                                |
| `TRIDENT_ENDPOINT`           | `init`, `scan`, `selfReport`, register | Override Trident host.                                            |
| `VOUCH_ENDPOINT`             | `init`, `scan`, `selfReport`, register | Back-compat alias.                                                |
| `TRIDENT_BASE_URL`           | register                               | Alias for endpoint in the register module.                        |
| `TRIDENT_AGENT_URL`          | `init`                                 | Explicit agent URL for Red Team registration. **Preferred name.** |
| `VOUCH_AGENT_URL`            | `init`                                 | Back-compat alias for agent URL.                                  |
| `TRIDENT_AGENT_PATH`         | `init`                                 | Path for assembled agent URLs.                                    |
| `VOUCH_AGENT_PATH`           | `init`                                 | Back-compat alias.                                                |
| `TRIDENT_REGISTER_DEBUG`     | register                               | Set to `"1"` to force verbose init logging in production.         |
| `PUBLIC_URL` / `BASE_URL`    | `init`                                 | Used when assembling a public URL from a sniffed listen port.     |
