OpenPanel

Manual capture

Send AI events yourself with op.ai() or the raw payload, from any language.

Beta

AI observability is in beta; this payload may still change. Fields will be added rather than removed, and events you have already sent stay readable. See the overview for how to give feedback.

With the Node SDK

@openpanel/sdk 1.4 adds op.ai(payload). The sticky profile id from identify() and groups from setGroups() are merged in, like track().

import { OpenPanel } from '@openpanel/sdk';

const op = new OpenPanel({ clientId: 'xxx', clientSecret: 'xxx' });

op.ai({
  kind: 'generation',
  traceId,
  name: 'summarize',
  conversationId: thread.id,
  model: 'gpt-5-mini',
  provider: 'openai',
  input: messages,
  output: [{ role: 'assistant', content: completion }],
  usage: { inputTokens: 812, outputTokens: 140, cacheReadTokens: 100 },
  latencyMs: 1840,
  stopReason: 'stop',
  properties: { feature: 'inbox-summary', plan: 'pro' },
  profileId: user.id,
  groups: [user.orgId],
});

Raw payload

Any language can post the same payload to POST /track with the client headers used for every other event:

curl -X POST https://api.openpanel.dev/track \
  -H 'content-type: application/json' \
  -H 'openpanel-client-id: xxx' -H 'openpanel-client-secret: xxx' \
  -d '{
    "type": "ai",
    "payload": {
      "kind": "generation",
      "traceId": "trace_01",
      "model": "gpt-5-mini",
      "provider": "openai",
      "input": [{ "role": "user", "content": "Summarize my inbox" }],
      "output": [{ "role": "assistant", "content": "You have 3 unread emails." }],
      "usage": { "inputTokens": 812, "outputTokens": 140 },
      "latencyMs": 1840,
      "profileId": "usr_123"
    }
  }'

Kinds

kindRequired fieldsMeaning
generationtraceId, model, provider, usage.inputTokens, usage.outputTokensOne model call
embeddingtraceId, model, provider, usage.inputTokensOne embedding call
spantraceIdA tool call, a retrieval, any non-model step
tracetraceIdAn optional explicit root with a name, total latency, input and output state

A trace does not need an explicit trace event; the trace list aggregates from its children.

Fields

FieldTypeNotes
traceIdstringGroups every event of one request. Letters, digits and - _ ~ . @ ( ) ! ' : |, up to 128 characters.
spanIdstringThis event's id. Defaults to the event id.
parentIdstringA spanId or the traceId, for the tree.
namestringDisplay name of the step or trace, e.g. summarize. Shows up as ai_name in reports.
conversationIdstringGroups traces into a conversation or thread.
model, providerstringe.g. gpt-5-mini, openai. Cost is looked up from these.
input, outputany JSONMessages for a generation, any JSON for a span or trace, strings for an embedding. Each is cut at 200 KB.
toolsany JSONThe tools the model could call.
usageobjectinputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, reasoningTokens. inputTokens includes cached tokens; outputTokens includes reasoning tokens.
costnumberUSD. When given it is stored as is instead of being computed.
latencyMs, timeToFirstTokenMsnumberMilliseconds.
errorobject{ message, code?, httpStatus? }. Its presence marks the event as failed.
stopReasonstringe.g. stop, length, tool-calls.
streambooleanWhether the response was streamed.
modelParametersobjecttemperature, maxTokens, topP and friends.
privacyModebooleanDrops input, output and tools before storage.
propertiesobjectYour own properties; filterable and breakdown-able like on any event.
profileId, groups, deviceIdWho the call belongs to.
timestampISO 8601Back-dates the event; send the call's start time.

What is stored

Each event is stored twice. A slim row in events carries the flat ai_* properties (ai_model, ai_provider, ai_name, ai_input_tokens, ai_output_tokens, ai_total_tokens, ai_cost, ai_latency_ms, ai_is_error, ai_trace_id, ai_conversation_id and more), which is what reports, funnels and the profile timeline read. The full row with the messages lives in a separate table that powers the AI pages; its content expires after 30 days.

Cost

Cost is computed at ingest from a built-in catalog covering the major providers and gateways. Model names are matched after stripping provider prefixes and date suffixes, so gpt-5-mini-2025-08-07 prices like gpt-5-mini. A model the catalog does not know is stored with a cost of zero; send cost yourself in that case.

On this page