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

# Telemetry and capture

> What the SDK reports to the console, what it never does, and how to turn bodies on.

## Telemetry

Telemetry is on by default. It carries **metrics**, not content: the decision, the severity, the rule id, the category, and character counts. It never carries payload text unless you turn on evidence.

Events are queued, flushed on an interval or when the batch fills, and sent with your API key as a bearer token. They sit off the request path. A flush failure goes to `onError` and is never raised, and the queue is bounded so a stalled network cannot grow memory.

<CodeGroup>
  ```ts TypeScript theme={null}
  const fw = new FrostWolfClient({
    apiKey,
    onError: (error) => logger.warn({ error }, "telemetry flush failed"),
  });

  // On shutdown, so the last batch is not lost.
  await fw.close();
  ```

  ```python Python theme={null}
  fw = FrostWolfClient(
      api_key=key,
      on_error=lambda error: logger.warning("telemetry flush failed: %s", error),
  )

  fw.close()
  ```
</CodeGroup>

Turn it off with `telemetry: false`. Detection is unaffected either way.

### What an event contains

| Field | Meaning |
| - | - |
| `type` | Which entry point produced it: `guard.scan`, `guard.sanitise`, `guard.wrap`, `guard.decorate`, `guard.tool_calls`, `guard.protect`, or `guard.cloak.restore`. |
| `decision`, `blocked`, `severity`, `reason`, `categories` | The verdict. |
| `signatureIds` | The rules that fired. |
| `scannedChars` | How much text was scanned. |
| `modelCheck` | The semantic pass's answer, when applicable. |
| `tool` | Counts and tool names for validated calls. Names are your own function names, not payload content. |
| `cloak` | What Cloak did, as counts. |
| `evidence` | Matched substrings. **Only when `includeEvidence` is on.** |

### Cloak telemetry never carries a value

The Cloak block reports counts by type, category, and strategy, whether the detector answered, and on a restore event how many tokens were restored, unknown, or refused. It never carries a value, a token, or an offset.

## Capture

Telemetry carries metrics only. Full request and response bodies travel on a separate stream, **off by default**, because bodies are your users' content.

```ts theme={null}
const fw = new FrostWolfClient({
  apiKey,
  capture: true, // or { request: true, response: true, maxBodyChars: 100_000 }
});
```

Capture is a flag on the key, server-side. Turn it on once at boot and the local pipeline starts with it:

```ts theme={null}
await fw.init({ capture: true });
```

A decorator created before that call starts capturing too, because the guard reads the setting at call time.

A capture record holds the base URL, the provider, the model, the request body, the response body (or the accumulated stream chunks), and the guard decision. Bodies are truncated to `maxBodyChars` before they are queued.

Capture can also be set per call, to ship bodies for one sensitive workflow without turning them on everywhere:

```ts theme={null}
const create = fw.guard.decorate(fn, { capture: { request: false } });
```

### Capture with Cloak

When a call uses [Cloak](/cloak/protect-and-restore), capture records the request **as it was forwarded**, with tokens in place of values, and the response **before** it was restored. A request that was refused is recorded only if its personal data can be redacted out of it, and its body is dropped otherwise. Losing the body is the smaller loss.

## Streams

A stream is teed, not buffered. Each chunk reaches your code before it is recorded, so capture adds no latency.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.