# Record schema

Every record in Logs, in exports and in log streams has the same shape, versioned as `clitrail.telemetry.v1`. New fields may be added within a version; a field is never renamed or given another meaning without a new version.

## Every record

| Field | Type | Description |
| --- | --- | --- |
| `schema` | string | `clitrail.telemetry.v1`, on each line of NDJSON exports and log streams. |
| `seq` | number | Only in the service operator’s own stream: the record’s position in the service-wide log. Your organisation’s streams leave it out; identify records by `id`. |
| `id` | string | `tl_`, the time in base 36, `_` and 16 hex characters. |
| `time` | string | When it was recorded, ISO 8601 in UTC. |
| `kind` | string | `inbound`, `outbound`, `audit`, `error` or `alert`; `test` for a log stream’s test record. |
| `type` | string | What happened within the kind: `collection` or `stripe_webhook` for inbound; `delivery`, `destination_test`, `logstream` or `request` for outbound; the action or alert code otherwise. |
| `org` | string or null | The organisation ID. |
| `website` | string or null | The website ID, when the record concerns one. |

## Inbound

| Field | Description |
| --- | --- |
| `route`, `method` | Such as `/v1/events` and `POST`. |
| `status`, `error` | The response status, and its error code when there was one, such as `rate_limited` or `quota_exceeded`. |
| `body` | The request body as received, as text. Receipts and handoff tokens become labels such as `receipt …Ab3dE9 #<keyed hash>`; any other credential-like field becomes `[redacted]`. |
| `bodyBytes`, `truncated` | The body’s size; `truncated: true` when it was cut at 16 KB or refused as too large. |
| `client` | The browser family, OS and device worked out from the request. |
| `ids` | What the request created, such as `{ "visit": "…" }` or `{ "event": "…" }`. |
| `ipHash` | A keyed hash of the client address, never the address. |

## Outbound

| Field | Description |
| --- | --- |
| `service` | Who was called: `ga4`, `google_ads`, `google`, `meta`, `tiktok`, `x`, `webhook`, `stripe`, `firebase`, `s3` or `logstream`. |
| `destination`, `destinationType` | The destination or log stream ID, and its type. |
| `method`, `url` | The request line; credentials in the URL, such as an API secret, become `[redacted]`. |
| `headers`, `responseHeaders` | With `Authorization` kept only as its scheme (`OAuth [redacted]`), and tokens, signatures and keys as `[redacted]`. |
| `body`, `bodyBytes` | The exact request body, JSON or form fields, credentials redacted, up to 64 KB. |
| `status`, `responseBody`, `responseBytes` | The response status and body, up to 64 KB, redacted. |
| `truncated` | `{ "request": …, "response": … }` when either body was cut at 64 KB. |
| `durationMs`, `attempt`, `error` | How long it took, which attempt it was, and what failed. |
| `outboxId`, `eventIds` | The queued delivery and the install events it carried. |
| `batch`, `records` | For log-stream deliveries: the batch name and how many records it held. |

## Audit, error, alert and test

| Kind | Fields |
| --- | --- |
| `audit` | `actor` (who), `action` (such as `invite_created` or `limit_reached`), `target` and `detail`. Never a secret value. |
| `error` | `where` it happened and a short `message`. |
| `alert` | A `code`, such as `stream_failing` or `stream_backlog_dropped`, and its `detail`. |
| `test` | Written by a log stream’s **Send test**: `type` `logstream.test`, the `stream` ID and a `message`. |

## Example

```json
{"schema":"clitrail.telemetry.v1","id":"tl_test_20260924T091203Z","time":"2026-09-24T09:12:03.512Z","kind":"test","type":"logstream.test","org":"o_5b1e0c9d2a7f43e8b610","website":null,"stream":"ls_9c2d4e6f8a0b1c3d","message":"CLItrail log stream test record"}
```

In the Logs list, inbound and outbound records come without their bodies (`summary: true`); open a record to read them. On Free, attribution fields are left out of every record. CSV exports use the columns `id`, `time`, `kind`, `type`, `org`, `website`, `route`, `method`, `url`, `service`, `destination`, `status`, `error`, `duration_ms`, `attempt`, `event_ids`, `actor`, `target`, `detail`, `body` and `response_body`, with any cell a spreadsheet could read as a formula prefixed with `'`.
