Telemetry
sdk.telemetry gives your app visibility into three streams of observability data:
| Sub-namespace | What it tracks | Backing store |
|---|---|---|
sdk.telemetry.audit | Who did what and when — tamper-evident event log | MongoDB + WORM archive |
sdk.telemetry.traces | LLM / AI call instrumentation — model, tokens, latency, cost | ClickHouse |
sdk.telemetry.events | Custom app analytics events | ClickHouse |
Audit Logs
Auto-instrumented. cPod automatically emits audit events for every EDM mutation (create, update, delete on People, Assets, Licenses, etc.) and every platform service invocation (skill runs, workflow triggers, job submissions). You only need sdk.telemetry.audit.emit() for custom business events not covered by built-in actions — approvals, policy exceptions, manual reviews, exports.
Every audit event is tamper-evident: each record includes a previousHash and a recordHash (HMAC-SHA-256), forming a per-tenant chain. Once written, events cannot be modified or deleted through the API.
Emit a custom audit event
import { CpodClient } from '@cpod/sdk'
const sdk = CpodClient.fromEnv()
await sdk.telemetry.audit.emit({
action: 'export.approved',
resourceId: 'per-abc123',
resourceType: 'person',
outcome: 'success',
metadata: { approvedBy: 'manager@acme.com', reason: 'GDPR request' },
})Query audit events
const events = await sdk.telemetry.audit.query({
resourceType: 'person',
action: 'person.deleted',
from: '2026-01-01T00:00:00Z',
limit: 50,
})
for (const event of events.items) {
console.log(event.actorId, event.action, event.ts)
}
// Paginate
if (events.nextCursor) {
const page2 = await sdk.telemetry.audit.query({ cursor: events.nextCursor, limit: 50 })
}AuditEvent fields
| Field | Type | Description |
|---|---|---|
id | string | aud- prefixed ID |
sequenceId | integer | Monotonic per-tenant sequence number |
tenantId | string | Owning tenant |
appId | string | App slug that originated the action |
action | string | Dot-separated action name (e.g. person.created, export.approved) |
actorType | string | user, service, system, or impersonator |
actorId | string | User or service account that triggered the event |
impersonatedUserId | string? | Set when an admin acts on behalf of another user |
resourceType | string | EDM domain type (e.g. person, license) |
resourceId | string | ID of the affected entity |
outcome | string | success, error, or denied |
reason | string? | Denial reason or error message |
before | object? | Snapshot of entity state before mutation (auto-populated for EDM ops) |
after | object? | Snapshot of entity state after mutation (auto-populated for EDM ops) |
changes | object? | Computed diff of changed fields |
metadata | object? | Custom key-value context |
ip | string? | Source IP of the originating request |
requestId | string? | Distributed trace correlation ID |
previousHash | string | SHA-256 of the prior event in the tenant chain |
recordHash | string | HMAC-SHA-256 of this record (tamper-detection) |
wormRef | string? | MinIO Object Lock URL once archived |
ts | string | ISO 8601 datetime |
Audit events are immutable and cannot be deleted via the API. Retention is governed by your subscription tier — 1 year on Pro, 7 years on Enterprise. Contact support for compliance-driven bulk export.
LLM Traces
Track every AI/LLM call your app makes — model, provider, token usage (including cache hits), latency, and cost. Traces support distributed span parenting so you can reconstruct multi-step agent runs.
Traces are automatically emitted by the cPod Skills runtime for every AI-backed skill execution. Manual instrumentation is only needed if your code calls an LLM directly outside the Skills system.
List traces
const traces = await sdk.telemetry.traces.list({
model: 'anthropic/claude-opus-4-7',
from: '2026-01-01T00:00:00Z',
limit: 20,
})
for (const trace of traces.items) {
const totalTokens = trace.inputTokens + trace.outputTokens
console.log(trace.model, totalTokens, `${trace.latencyMs}ms`, `$${trace.costUsd}`)
}Trace fields
| Field | Type | Description |
|---|---|---|
id | string | trc- prefixed ID |
tenantId | string | Owning tenant |
appId | string | Source app slug |
parentSpanId | string? | Distributed-tracing parent span |
agentRunId | string? | Agent run this trace belongs to |
userId | string? | Initiating user (null for system calls) |
model | string | Model identifier (e.g. anthropic/claude-opus-4-7) |
provider | string | anthropic, openai, azure, etc. |
spanKind | string | chat, embedding, image, audio, tool_call, or evaluation |
inputTokens | integer | Prompt token count |
outputTokens | integer | Completion token count |
cacheReadTokens | integer | Tokens served from cache |
cacheWriteTokens | integer | Tokens written to cache |
latencyMs | integer | End-to-end latency in milliseconds |
cacheHit | boolean | Whether the response was served from cache |
costUsd | number | Estimated cost in USD |
finishReason | string? | stop, length, tool_calls, or error |
securityViolations | object[]? | PII/DLP hits caught by the policy engine |
ts | string | ISO 8601 datetime |
App Events
Emit custom analytics events for user interactions, feature usage, and business milestones. All events land in ClickHouse and are immediately queryable.
Emit an event
await sdk.telemetry.events.emit('report.exported', {
format: 'pdf',
recordCount: 1500,
triggeredBy: 'scheduled-job',
})Query events
const events = await sdk.telemetry.events.query({
eventType: 'report.exported',
from: '2026-01-01T00:00:00Z',
limit: 100,
})
for (const event of events.items) {
console.log(event.eventType, event.userId, event.properties)
}AppEvent fields
| Field | Type | Description |
|---|---|---|
id | string | evt- prefixed ID |
tenantId | string | Owning tenant |
appId | string | Source app slug |
userId | string? | Initiating user (null for anonymous events) |
sessionId | string? | Browser session identifier |
eventType | string | Event name (e.g. report.exported, page_view) |
eventCategory | string? | navigation, interaction, mutation, or business_event |
entityType | string? | Target entity type if applicable |
entityId | string? | Target entity ID |
properties | object | Free-form event-specific properties |
pageUrl | string? | URL where the event occurred |
geo | object? | { country, region, city } |
device | object? | { type, os, browser } |
ts | string | ISO 8601 datetime |