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

# POST /v1/telemetry

> Ship batched decision records to the console.

The SDKs batch decision records and send them here in the background. It carries metrics only: a decision, a severity, a rule id, character counts. No payload text.

## Request

<ParamField body="records" type="object[]" required>
  Up to 500 records.
</ParamField>

Each record:

| Field | Type | Meaning |
| - | - | - |
| `id` | string | Unique within your stream. |
| `ts` | string | ISO 8601 timestamp. |
| `type` | string | `guard.scan`, `guard.sanitise`, `guard.wrap`, `guard.decorate`, `guard.tool_calls`, `guard.protect`, or `guard.cloak.restore`. |
| `decision` | `"allow" \| "block"` | |
| `blocked` | boolean | |
| `severity` | string \| null | |
| `reason` | string \| null | |
| `categories` | string\[] | |
| `signatureIds` | string\[] | |
| `scannedChars` | integer | |
| `onMatch` | string | |
| `modelCheck` | object | The semantic pass's verdict, when applicable. |
| `tool` | object | `{ calls, findings, blocked, names, modelCheck? }`, when calls were validated. |
| `cloak` | object | What Cloak did. Counts only. See below. |
| `evidence` | string\[] | Matched substrings, only when evidence is enabled. |
| `sdk` | object | `{ name, version }` |

### The `cloak` block

```json theme={null}
{
  "ran": true,
  "entities": 3,
  "byType": { "email": 2, "payment_card": 1 },
  "byCategory": { "pii": 2, "pci": 1 },
  "byStrategy": { "tokenize": 3 },
  "error": null,
  "restored": 0,
  "unknown": 0,
  "denied": 0
}
```

Counts only. A value, a token, and an offset are all content, or close enough to it, so none is sent, and the control plane rebuilds the block from the fields it knows and discards anything else. A key it does not know is ignored, so a newer SDK does not fail against an older control plane.

## Response

```json theme={null}
{ "accepted": 50, "rejected": 0 }
```

Records are validated one by one. A record that does not parse is dropped and counted in `rejected`, so one bad field does not discard the rest of the batch. A body that is not `{ "records": [...] }` is a `400`.

## Idempotency

Records are keyed by `(workspace, id)`. A retried batch does not double-count.


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