OpenTelemetry Export
OpenTelemetry Export streams Cursor usage data for your team to a collector you run. Cursor sends metrics (tokens, tool calls, best-effort cost) and logs (API requests, errors, corrections, skills, hooks, plugins, cloud agent lifecycle events, and recorded Grok Bot actions) to one team-managed destination. Teams can also opt in to conversation content: the user prompts and assistant responses from Cloud Agents and Grok Bot. Export runs server-side.
OpenTelemetry Export is available on the Enterprise plan. Admins configure it in Team Settings > OpenTelemetry Export.
The Wire Reference documents every metric, log event, and attribute.
Prerequisites
- An HTTPS endpoint that accepts OTLP/HTTP protobuf on
/v1/metricsand/v1/logs. Datadog Agent OTLP ingest, the OpenTelemetry Collector, and ClickHouse/ClickStack all work. - A bearer token or API key Cursor can send as a request header.
- The endpoint must be reachable from the public internet. Cursor egresses from a fixed set of source IPs.
Source IPs
Cursor delivers OTLP through a server-side egress proxy. Traffic originates from these static addresses (all /32):
| IP address | CIDR |
|---|---|
| 3.218.161.44 | /32 |
| 3.231.18.206 | /32 |
| 35.174.159.35 | /32 |
| 184.73.225.134 | /32 |
| 3.209.66.12 | /32 |
| 52.44.113.131 | /32 |
These IPs don't rotate without advance notice. Use TLS and auth as the primary control. Add IP allowlisting if your network requires it.
Collector recipes
Cursor pushes to your collector over OTLP/HTTP binary protobuf. gRPC and JSON are not supported. Enter the HTTPS base URL in Team Settings without a /v1 suffix; Cursor appends /v1/metrics and /v1/logs.
Minimal OpenTelemetry Collector
receivers: otlp: protocols: http: endpoint: 0.0.0.0:4318processors: batch:exporters: # Swap for your sink (datadog, clickhouse, logging, etc.) logging: verbosity: basicservice: pipelines: metrics: receivers: [otlp] processors: [batch] exporters: [logging] logs: receivers: [otlp] processors: [batch] exporters: [logging]Terminate TLS in front of the collector with a load balancer, ingress, or the otelcol TLS settings. Enter https://otel.example.com in Cursor, not https://otel.example.com:4318/v1. For auth, terminate at the load balancer or configure a static header for Cursor to send, such as Authorization: Bearer <token>.
Datadog Agent (OTLP ingest)
Enable OTLP HTTP ingest and logs in the Agent, then expose the Agent (or a gateway in front of it) over HTTPS:
logs_enabled: trueotlp_config: receiver: protocols: http: endpoint: 0.0.0.0:4318 logs: enabled: trueThe env-var equivalents are DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT=0.0.0.0:4318, DD_LOGS_ENABLED=true, and DD_OTLP_CONFIG_LOGS_ENABLED=true. Expose port 4318, or terminate TLS on 443 and proxy to 4318.
In Cursor, the base URL is the public https:// endpoint in front of that listener. Add DD-API-KEY or site headers only if your gateway expects them; the Agent already has api_key configured locally.
See OTLP ingest in the Datadog Agent for Agent configuration details.
Databricks and warehouse-style sinks
For warehouse destinations like Databricks or ClickHouse, run a collector with an OTLP HTTP receiver and the vendor exporter, or forward over HTTP into your ingest pipeline. The Cursor side is the same: an HTTPS base URL serving protobuf on /v1/metrics and /v1/logs. Consume metrics as sums of deltas and dedupe logs on cursor.event.id.
Enable
In Team Settings > OpenTelemetry Export:
- Create destination with the base URL (no
/v1/...; Cursor appends the paths) and auth headers - Test connection to check the URL and auth
- Enable. Export starts within about a minute.
Each signal and telemetry family has its own toggle. New families default on unless you turn off auto_enable_new_families. Conversation content is the exception: it stays off until you enable it.
Conversation content
The conversation_content family streams the text of user prompts and assistant responses to your collector as cursor.conversation.user_message and cursor.conversation.assistant_message logs. Today it covers Cloud Agents and Grok Bot conversations only. IDE, CLI, and desktop conversations are not on this family yet. It is the only family that carries message text. The Wire Reference documents the record shape.
Conversation content is off by default. Turn on both toggles below before any message text is exported. Turning either off stops the export; turning them back on does not backfill earlier messages. Teams on Privacy Mode (Legacy) can't turn on Allow conversation content export; the control is unavailable.
Allow conversation content export for the team
In Team Settings > OpenTelemetry Export, turn on Allow conversation content export. It sits above the destination family toggles. Cursor confirms with Conversation content export enabled.
Turn on Conversation content on the destination
On the destination, turn on Conversation content. Prompts and responses export together; there is no separate toggle for each.
What ships once both controls are on:
- Two log events.
cursor.conversation.user_messagecarries a user prompt andcursor.conversation.assistant_messagecarries the final assistant response. The event name identifies the role. The body is the message text. - Scrubbed and capped. Cursor scrubs message text before export and caps each body at 32 KiB.
cursor.conversation.content_truncatedis true when a message hit the cap. - Opaque ids only. Records carry the same ids as other logs. No
user.emailappears on the wire. The only user identifier is the optional, opaquecursor.user.idresource attribute. - Cloud Agents and Grok Bot only. Cloud Agent conversations arrive as
cursor.surface=cloud_agentand Grok Bot conversations ascursor.surface=grok_bot. IDE, CLI, and desktop conversations are not exported by this family yet. Grok Bot messages are separate from thegrok_bot_agent_actionsfamily, which needs Action Recording and carries actions rather than messages.
What Cursor exports
Scope: cursor.telemetry 0.1.0.
Everything below is on by default for a new destination, except conversation_content. Turn individual families off in Team Settings.
Metrics (delta temporality)
cursor.token.usage: bycursor.token.type(input/output/cache_read/cache_creation)cursor.tool.calls: builtin and MCP (cursor.tool.kind)cursor.cost.usage: best-effort USD estimate, not an invoice
Logs
cursor.api.request: model call summarycursor.api.error: error event (no raw messages)cursor.api.correction: billing finalization; join oncursor.usage_event.idcursor.skill.activatedcursor.hook.execution_completecursor.plugin.installedcursor.cloud_agent.setup:started/completed/failedcursor.cloud_agent.artifactcursor.cloud_agent.pull_request:opened/creation_failedcursor.cloud_agent.mcp_auth_error: an MCP server rejected the run's credentialscursor.grok_bot.mcp_tool_call: a Grok Bot connector (MCP) tool callcursor.grok_bot.shell_command: a Grok Bot shell command, secrets scrubbedcursor.grok_bot.browser_navigation: a page the Grok Bot browser navigated tocursor.grok_bot.computer_use_session: a Grok Bot computer use session summarycursor.conversation.user_message: a user prompt, scrubbed; opt-incursor.conversation.assistant_message: an assistant response, scrubbed; opt-in
The cursor.grok_bot.* events carry Action Recording data, so they flow only after a team admin enables Action Recording on the dashboard Grok Bot page. Events are sanitized before export: shell commands are secret-scrubbed and browser URLs are stripped of query strings and fragments.
The cursor.conversation.* events carry message text and flow only after the team opts in and the destination enables the family. See Conversation content.
Families (admin toggles; all default on except conversation_content)
model_usage: token and cost metrics; api.request / api.error / api.correctiontool_calls: tool.calls metricskills_hooks_plugins: skill / hook / plugin logscloud_agents: cloud_agent.* logsgrok_bot_agent_actions: grok_bot.* action logs; requires Action Recording (Enterprise)conversation_content: conversation.* message logs; off by default
Useful attributes
- Resource:
service.name=cursor,cursor.team.id, optionalcursor.user.id, surface/entrypoint. Grok Bot traffic exports ascursor.surface=grok_botacross all families;desktopno longer includes it. - Logs:
cursor.event.id(dedupe), andcursor.request.id/cursor.conversation.id/cursor.usage_event.idwhen present
Delivery
- Metrics are at-most-once. Delta sums can have brief gaps after a failure.
- Logs are at-least-once. Dedupe on
cursor.event.idfor exactly-once views. - There is no backfill from before the destination existed.
- Editing the endpoint or credentials keeps the destination. Disabling or deleting it drops in-flight data.
Auth
Cursor stores headers encrypted. To rotate credentials, edit the destination and save. Changes take effect in about 30 seconds.
Limitations
- Cost is not billing.
cursor.cost.usageis a best-effort estimate. One series covers both included-quota drawdown and on-demand usage. For BYOK it reflects the Cursor Token Rate only, not provider spend. Use the Admin and billing APIs for invoices. - Disabling or deleting a destination drops in-flight data. Rotate credentials by editing the destination instead of deleting and re-adding it.
- Logs can arrive more than once. Delivery is at-least-once. Dedupe on
cursor.event.id. - No prompt content unless you opt in. Message text ships only through the opt-in
conversation_contentfamily. Every other log event carries ids, counts, and low-cardinality attributes. See Conversation content. - No trace context or historical backfill. Exported logs don't carry OpenTelemetry
trace_idorspan_idfields, and Cursor doesn't send traces. Export starts when you enable the destination. - Metric datapoints carry no correlation IDs. Use log attributes for per-conversation joins. See Joining sessions.
- Metrics are delta-only. Sum deltas per series. A strict delta-to-cumulative processor may drop end-time-inverted points.
Joining sessions
Metrics (cursor.token.usage, cursor.tool.calls, cursor.cost.usage) are aggregates. Datapoints carry no conversation.id, request.id, or usage_event.id. This keeps metric cardinality bounded. For session- or request-scoped analysis, use logs.
What each id means
cursor.conversation.idis the session key. In the IDE and CLI it's the composer chat UUID. For cloud agents it's the customer-visiblebc-...agent id. Forgrok_bot.*logs it identifies the Bot. The same value appears on that run'sapi.request,api.error,skill.activated,hook.execution_complete,cloud_agent.*,grok_bot.*, and (when the team opts in)conversation.*logs when present.cursor.usage_event.idis the request-grain key onapi.request,api.error, andapi.correction. Use it to reconcile against Cursor usage and billing exports and to apply corrections.cursor.request.idis an optional per-call id on most logs. It never appears onapi.correction,cloud_agent.*, orgrok_bot.*.cursor.event.idis a dedupe key only, not a join key across event types.
Group Grok Bot activity
| Goal | Group by | Coverage |
|---|---|---|
| One Bot | cursor.conversation.id | Action Recording and model request logs for the Bot |
| One turn | cursor.grok_bot.turn.id | Action Recording logs from the turn when the source provides the id |
| One user | Resource attribute cursor.user.id | Logs and metrics when the source provides the id |
cursor.grok_bot.turn.id appears on grok_bot.* Action Recording logs. It does not appear on api.request, so model requests join to Grok Bot activity per Bot, not per turn. cursor.user.id is an optional opaque id. Don't require it on every record. Subagents use their own cursor.conversation.id; parent rollup is not exported.
For example, a turn can produce these records:
| Log event | cursor.conversation.id | cursor.grok_bot.turn.id | cursor.grok_bot.tool_call.id |
|---|---|---|---|
cursor.api.request | bot-a | Not present | Not present |
cursor.grok_bot.shell_command | bot-a | turn-1 | Not present |
cursor.grok_bot.browser_navigation | bot-a | turn-1 | Not present |
cursor.grok_bot.mcp_tool_call | bot-a | turn-1 | tool-call-1 when present |
Group the grok_bot.* records by turn-1 to reconstruct the recorded actions from the turn. Group all four records by bot-a to combine model requests and actions for the Bot. These fields are custom log attributes, not OpenTelemetry trace or span ids.
With conversation content enabled, the Bot's cursor.conversation.user_message and cursor.conversation.assistant_message logs carry the same cursor.conversation.id (bot-a). Join on it to place the prompt and response next to the Bot's model requests and recorded actions. Message logs carry their own optional cursor.conversation.turn.id. Don't depend on cursor.grok_bot.turn.id or cursor.request.id on them.
Recipe: rank sessions by tokens, then attach skills and tools
- Take
cursor.api.requestlog rows. Sumcursor.api.request.input_tokensandoutput_tokens(and the cache fields if you need them) grouped bycursor.conversation.id. This gives per-session token totals, which metrics can't provide. - Rank conversations by that sum, or by estimated cost.
- Left-join other logs on the same
cursor.conversation.id:cursor.skill.activatedshows which skills rancursor.hook.execution_completeshows hookscursor.cloud_agent.*shows setup, pull requests, artifacts, and MCP auth failures (cloud agents only)cursor.conversation.user_messageandcursor.conversation.assistant_messageshow the prompts and responses (Cloud Agents and Grok Bot, only with theconversation_contentopt-in)
cursor.tool.callsis metric-only, so it has no conversation id. Report org-wide tool rates from the metric. Per-session tool attribution is not on the wire yet.
cursor.cost.usage is also metric-only. To rank sessions by cost, approximate from api.request token totals and your own rates, or pull spend from the Admin and billing APIs and join on cursor.usage_event.id where available.
Recipe: apply a billing correction
- Find
cursor.api.correctionlogs. - Join on
cursor.usage_event.idto theapi.requestandapi.errorlogs sharing that id. - Treat the whole group as not billed.
Caveats
- Subagents get their own conversation id. Parent rollup is not exported yet.
- Dedupe log rows on
cursor.event.idbefore joining if you need exactly-once views.
Change policy
New metrics and events may appear as coverage expands. auto_enable_new_families controls whether they turn on automatically. Renames and removals get explicit notice. The Wire Reference documents the full attribute surface.
OpenTelemetry Export is available on the Enterprise plan
Contact our team to stream Cursor usage into your observability stack.