This plugin ingests Cursor team usage, billing, and adoption metrics from the Cursor Admin API and stores them in DevLake tool-layer tables for Grafana dashboards and SQL analysis.
It follows the same structure and patterns as other DevLake AI usage plugins (notably backend/plugins/gh-copilot).
Cursor Admin API endpoints:
| Endpoint | Method | Data |
|---|---|---|
/teams/members | GET | Team roster |
/teams/spend | POST | Per-user billing cycle spend |
/teams/filtered-usage-events | POST | Event-level usage and charges |
/teams/daily-usage-data | POST | Per-user per-day adoption metrics |
Stored data (tool layer):
| Table | Description |
|---|---|
_tool_cursor_members | Team member roster (email, name, role) |
_tool_cursor_usage_events | Billable usage events with model, tokens, and charged amounts |
_tool_cursor_user_spend | Per-user spend for the current billing cycle (on-demand and included) |
_tool_cursor_daily_usage | Daily adoption metrics: completions, requests by feature, tab acceptance, line edits |
Data is collected in the Raw → Tool layers only. There is no domain-layer converter in this plugin; Grafana dashboards query _tool_cursor_* tables directly.
Pipeline subtasks (in order):
collectMembers → extractMemberscollectUsageEvents → extractUsageEventscollectUserSpend → extractUserSpendcollectDailyUsage → extractDailyUsageapi/ — REST layer for connections, scopes, and scope configsimpl/ — plugin meta, blueprint v200, connection helpersmodels/ — tool-layer models and migration scriptstasks/ — collectors, extractors, and pipeline registrationservice/ — connection test logic (Admin API permission probes)e2e/ — E2E fixtures and golden CSV assertions/teams/* endpointsAuthentication uses HTTP Basic auth: the API key as username with an empty password.
Cursor Production Teamhttps://api.cursor.com/teams/members, /teams/spend, and /teams/filtered-usage-events and reports which endpoints the key can access.When updating an existing connection, omit the token field to keep the encrypted value already stored in DevLake.
Cursor data is team-level. Add a Team scope for the connection. The default scope ID is team.
Use a blueprint plan like:
[ [ { "plugin": "cursor", "options": { "connectionId": 1, "scopeId": "team" } } ] ]
Run the blueprint on a daily schedule to keep usage and cost data current.
LatestSuccessStart) and rewind 7 calendar days so recently-missed or partial days are re-fetched (same idea as the gh-copilot report lookback)./teams/daily-usage-data and /teams/filtered-usage-events requests are split into 30-day chunks (API limit for daily usage; applied to usage events for resilience).extractUsageEvents and extractDailyUsage use a cursor-local stateful extractor (incremental by default). Incremental runs also promote any raw rows with id > MAX(_raw_data_id) already in the tool table, so collected-but-unpromoted raw data is healed without a full refresh. A config version bump (extractorVersion) triggers a one-time full re-extract after upgrade._tool_cursor_* tables only. Legacy domain tables (cursor_usage, cursor_team_events) are unrelated and must not be used.Retry-After response headers and respect the configured rateLimitPerHour.Grafana dashboard JSON lives under grafana/dashboards/mysql/:
| Dashboard | File | UID |
|---|---|---|
| Cursor Usage & Cost | cursor-usage.json | cursor_usage |
| AI Cost Efficiency (Cursor panels) | ai-cost-efficiency.json | — |
| Multi-AI Comparison (Cursor panels) | multi-ai-comparison.json | — |
| Symptom | Likely cause |
|---|---|
| 401 Unauthorized on test connection | Invalid API key, or a User API key instead of a Team Admin key |
| 403 Forbidden on spend or usage events | Key lacks permission for that Admin API endpoint |
| 429 Too Many Requests | Rate limit exceeded — lower rateLimitPerHour or wait for Retry-After |
| Empty usage events after successful run | Selected time range has no billable events, or sync policy excludes the date range |
| Tool max date stuck while raw advances | Deploy plugin with lookback + extract repair, then full-refresh the blueprint; verify MAX(_raw_data_id) in _tool_cursor_* catches up to MAX(id) in _raw_cursor_* |
| Reconciliation delta on dashboard | Expected when comparing event-level charges to billing-cycle spend snapshots; see dashboard notes |
Tokens are sanitized before persisting. Connection test results include a permissions object showing which Admin API endpoints succeeded.
The following endpoints from the AI Code Tracking API are Enterprise plan only and are not implemented in this plugin (no collector, extractor, or _tool_cursor_* table):
| Endpoint | Purpose |
|---|---|
GET /analytics/ai-code/commits | Per-commit AI line attribution (TAB vs Composer vs non-AI) |
GET /analytics/ai-code/changes | Granular accepted AI changes |
GET /analytics/ai-code/commits.csv / changes.csv | Bulk CSV exports of the above |
Team/Business Admin API keys typically receive 401 on these routes. Connection test optionally probes GET /analytics/team/dau (probeEnterpriseAnalytics) to detect Enterprise access; it does not ingest analytics data.
Metrics this would unlock (shown on the Cursor native dashboard but absent from DevLake today):
commitSource: IDE, CLI, or cloudThe Cursor Usage Grafana dashboard (grafana/dashboards/mysql/cursor-usage.json) uses Admin API proxies from _tool_cursor_daily_usage and _tool_cursor_usage_events instead; panel descriptions note where metrics are approximate.
/analytics/*) are not collected in this plugin (see Not collected (Enterprise AI Code Tracking) above).# Unit tests cd backend && go test ./plugins/cursor/... # E2E (requires E2E_DB_URL) make e2e-test
E2E fixtures live in backend/plugins/cursor/e2e/raw_tables/ and e2e/snapshot_tables/.