OpenTelemetry Export Wire Reference
Companion to OpenTelemetry Export. Full wire surface: every metric, log event, attribute, enum, and presence rule.
The surface is additive. Tolerate unknown attributes, events, and enum values. Renames and removals get explicit notice.
Transport and scope
- OTLP/HTTP binary protobuf (
application/x-protobuf),POST - Endpoints:
<base>/v1/metricsand<base>/v1/logs - Scope:
cursor.telemetry/0.1.0
Resource attributes
One resource per (team, user, surface, entrypoint, surface version) grouping.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
service.name | string | Always | Constant cursor |
service.version | string | Optional | Client version when source is desktop/CLI; usually absent on cloud_agent / bugbot |
cursor.team.id | int | Always | Your team id |
cursor.surface | string | Always | unspecified | desktop | cli | cloud_agent | bugbot | grok_bot |
cursor.entrypoint | string | Always | unspecified | desktop | cli | web | mobile | sdk_ts | sdk_py | api | automation | github_pr |
cursor.user.id | int | Optional | Opaque team-scoped user id when the source has one. Often absent on cloud agent. Do not require presence. |
Families
Family ids match the toggles in Team Settings. All default on for a new destination except conversation_content, which is off until the team opts in and the destination enables it.
| Family id | Signals | Default | Covers |
|---|---|---|---|
model_usage | metrics + logs | On | token.usage, cost.usage; api.request, api.error, api.correction |
tool_calls | metrics | On | tool.calls |
skills_hooks_plugins | logs | On | skill.activated, hook.execution_complete, plugin.installed |
cloud_agents | logs | On | cloud_agent.pull_request, cloud_agent.setup, cloud_agent.artifact, cloud_agent.mcp_auth_error |
grok_bot_agent_actions | logs | On (needs Action Recording) | grok_bot.mcp_tool_call, grok_bot.shell_command, grok_bot.browser_navigation, grok_bot.computer_use_session |
conversation_content | logs | Off (team opt-in + destination toggle) | conversation.user_message, conversation.assistant_message |
Metrics
All metrics are monotonic delta sums. Metric datapoints carry no correlation IDs; those appear on logs only.
Consume metrics as sums of deltas per series. A series is the resource, the metric name, and the exact datapoint attribute set. Windows for the same series can overlap across flushes.
cursor.token.usage
Unit {token}. Family model_usage.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.token.type | string | Always | input | output | cache_read | cache_creation |
cursor.model.name | string | Optional | Requested public model after routed-intent collapse (auto: to Auto, thinking: to Thinking, pro: to Pro, premium: to Premium; else pass-through). Absent on bugbot or when the source had no model. |
cursor.api.status | string | Optional | success | errored | aborted |
cursor.api.billable | bool | Optional |
cursor.tool.calls
Unit {call}. Family tool_calls. Value 1 per completed tool call.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.tool.kind | string | Always | builtin | mcp |
cursor.tool.name | string | Always | Builtin id (e.g. read, shell) or customer MCP tool name (open) |
cursor.tool.status | string | Always | success | failure | aborted (MCP never reports aborted) |
cursor.mcp.server.name | string | MCP only | Customer-defined server display name (open) |
cursor.cost.usage
Unit USD (double). Family model_usage. Best-effort estimated cost at event time, not an invoice. Subject to cursor.api.correction. For BYOK, this is the Cursor Token Rate only, not provider spend.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.model.name | string | Optional | Same collapse rules as token.usage |
Log events
Severities: INFO=9, WARN=13, ERROR=17.
Common log attributes
| Attribute | Type | Presence | Notes |
|---|---|---|---|
cursor.event.id | string | Always | Dedupe key. Opaque. Deterministic across retries, worker restarts, and Cursor Kafka replay. Prefix customer-telemetry:v1:... is stable; treat the whole string as opaque. |
cursor.source_event.id | string | Always | Opaque internal source identity. Several signals may share one value. |
cursor.request.id | string | Optional | On api.request, api.error, skill.activated, hook.execution_complete, plugin.installed. Never on api.correction, cloud_agent.*, or grok_bot.*. Don't depend on it on conversation.*. |
cursor.conversation.id | string | Optional | IDE/CLI: composer UUID. Cloud agent: customer-visible bc-... agent id. Grok Bot (grok_bot.*, and any log with cursor.surface=grok_bot): Grok Bot conversation id. Join key for session reconstruction across api, skill/hook, cloud_agent, grok_bot, and conversation logs. |
cursor.usage_event.id | string | Optional | api.request / api.error / api.correction only. Request-grain key against Cursor usage and billing exports. |
cursor.api.request
INFO, body api_request. Family model_usage.
| Attribute | Type | Presence | Notes |
|---|---|---|---|
cursor.api.request.input_tokens | int | Always | |
cursor.api.request.output_tokens | int | Always | |
cursor.api.request.cache_read_tokens | int | Always | |
cursor.api.request.cache_creation_tokens | int | Always | |
cursor.model.name | string | Optional | |
cursor.api.billable | bool | Optional |
cursor.api.error
ERROR, body api_error. Family model_usage. No raw error messages. Low-cardinality kind and status attributes are planned; don't depend on them yet.
| Attribute | Type | Presence | Notes |
|---|---|---|---|
cursor.model.name | string | Optional | |
cursor.api.billable | bool | Optional |
cursor.api.correction
WARN, body api_correction_<kind>. Family model_usage. Billing finalization: the usage event was retroactively not billed. Join on cursor.usage_event.id and drop the whole group for billing. Deliberately carries no cursor.model.name.
| Attribute | Type | Presence | Values |
|---|---|---|---|
cursor.api.correction.kind | string | Always | not_billed_errored | not_billed_aborted_before_timeout |
cursor.skill.activated
INFO, body skill_activated. Family skills_hooks_plugins.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.skill.name | string | Always | Customer-authored (open) |
cursor.skill.trigger | string | Always | agent_read | manually_attached | skill_name_in_prompt |
cursor.skill.source | string | Always | unspecified | workspace | user | builtin | plugin | claude |
cursor.plugin.name | string | Optional | When the skill came from a plugin |
cursor.hook.execution_complete
INFO (ERROR for failed / timeout), body hook_execution_complete. Family skills_hooks_plugins.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.hook.name | string | Always | Customer-configured (open) |
cursor.hook.type | string | Always | pre_tool_use | post_tool_use | post_tool_use_failure | before_submit_prompt | after_agent_response | after_agent_thought | stop | subagent_start | subagent_stop |
cursor.hook.outcome | string | Always | success | blocked | failed | timeout |
cursor.hook.duration_ms | int | Always | |
cursor.plugin.name | string | Optional | When the hook came from a plugin |
cursor.plugin.installed
INFO, body plugin_installed. Family skills_hooks_plugins. No conversation.id (install is not conversation-scoped).
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.plugin.name | string | Always | Open |
cursor.plugin.scope | string | Always | unspecified | public | private_marketplace |
cursor.cloud_agent.pull_request
INFO (opened) / WARN (creation_failed), body cloud_agent_pull_request_<kind>. Family cloud_agents. conversation.id = bc-....
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.cloud_agent.pull_request.kind | string | Always | opened | creation_failed |
cursor.cloud_agent.pull_request.number | int | opened only | |
cursor.cloud_agent.pull_request.draft | bool | opened only |
creation_failed is live. opened may be sparse while the producer rolls out.
cursor.cloud_agent.setup
INFO (started / completed) / ERROR (failed), body cloud_agent_setup_<kind>. Family cloud_agents. conversation.id = bc-....
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.cloud_agent.setup.kind | string | Always | started | completed | failed |
cursor.cloud_agent.setup.duration_ms | int | Terminal kinds when present | completed / failed |
cursor.cloud_agent.setup.reason | string | failed only | Open vocabulary (e.g. install_command_failed) |
cursor.cloud_agent.artifact
INFO, body cloud_agent_artifact_created. Family cloud_agents. conversation.id = bc-....
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.cloud_agent.artifact.file_name | string | Always | Open |
cursor.cloud_agent.artifact.content_type | string | Optional | MIME |
cursor.cloud_agent.mcp_auth_error
ERROR, body cloud_agent_mcp_auth_error. Family cloud_agents. conversation.id = bc-....
An MCP server you connected rejected the run's credentials. That server's tool calls failed while the run continued. ERROR because only you can fix the integration; alert on this to catch Automations and Cloud Agents silently losing an MCP server.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.mcp.server.name | string | Always | Customer-defined server display name (open), e.g. github. Same value space as the cursor.tool.calls datapoint attribute. |
Shared grok_bot.* attributes
The grok_bot.* events carry Bot actions from Action Recording, secret-scrubbed before export. Family grok_bot_agent_actions. Every record has cursor.surface=grok_bot. Each event also carries:
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.grok_bot.provenance | string | Always | client (reported by the Grok Bot host) | server (observed by Cursor) |
cursor.grok_bot.turn.id | string | Optional | Turn id within the conversation |
cursor.grok_bot.box.id | string | Optional | Grok Bot computer id. Absent on server provenance |
cursor.grok_bot.mcp_tool_call
INFO (ERROR for failure status), body grok_bot_mcp_tool_call. Family grok_bot_agent_actions. One MCP tool call made by a Bot. Tool arguments and results are never exported.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.tool.name | string | Always | Customer-defined MCP tool name (open) |
cursor.tool.status | string | Always | success | failure |
cursor.grok_bot.mcp.transport | string | Always | http (server-observed) | stdio (on the computer) |
cursor.grok_bot.mcp.duration_ms | int | Always | |
cursor.mcp.server.name | string | Optional | Customer-defined server display name (open) |
cursor.grok_bot.tool_call.id | string | Optional | Grok Bot tool-call id |
cursor.grok_bot.shell_command
INFO (WARN when blocked), body grok_bot_shell_command. Family grok_bot_agent_actions. A shell command a Bot ran or was blocked from running.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.grok_bot.shell.command | string | Always | Secret-scrubbed command text, at most 8 KiB (open) |
cursor.grok_bot.shell.command_truncated | bool | Always | True when the source command exceeded the cap |
cursor.grok_bot.shell.kind | string | Always | foreground | background |
cursor.grok_bot.shell.target | string | Always | box (the Grok Bot computer) | user_machine |
cursor.grok_bot.shell.allowed | bool | Always | Shell policy decision |
cursor.grok_bot.shell.blocked_reason | string | Optional | Policy reason when blocked; secret-scrubbed (open) |
cursor.grok_bot.shell.classification_reasons | string[] | Optional | At most 10 policy classification reasons; secret-scrubbed (open) |
cursor.grok_bot.browser_navigation
INFO, body grok_bot_browser_navigation. Family grok_bot_agent_actions. conversation.id is the Grok Bot conversation id.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.grok_bot.browser.url | string | Always | Normalized scheme://host/path (open). Non-hierarchical schemes never export |
cursor.grok_bot.browser.page_title | string | Optional | Secret-scrubbed (open) |
cursor.grok_bot.computer_use_session
INFO, body grok_bot_computer_use_session. Family grok_bot_agent_actions. Counts and wall time only. No coordinates, typed text, or screenshots.
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.grok_bot.computer_use.action_count | int | Always | |
cursor.grok_bot.computer_use.duration_ms | int | Always | |
cursor.grok_bot.computer_use.screenshot_count | int | Always | |
cursor.grok_bot.tool_call.id | string | Optional | Tool-call id of the subagent invocation |
Conversation content
Family conversation_content. The conversation.* events are the only log records whose body is message text rather than a constant event name. Route on the log event name, as with every other family. cursor.conversation.user_message is a prompt and cursor.conversation.assistant_message is a response; don't parse the body to tell them apart.
Body. Scrubbed message text, at most 32 KiB. Longer messages are cut at the cap and flagged with cursor.conversation.content_truncated.
Identity. Records carry the common log attributes. cursor.conversation.id joins them to the conversation's api.request, skill.activated, hook.execution_complete, cloud_agent.*, and grok_bot.* logs. No user.email or other direct identifier appears on the wire. The only user identifier is the optional, opaque cursor.user.id resource attribute. Don't depend on cursor.request.id, cursor.usage_event.id, or cursor.grok_bot.turn.id on these records.
Surfaces. Cloud Agents and Grok Bot only. Cloud Agent conversations arrive with cursor.surface=cloud_agent on the resource. Grok Bot conversations arrive with cursor.surface=grok_bot, alongside the Bot's grok_bot.* action logs when Action Recording is on. IDE, CLI, and desktop conversations are not on this family yet. Filter or route on cursor.surface.
Both events are INFO and carry these attributes:
| Attribute | Type | Presence | Values / notes |
|---|---|---|---|
cursor.conversation.provenance | string | Always | server (observed by Cursor). client is reserved; tolerate it. |
cursor.conversation.message.id | string | Always | Message id within the conversation |
cursor.conversation.turn.id | string | Optional | Turn id within the conversation. Separate from cursor.grok_bot.turn.id. |
cursor.conversation.content_truncated | bool | Always | True when the body was cut at the cap |
cursor.conversation.user_message
INFO. Body: the scrubbed text of a user prompt.
cursor.conversation.assistant_message
INFO. Body: the scrubbed final assistant text for the response.
Identity and joins
| Goal | Field | Coverage |
|---|---|---|
| Dedupe logs | cursor.event.id | Every log record |
| Group by session or Bot | cursor.conversation.id | Logs when present. For Grok Bot, this value identifies the Bot. |
| Group Grok Bot actions by turn | cursor.grok_bot.turn.id | grok_bot.* logs when present. api.request logs do not carry this field; don't depend on it on conversation.*. |
| Attach prompts and responses to a session | cursor.conversation.id | conversation.* logs (Cloud Agents and Grok Bot), only with the conversation_content opt-in |
| Group prompts and responses by turn | cursor.conversation.turn.id | conversation.* logs when present |
| Group by user | Resource attribute cursor.user.id | Logs and metrics when present. This is an opaque id. No email or other direct identifier is on the wire. |
| Reconcile billing | cursor.usage_event.id | api.request, api.error, and api.correction logs |
Exported logs do not carry OpenTelemetry trace_id or span_id fields. Use cursor.conversation.id and cursor.grok_bot.turn.id for Bot and turn correlation. Metrics do not carry correlation ids; use api.request logs for per-conversation token totals.
See Joining sessions on the setup page for recipes.
Delivery semantics
- Logs are at-least-once. Transient failures recover automatically for about 7 days; dedupe on
event.id. Terminal rejections (persistent 4xx, bad payloads) are not replayed. - Metrics are at-most-once. Failed metric requests are not retried or replayed.
- No ordering guarantee. Corrections can arrive after the requests they amend; order by record timestamp.
- OTLP partial success is honored. Rejected items are not re-sent.
- No backfill from before destination activation. Source retention upstream of export is also about 7 days (separate from the delivery retry window).