Subscription spend connectors
Spend visibility for closed clients without intercepting their traffic.
What a connector does
A connector adds visibility, not routing. It imports vendor usage metadata (time, user, model, tokens, native units, reported cost) and never imports prompts, responses, source code, or attachments. Imported usage stays separate from gateway spend: it adds no gateway requests, consumes no token caps, changes no routing, and never enters API-billing rollups. Each vendor's units remain separate, and imported events never appear as gateway traces or optimizer savings, because the model requests did not traverse Anyray.
Observations live in your deployment's Postgres (anyray_spend_connector_events), not an
Anyray-hosted store. Content fields are removed before storage and GDPR export. Rows are kept at
most 90 days, or the shorter ANYRAY_SPEND_RETENTION_DAYS value, even after credentials are
removed; the console trace-retention window does not apply. The owner-only
get /admin/gdpr/users/:user/export and
delete /admin/gdpr/users/:user endpoints include or erase exact matches
on record.attribution.user / userId. Tenant SSO users cannot view or delete these
deployment-wide rows.
The gateway polls automatically: one replica at a time, retries never duplicate rows, and
Sync now requests an immediate poll without interrupting one already running. Totals are served
by GET /admin/spend-connectors/usage?days= (1–90, default 30), each source in its native units.
Configure a source
Open Settings → Providers and scroll to Connect read-only tools. Connectors apply to the
whole deployment. In fleet mode only the deployment owner can manage them; in a single-tenant
deployment, config:read can view totals and security_admin or owner can change credentials.
Credentials are encrypted using the deployment admin token and never returned by the API; after
rotating the admin token, re-enter them.
| Source | Credential and permission | Imported units | Native traffic |
|---|---|---|---|
| Cursor Team/Enterprise | A team administrator creates an admin:* key under Cursor dashboard API Keys. Cursor's Team pricing page advertises Admin API usage stats, but its API overview marks the Admin API Enterprise-only, so test the key against /teams/filtered-usage-events before enabling the connector; a 403 means Cursor is enforcing that plan gate. | Per-event users, models, tokens, request units, and Cursor-reported cost. | Cursor-hosted native model traffic remains on Cursor. |
| Devin Enterprise | A service-user bearer token with the read-only ViewAccountConsumption permission. | Closed-day ACUs and Devin's per-product ACU breakdown. | Native Devin sessions remain on Devin. |
| GitHub Copilot Business/Enterprise | Enable the Copilot usage metrics policy, then issue a read-only metrics token for the scope you choose: GitHub Copilot token permissions. | Per-user/day activity, interactions, generations, acceptances, AI credits, public list value, and CLI tokens when present. List value is not an invoice total. | Native chat, inline completions, and other Copilot surfaces remain on GitHub. |
Vendor references, current before issuing a credential:
- Cursor Team pricing
- Cursor API overview
- Cursor Admin API
- Devin daily account consumption API
- GitHub Copilot usage metrics API
Use a dedicated least-privilege credential and rotate it from the same console form. Removing a
credential stops future polling; it does not rewrite historical observations. Older connector
settings keep their prior enabled state: a paused source shows Configured · paused; re-enter
its credential to enable it, or remove it.
GitHub Copilot token permissions
Enable the Copilot usage metrics policy first (organization Settings → Copilot → Policies; an enterprise sets it to Enabled everywhere). Without it the report endpoints return nothing, whatever the token carries.
The Scope you pick selects which GitHub endpoint the gateway calls, and the two scopes do not accept the same credential:
| Scope | Endpoint | Fine-grained token | Classic token | Issued by |
|---|---|---|---|---|
| Organization (slug = org login) | get /orgs/{org}/copilot/metrics/reports/users-1-day | Supported. Set Resource owner to the organization, then grant Organization Copilot metrics: Read-only. | read:org | Org owners, or a custom role holding View Organization Copilot Metrics |
| Enterprise (slug = enterprise slug) | get /enterprises/{enterprise}/copilot/metrics/reports/users-1-day | Not supported. GitHub accepts only GitHub App user or installation tokens here. | manage_billing:copilot or read:enterprise | Enterprise owners and billing managers |
Copilot Business runs on organizations under the GitHub Free and Team plans, and the org-scope
report endpoints work there; only the /enterprises/… endpoints need an enterprise. If the
Organization Copilot metrics permission is missing while you create a fine-grained token, the
resource owner is still set to your personal account: switch it to the organization and the
permission appears.
Seats decide whether there is anything to import: the reports cover Copilot Business and Enterprise seats granted by that organization or enterprise. Developers on their own Copilot Pro seats produce no org-level metrics, so the connector stays empty by design.
When a poll fails
The console names the cause under the source's status pill, and Sync now repeats it: a rejected
credential (401), a missing read permission (403, which for Copilot means the usage-metrics
policy or the token's resource owner), an unknown scope (404, a wrong slug), a rate limit
(429), or an unreachable vendor API. Only the first three are yours to fix; the rest retry on
their own.
Two failures deliberately read as the vendor rather than your credential, because neither has an
operator remedy: a request that never reached a status (DNS, TLS, a reset, the 15-second timeout),
and a failure downloading a report from the signed link GitHub just issued, where a 403 means the
signature expired. A cause the gateway cannot place is reported as unrecognized instead of guessed.
Classification uses the vendor's response status alone; the body is never read, logged, or
shown, so no vendor detail and no part of your credential can surface through it.
The gateway reads whole finalized UTC days and holds back the three most recent, which GitHub is still settling. A connector saved today therefore imports nothing until the next finalized day lands, then backfills up to 30 days on its first successful poll. An empty first sync is not a credential failure.
Outbound network policy
Connectors are disabled by default. Enabling one authorizes the gateway to make outbound HTTPS requests to that vendor's official API; allow only the hosts for sources you configure:
| Source | Required outbound HTTPS host |
|---|---|
| Cursor | api.cursor.com |
| Devin | api.devin.ai |
| GitHub Copilot | api.github.com and GitHub-issued report downloads pinned to copilot-reports.github.com |
This traffic goes to the selected vendor, not Anyray. Requests contain the credential and report window; responses contain usage metadata. Connectors never read local prompts, responses, source code, or attachments.
Route the work when optimization is required
Visibility alone cannot optimize a request that never reaches the gateway. Use a supported carrier for that workload:
- Cursor's supported OpenAI custom endpoint for eligible BYOK chat requests, when the organization intentionally provides an upstream API key.
- Cursor Team native Claude/Opus with normal
anyray-connect --tools cursorsetup for supported local Shell/MCP context optimization; the native model request remains Cursor-hosted. - The Anyray language-model provider for VS Code chat; on Copilot Business/Enterprise, native Copilot and inline completions remain visible through the GitHub connector. Individual has no official automatic native-usage connector.
- The pinned Claude or Codex ACP agent that
anyray-connectregisters in JetBrains and Devin. - Claude Code or Codex for their respective first-party subscription seats.
These use supported app settings. Anyray never installs a CA, changes DNS or proxy settings, or uses a private vendor endpoint.