On this page
SDK telemetry event schema
This page is the binding contract for telemetry in the Rust, Python, and TypeScript SDKs. All three SDKs capture the same PostHog event names with the same properties so cross-SDK funnel analysis — init → first API call → first order placed → first order settled — works without drift. Every SDK implementer lands here first; no SDK ships telemetry that disagrees with this page.
Current schema_version: 2.
SDK-side capture POSTs directly to PostHog and bypasses the Rust gateway
capture() allow-list. SDK authors must construct property bags that stay
within the per-event allow-list below. distinct_id, trace_id, and
timestamp are required identity fields on every event and are not listed
in the per-event tables.
SDK onboarding funnel events
Each of the four funnel events fires once per install (once per
distinct_id). SDKs track whether a step has been sent and never re-emit it.
Every event also carries the shared identity fields.
All four events are a no-op when telemetry is disabled or no PostHog project
key is configured.
sdk.initialized
Fires the first time the SDK client is constructed after installation.
| Property | Type | Required | Description |
|---|---|---|---|
language | string (rust | typescript | python) | required | Emitting SDK. |
version | string | required | SDK package version. |
environment | string (prod | dev) | required | Coarse run context. |
user_id | string | required | Carries the distinct_id value (never an email or db id). |
sdk.first_api_call
Fires on the first completed HTTP call after initialization, regardless of success.
| Property | Type | Required | Description |
|---|---|---|---|
latency_ms | integer | required | Request round-trip in milliseconds. |
endpoint | string | required | Path only; query string stripped. |
response_status | integer | required | HTTP status code. |
sdk.first_order_placed
Fires the first time the buyer submits a non-streaming
POST /v1/chat/completions. The streaming path does not emit this event.
| Property | Type | Required | Description |
|---|---|---|---|
success | boolean | required | Whether placement succeeded. |
model_tier | string | required | Requested model / tier. |
order_size | integer | required | Requested token quantity (max_tokens, or 0 when unset). |
sdk.first_order_settled
Fires the first time that first order is observed as settled — the first
successful (2xx) non-streaming POST /v1/chat/completions response. The
gateway only returns the completion after the match is fulfilled and settlement
has been spawned. The streaming path does not emit this event.
| Property | Type | Required | Description |
|---|---|---|---|
end_to_end_latency_ms | integer | required | Placement → settlement observed by the SDK, in ms. |
settlement_status | string | required | "success" or "failed". |
API call pattern event
api_call
Fires on every SDK HTTP call after opt-in, success or failure. Powers SDK API coverage and health/usage analysis.
| Property | Type | Required | Description | Rationale |
|---|---|---|---|---|
endpoint | string | required | Canonicalised path template, not a concrete URL. | Groups calls by route without leaking ids or query data. |
method | string | required | HTTP method (POST, GET, DELETE). | Distinguishes operations on the same path. |
response_time_ms | integer | required | Send-to-response wall-clock time in ms. | Feeds latency/health analysis. |
success | boolean | required | true when status_code is 2xx. | One-field funnel/health filter. |
status_code | integer | required | HTTP status returned. | Distinguishes failure modes (408 vs 502 vs 429). |
What endpoint means: the canonicalised HTTP method target — a path
template, never user data. Path parameters are replaced with placeholders and
query strings are dropped. Canonical templates:
| Template | Methods |
|---|---|
/v1/chat/completions | POST |
/v1/sellers/register | POST |
/v1/sellers/{id}/asks | POST |
/v1/sellers/{id}/asks/{ask_id} | DELETE |
/v1/sellers/{id}/capacity | POST |
/v1/sellers/{id}/trades | GET |
Shared property schema
Present on every sdk.* funnel event. The orthogonal api_call pattern
event may also include these plus its own keys.
| Property | Wire type | Required | Description / redaction |
|---|---|---|---|
distinct_id | string | required | Stable per-developer install-correlation id. See opt-in and identity. Contains no secret. |
trace_id | string | required | Fresh UUID generated per emission so the four funnel steps can be correlated. |
timestamp | string (ISO-8601 / RFC 3339 UTC) | required | When the event occurred, e.g. 2026-08-21T12:00:00Z. |
No API keys, request bodies, response bodies, or query parameters are ever included in any property.
PostHog config convention
All three SDKs read PostHog configuration from the same environment variables
and expose the same client-config field, so telemetry setup is identical across
languages. Names are namespaced under TOKEN_GATEWAY_ to avoid colliding with a
developer's own PostHog environment variables.
| Setting | Canonical env var | Default |
|---|---|---|
| PostHog project API key | TOKEN_GATEWAY_POSTHOG_API_KEY | none — no events sent if unset |
| PostHog host | TOKEN_GATEWAY_POSTHOG_HOST | https://app.posthog.com |
Per-SDK client-config field for the project API key: Rust posthog_api_key
(snake_case), Python posthog_api_key (snake_case kwarg), TypeScript
posthogApiKey (camelCase). The project API key must be configurable, never
hardcoded; a config field, when provided, takes precedence over the env var.
Opt-in default and disclosure
Sourced from the ADR docs/decisions/sdk-telemetry-opt-in.md in the repo (see
also the working decision record):
- Default: opt-in by default — telemetry is off unless explicitly enabled.
- Enable via the
telemetry_enabledconfig flag (TypeScript:telemetryEnabled; defaultfalse) or theTOKEN_GATEWAY_TELEMETRYenv var. The env var wins when both are set. - Identity:
distinct_idis a locally-generated persisted UUIDv4 stored at<config_dir>/token-gateway/telemetry-id, overridable viaTOKEN_GATEWAY_TELEMETRY_DISTINCT_ID, with a per-process ephemeral UUID fallback when the config dir is unavailable. Never derived from a secret.
Disclosure copy template
Each SDK README pastes this verbatim (swap the enable snippet for the language):
## Telemetry
This SDK can send anonymous usage telemetry (onboarding milestones and API call
patterns) to help improve the AI Token Gateway. **Telemetry is off by default.**
No API keys, request bodies, or response bodies are ever collected.
To enable it, set the telemetry flag when constructing the client, or set the
`TOKEN_GATEWAY_TELEMETRY=1` environment variable. See the
[SDK telemetry event schema](/docs/sdk/telemetry) for the full list of events and
properties.use token_gateway_sdk::{BuyerClient, BuyerKey, TelemetryConfig};
let client = BuyerClient::new("http://127.0.0.1:8080", BuyerKey("sk-buyer".into()))
.with_telemetry(TelemetryConfig::from_env().with_enabled(true));from token_gateway import BuyerAuth, BuyerTokenGateway, TelemetryConfig
gw = BuyerTokenGateway(
"http://127.0.0.1:8080",
buyer=BuyerAuth("sk-buyer"),
telemetry=TelemetryConfig(enabled=True),
)import { BuyerClient } from "@ai-token-gateway/sdk";
const buyer = new BuyerClient({
baseUrl: "http://127.0.0.1:8080",
apiKey: "sk-buyer",
telemetry: { enabled: true },
});Change control
When the schema changes:
- Bump the
schema_versionat the top of this page and theschema_versionproperty value SDKs emit. - Update all three SDKs to emit the new version and any changed properties in the same release train, so no SDK emits a mismatched schema.
- Update this page and re-request reviewer sign-off below.
Additive, backward-compatible property additions may keep the same
schema_version at the reviewers' discretion; renames or removals must bump it.
Reviewer sign-off
This contract must be reviewed and approved before any per-SDK telemetry implementation task begins ("reviewed before implementation begins" gate).
| Reviewer role | Name | Approved (Y/N) | Date |
|---|---|---|---|
| Rust SDK owner | |||
| Python SDK owner | |||
| TypeScript SDK owner | |||
| Platform lead |