Organization API
The Organization API lets you perform actions that apply across teams linked to an organization, such as moving users between those teams, reporting on pooled usage across teams, managing organization groups, and reading or updating model access. It uses an Organization API key and the same HTTP patterns as the team Admin API.
- The Organization API uses Basic Authentication with your API key as the username.
- For details on creating API keys, authentication methods, rate limits, and best practices, see the API Overview.
Organization API keys vs Team API keys
Organization API keys are organization-scoped credentials. Team API keys are team-scoped credentials.
Use an Organization API key when calling organization-level endpoints like /organizations/team-memberships/sync, /organizations/pooled-usage, and /organizations/groups.
Use a Team API key when calling team-level endpoints under /teams/* (for example, /teams/members and /teams/spend).
Key differences
- Scope: Organization API keys can act across teams linked to the same organization. Team API keys can only act within one team.
- Endpoint compatibility: Organization endpoints require Organization API keys. Team endpoints require Team API keys.
- Key scopes: Each route requires a specific scope on the key. Read-only membership routes accept
members:read; membership and group write routes needmembers:*; usage routes needusage:*. Keys withadmin:*work everywhere because admin implies the other scopes. - Authorization failures: If the key scope does not match the endpoint scope, requests fail with authentication or authorization errors (typically
401or403).
Scopes
Every Organization API key carries exactly one scope. A route runs only when the key's scope covers it, and broader scopes include everything narrower scopes allow.
| Scope | Access | Example routes |
|---|---|---|
members:read | Read-only access to organization membership. | GET /organizations/members |
members:* | Read and write access to membership and groups. Includes everything members:read allows. | GET /organizations/members, POST /organizations/team-memberships/sync, all /organizations/groups routes |
usage:* | Read access to pooled usage and reporting. | POST /organizations/pooled-usage, POST /organizations/filtered-usage-events, POST /organizations/daily-usage-data, POST /organizations/spend |
models:read | Read-only access to model-access configuration and provider inventories. | GET /organizations/teams/model-access/configuration, GET /organizations/teams/{teamId}/model-access/configuration, GET /organizations/teams/{teamId}/model-access/providers |
models:* | Read and write access to model access. Includes everything models:read allows. | All model-access routes, including bulk provider/model toggles and bulk configuration |
admin:* | Full access to every organization route. | All of the above |
Pick the narrowest scope for the job. Use members:read for read-only integrations that list members but never change membership. Use models:read or models:* for model-access automation without granting full admin. You can select these scopes when you create an Organization API key in the dashboard.
How should I pass an Organization API key?
Pass it the same way as other Cursor API keys: Basic authentication with the key as the username and an empty password.
curl -X POST https://api.cursor.com/organizations/team-memberships/sync \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "organizationId": "org_abc123", "users": [ { "userId": 12345, "destinationTeamId": 7 } ] }'Members
Read organization membership and move members between the teams linked to your organization.
- Availability: Enterprise only
- Authentication: Organization API key (Basic auth). Reading members accepts the read-only
members:readscope; moving members requiresmembers:*. Keys withadmin:*work for both. - Scope:
GET /organizations/membersis organization-scoped and paginated, returning each member's organization role plus every linked-team assignment in one response. - Pagination:
GET /organizations/membersacceptspageandpageSize.pageSizeis capped at 200; larger values are clamped to 200.
List Organization Members
/organizations/membersRetrieve members of the organization attached to your API key, along with each member's organization role and their assignments across linked teams. Results are paginated.
Query parameters
page number
pageSize number
Response Fields
members array
userIdnumber - Unique numeric identifier for the member, matching theidreturned by the teamGET /teams/membersendpointemailstring - Email address of the membernamestring - Display name of the memberorganizationRolestring - Organization-level role, eitheradminormember. This is distinct from each team assignment'steamRole: a user can be an orgadminwhile holding amemberrole on a specific team, or vice versa.teamsarray - The member's assignments across teams linked to the organization. Each object contains:teamIdnumber - Integer ID of a linked team the member belongs toteamRolestring - Role within that team (e.g.,member,owner)
pagination object
page, pageSize, totalCount, totalPages, hasNextPage, and hasPreviousPage.curl -X GET "https://api.cursor.com/organizations/members?page=1&pageSize=50" \ -u YOUR_ORGANIZATION_API_KEY:Response:
{ "members": [ { "userId": 12345, "email": "developer@company.com", "name": "Alex", "organizationRole": "member", "teams": [ { "teamId": 7, "teamRole": "member" }, { "teamId": 8, "teamRole": "owner" } ] }, { "userId": 12346, "email": "admin@company.com", "name": "Sam", "organizationRole": "admin", "teams": [ { "teamId": 7, "teamRole": "owner" } ] } ], "pagination": { "page": 1, "pageSize": 50, "totalCount": 2, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false }}Sync Organization Team Memberships
/organizations/team-memberships/syncSet the teams that one or more users belong to within your organization. This matches the bulk style of the CSV import API: you send an array of users and receive a result row for each one.
Each entry must use exactly one of teamIds or destinationTeamId:
teamIdsis the complete set of team IDs the user should belong to. The endpoint reconciles the user's memberships to exactly that set. It adds any listed teams the user is not on yet and removes any teams that are not listed. To keep a user on their current team while adding another during a migration, list both (for example[oldTeamId, newTeamId]).destinationTeamIdputs the user on a single team. They are placed on the specified team and removed from every other team. SettingdestinationTeamId: NNNis functionally equivalent toteamIds: [NNN].
Request body
organizationId string Required
org_abc123). Must match the organization for the Organization API key used to call the endpoint.users array Required
teamIds or destinationTeamId):userIdnumber | string: ID of the user to sync. Accepts either an integer numeric ID (for example12345) or a string ID (for example"user_abc123").teamIdsnumber[]: The complete set of org-linked team IDs the user should belong to after the sync. Memberships are reconciled to exactly this set. Any team not listed is removed. Include the user's current teams to keep them (for example[7, 8]). At most 100 teams per entry.destinationTeamIdnumber: Field for syncing to a single team. SettingdestinationTeamId: NNNis the same as sendingteamIds: [NNN]. The user's teams are set to exactly that one team. Must be a team linked to the organization.
teamIds or destinationTeamId per entry.Success response (HTTP 200)
results array
userId, the resolved teamIds for that entry, and either status: "success" or status: "error" with errorMessage when that row failed. Entries sent with destinationTeamId also echo destinationTeamId (the first team in teamIds).successCount number
status: "success".errorCount number
status: "error".- Availability: Enterprise only
- Authentication: Organization API key (Basic auth). The key must include the
members:*scope for this route; keys withadmin:*also work because admin implies members. - Organization match: The
organizationIdin the body must be the same organization as the API key; otherwise the request is rejected. - Team set:
teamIdsis the exact set of teams the user should belong to after the call. The user will be removed from any team NOT listed, so include the user's existing teams in the set to retain them. - One team field per entry: Provide exactly one of
teamIdsordestinationTeamIdfor each entry. - Per-entry team cap: An entry's
teamIdsmay list at most 100 teams. - The target user must already be a member of the organization for a sync to succeed.
- Every team in the entry must be linked to the organization for the sync to succeed.
- If one entry in
usersfails, others can still succeed; check eachresultsentry’sstatusanderrorMessage. - Batch size: A single request may include up to 500 entries. Send additional batches in separate requests if needed.
curl -X POST https://api.cursor.com/organizations/team-memberships/sync \ -u YOUR_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "organizationId": "org_abc123", "users": [ { "userId": 12345, "teamIds": [7, 8] }, { "userId": "user_abc123", "destinationTeamId": 8 } ] }'The first entry links user 12345 to exactly teams 7 and 8 (adding either team the user isn't already on, and removing any other linked team). The second entry uses destinationTeamId, which is the same as sending teamIds: [8].
Response:
{ "results": [ { "userId": 12345, "teamIds": [7, 8], "status": "success" }, { "userId": "user_abc123", "teamIds": [8], "destinationTeamId": 8, "status": "success" } ], "successCount": 2, "errorCount": 0}Error responses:
Most thrown API errors use HTTP 401, 403, or 400 and a JSON body shaped like:
{ "code": "error", "message": "…"}404: organization not found (this route uses a different field name for the message):
{ "error": "Organization not found"}401: invalid Organization API key (wrong or missing key):
{ "code": "error", "message": "Invalid Organization API Key"}401: missing required scope (key is valid but does not include members:* or admin:*):
{ "code": "error", "message": "Organization API key missing required scope: members:*"}403: organization does not match the key (organizationId in the body is not the organization for this API key):
{ "code": "error", "message": "Not authorized"}400: invalid request body (examples; only one applies per failed request):
{ "code": "error", "message": "Request body is required"}{ "code": "error", "message": "organizationId is required"}{ "code": "error", "message": "users must be a non-empty array"}{ "code": "error", "message": "users must not contain more than 500 moves"}Per-row failures (HTTP 200): Validation or business rules for a single entry are returned in results with status: "error" and errorMessage. The examples below use destinationTeamId, so the rows echo destinationTeamId; entries sent with teamIds echo teamIds instead. Invalid userId / destinationTeamId types use 0 for the invalid field in the row:
{ "results": [ { "userId": 0, "destinationTeamId": 7, "status": "error", "errorMessage": "Invalid userId" } ], "successCount": 0, "errorCount": 1}{ "results": [ { "userId": 12345, "destinationTeamId": 0, "status": "error", "errorMessage": "Invalid destinationTeamId" } ], "successCount": 0, "errorCount": 1}{ "results": [ { "userId": 0, "destinationTeamId": 0, "status": "error", "errorMessage": "Invalid userId. Invalid destinationTeamId" } ], "successCount": 0, "errorCount": 1}Per-row failures (HTTP 200): From the sync logic when inputs are well-typed but the change cannot be applied:
{ "results": [ { "userId": 12345, "destinationTeamId": 999, "status": "error", "errorMessage": "Team is not linked to this organization" } ], "successCount": 0, "errorCount": 1}{ "results": [ { "userId": 12345, "destinationTeamId": 7, "status": "error", "errorMessage": "User is not a member of this organization" } ], "successCount": 0, "errorCount": 1}{ "results": [ { "userId": 12345, "destinationTeamId": 7, "status": "error", "errorMessage": "User not found" } ], "successCount": 0, "errorCount": 1}Usage
Report on usage across every team linked to your organization. These endpoints aggregate data from all teams in the organization pool, so you don't need a separate Team API key per team. For single-team reporting, use the team Admin API usage endpoints instead.
- Availability: Enterprise only
- Authentication: Organization API key (Basic auth). The key must include the
usage:*scope for these routes; keys withadmin:*also work because admin implies usage. - Organization match: The
organizationIdin the body must be the same organization as the API key; otherwise the request is rejected. - Team containment: Every entry in
teamIdsmust belong to the organization. Requests that reference a team outside the organization are rejected. - Polling: Usage data is aggregated at the hourly level. Poll these endpoints at most once per hour. Rate limited to 20 requests per minute. See rate limits and best practices.
Get Pooled Usage
/organizations/pooled-usageRetrieve organization-pooled usage: the pool's spend limit, total usage across the organization, and a per-team breakdown. This powers the pooled-usage section of the dashboard. All monetary fields are in cents.
Request body
organizationId string Required
org_abc123). Must match the organization for the Organization API key used to call the endpoint.Response Fields
pool object
limitCentsnumber - Pooled spend limit for the organization, in centsusedCentsnumber - Total pooled usage consumed so far, in centsremainingCentsnumber - Remaining pooled budget (limitCentsminususedCents), in centscontractStartDatestring | null - ISO 8601 timestamp marking the start of the current contract period, ornullwhen no contract dates are setcontractEndDatestring | null - ISO 8601 timestamp marking the end of the current contract period, ornullwhen no contract dates are set
teams array
usedCents equals pool.usedCents. Each object contains:teamIdnumber - Integer ID of a team linked to the organizationusedCentsnumber - Usage consumed by this team during the current contract period, in centsbudgetLimitCentsnumber | undefined - Per-team budget cap in cents. Present only when a budget is configured for the team.
curl -X POST https://api.cursor.com/organizations/pooled-usage \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "organizationId": "org_abc123" }'Response:
{ "pool": { "limitCents": 5000000, "usedCents": 1862340, "remainingCents": 3137660, "contractStartDate": "2026-01-01T00:00:00.000Z", "contractEndDate": "2026-12-31T23:59:59.999Z" }, "teams": [ { "teamId": 7, "usedCents": 1440100, "budgetLimitCents": 2000000 }, { "teamId": 8, "usedCents": 422240 } ]}Get Usage Events
/organizations/filtered-usage-eventsRetrieve detailed usage events across the teams linked to your organization. This is the organization-wide counterpart to the team /teams/filtered-usage-events endpoint: it returns the same event shape, with each event tagged by its owning teamId.
By default, events from all teams in the organization pool are returned. Pass teamIds to restrict the response to specific teams.
Cost Calculation: Sum the chargedCents field across events to reconcile event-level costs with the per-team usedCents breakdown from /organizations/pooled-usage. This field includes both the model cost and the Cursor Token Rate when a request is eligible for the rate.
The cursorTokenFee field represents the Cursor Token Rate and is only present when the rate applies to a third-party model request. This includes when Auto Balance or Auto Intelligence routes to a third-party model. Auto Cost, first-party Cursor models such as Composer 2.5, Grok 4.6, and Grok 4.5, and request-based enterprise accounts do not include this fee.
Request body
organizationId string Required
org_abc123). Must match the organization for the Organization API key used to call the endpoint.teamIds number[]
startDate number
endDate number
userId number
email string
serviceAccountId string
page number
1pageSize number
10Response Fields
Each object in usageEvents contains the same fields as the team endpoint, plus an owning-team tag:
teamIdnumber - Integer ID of the team that owns this eventtimestampstring - Event timestamp in epoch milliseconds (as a string)userEmailstring - Email address of the user who made the requestserviceAccountIdstring | undefined - ID of the service account that made the request. Omitted for human user events.serviceAccountNamestring | undefined - Display name of the service account that made the request. Omitted for human user events.modelstring - AI model used for the requestkindstring - Billing category (e.g.,Usage-based,Included in Business)maxModeboolean - Whether the request used max moderequestsCostsnumber - Cost in request unitsisTokenBasedCallboolean - Whether the request was billed by token usageisChargeableboolean - Whether this event incurs a chargeisHeadlessboolean - Whether this request was made without a connected client (e.g., background agents)tokenUsageobject | undefined - Token usage details (present whenisTokenBasedCallistrue):inputTokensnumber - Input tokens consumedoutputTokensnumber - Output tokens generatedcacheWriteTokensnumber - Tokens written to cachecacheReadTokensnumber - Tokens read from cachetotalCentsnumber - Total model cost in centsdiscountPercentOffnumber | undefined - Discount percentage applied, if any
chargedCentsnumber - Total amount charged in cents for this event. For third-party model requests subject to the Cursor Token Rate, this includes model cost plus the Cursor Token Rate.cursorTokenFeenumber | undefined - Cursor Token Rate in cents. Present only when the rate applies to a third-party model request (including Auto Balance or Auto Intelligence routes to a third-party model).
# Events across all teams in the organization poolcurl -X POST https://api.cursor.com/organizations/filtered-usage-events \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "organizationId": "org_abc123", "startDate": 1748411762359, "endDate": 1751003762359, "page": 1, "pageSize": 25 }'# Events restricted to specific teamscurl -X POST https://api.cursor.com/organizations/filtered-usage-events \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "organizationId": "org_abc123", "teamIds": [7, 8], "startDate": 1748411762359, "endDate": 1751003762359, "page": 1, "pageSize": 25 }'Response:
{ "totalUsageEventsCount": 113, "pagination": { "numPages": 12, "currentPage": 1, "pageSize": 10, "hasNextPage": true, "hasPreviousPage": false }, "usageEvents": [ { "teamId": 7, "timestamp": "1750979225854", "userEmail": "developer@company.com", "model": "claude-4.5-sonnet", "kind": "Usage-based", "maxMode": true, "requestsCosts": 5, "isTokenBasedCall": true, "isChargeable": true, "isHeadless": false, "tokenUsage": { "inputTokens": 126, "outputTokens": 450, "cacheWriteTokens": 6112, "cacheReadTokens": 11964, "totalCents": 20.18232 }, "chargedCents": 21.36232, "cursorTokenFee": 1.18 }, { "teamId": 8, "timestamp": "1750978339901", "userEmail": "admin@company.com", "model": "claude-4-sonnet-thinking", "kind": "Included in Business", "maxMode": true, "requestsCosts": 1.4, "isTokenBasedCall": false, "isChargeable": false, "isHeadless": false, "chargedCents": 8 } ], "period": { "startDate": 1748411762359, "endDate": 1751003762359 }}Get Daily Usage Data
/organizations/daily-usage-dataRetrieve daily usage metrics for every member across the teams linked to your organization. This is the organization-wide counterpart to the team /teams/daily-usage-data endpoint, with each row tagged by its owning teamId. Results are paginated by user and return data for all members with a membership during the requested date range; use page and pageSize to page through them.
Request body
organizationId string Required
org_abc123). Must match the organization for the Organization API key used to call the endpoint.startDate number
endDate number
teamIds number[]
page number
1pageSize number
1000userEmail string
userEmails is accepted as an alias.Date range cannot exceed 30 days. Make multiple requests for longer periods.
The fields subscriptionIncludedReqs, usageBasedReqs, and apiKeyReqs count raw usage events, not billable request units in older request-based pricing.
Response Fields
Each object in the data array contains the same fields as the team daily usage endpoint, plus a teamId. Key fields:
userIdstring - Encoded user ID with theuser_prefix (e.g.,user_abc123)teamIdnumber - ID of the org-linked team this row belongs todaystring - The date this record covers (ISO date, e.g.,2024-03-18)datenumber - Date as epoch millisecondsemailstring - User's email addressisActiveboolean - Whether the user had activity on this daytotalLinesAddednumber - Total lines of code addedtotalLinesDeletednumber - Total lines of code deletedacceptedLinesAddednumber - AI-suggested lines added that were acceptedacceptedLinesDeletednumber - AI-suggested lines deleted that were acceptedtotalAppliesnumber - Total AI code apply actionstotalAcceptsnumber - Total accepted AI suggestionstotalRejectsnumber - Total rejected AI suggestionstotalTabsShownnumber - Total Tab completions shown to the usertotalTabsAcceptednumber - Total Tab completions accepted by the usercomposerRequestsnumber - Number of Composer requests madechatRequestsnumber - Number of chat requests madeagentRequestsnumber - Number of Agent mode requests madecmdkUsagesnumber - Number of Cmd+K inline edit usagessubscriptionIncludedReqsnumber - Requests included in the subscription planapiKeyReqsnumber - Requests made via API keyusageBasedReqsnumber - Usage-based (overage) requestsbugbotUsagesnumber - Number of Bugbot usagesmostUsedModelstring | null - Most frequently used AI model for the dayapplyMostUsedExtensionstring | null - Most common file extension for apply actionstabMostUsedExtensionstring | null - Most common file extension for Tab completionsclientVersionstring | null - Cursor client version used
The response also includes a pagination object (page, pageSize, totalUsers, totalPages, hasNextPage, hasPreviousPage) and a period object (startDate, endDate).
curl -X POST https://api.cursor.com/organizations/daily-usage-data \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "organizationId": "org_abc123", "startDate": 1710720000000, "endDate": 1710892800000, "page": 1, "pageSize": 1000 }'Response:
{ "data": [ { "userId": "user_abc123", "teamId": 101, "day": "2024-03-18", "date": 1710720000000, "isActive": true, "totalLinesAdded": 1543, "totalLinesDeleted": 892, "acceptedLinesAdded": 1102, "acceptedLinesDeleted": 645, "totalApplies": 87, "totalAccepts": 73, "totalRejects": 14, "totalTabsShown": 342, "totalTabsAccepted": 289, "composerRequests": 45, "chatRequests": 128, "agentRequests": 12, "cmdkUsages": 67, "subscriptionIncludedReqs": 180, "apiKeyReqs": 0, "usageBasedReqs": 5, "bugbotUsages": 3, "mostUsedModel": "gpt-5", "applyMostUsedExtension": ".tsx", "tabMostUsedExtension": ".ts", "clientVersion": "0.25.1", "email": "developer@company.com" } ], "period": { "startDate": 1710720000000, "endDate": 1710892800000 }, "pagination": { "page": 1, "pageSize": 1000, "totalUsers": 150, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false }}Get Spending Data
/organizations/spendRetrieve per-member spend across the teams linked to your organization. This is the organization-wide counterpart to the team /teams/spend endpoint, with each member tagged by its owning teamId. Unlike the team endpoint, spend is reported over the organization contract window (not per-team billing cycles) using the same included-spend definition as /organizations/pooled-usage, so the numbers reconcile with the pool.
Request body
organizationId string Required
org_abc123). Must match the organization for the Organization API key used to call the endpoint.teamIds number[]
sortBy string
email, name, spendCents. Default: emailsortDirection string
asc, desc. Default: ascpage number
1pageSize number
100Spend is reported across the organization's pooled teams, so the single-team fields subscriptionCycleStart, overallSpendCents, fastPremiumRequests, hardLimitOverrideDollars, and monthlyLimitDollars from /teams/spend are not included. The reporting window is returned in period.
Response Fields
Each object in teamMemberSpend contains:
userIdstring - Encoded user ID with theuser_prefix (e.g.,user_abc123)teamIdnumber - ID of the org-linked team this member belongs tonamestring - Display name of the useremailstring - Email address of the userrolestring - Role in the team (e.g.,member,owner)spendCentsnumber - Included pool spend in cents attributed to this member over the organization contract window
The response also includes totalMembers (number), totalPages (number), and a period object (startDate, endDate in epoch milliseconds) describing the organization contract window.
curl -X POST https://api.cursor.com/organizations/spend \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "organizationId": "org_abc123", "sortBy": "spendCents", "sortDirection": "desc", "page": 1, "pageSize": 25 }'Response:
{ "teamMemberSpend": [ { "userId": "user_abc123", "teamId": 101, "name": "Alex", "email": "developer@company.com", "role": "member", "spendCents": 2450 }, { "userId": "user_def456", "teamId": 202, "name": "Sam", "email": "admin@company.com", "role": "owner", "spendCents": 1875 } ], "totalMembers": 15, "totalPages": 1, "period": { "startDate": 1735689600000, "endDate": 1767225600000 }}Model access
Model access routes are in preview and may change. Paths, response fields, and error behavior can shift before general availability.
Read and update model access policy for teams linked to the organization. These routes match the team model access API, scoped to linked teams.
Use the list and per-team GETs to catch configuration drift. Align teams with configuration PUTs plus provider/model toggles (including per-model parameters). There is no org-level copy endpoint or policy fingerprint.
Enabling a model without parameter settings leaves it on the catalog defaults. Use the bulk model route when defaults such as Fast do not match your organization policy.
Numeric teamId values come from routes such as GET /organizations/members.
- Availability: Enterprise organizations. Target teams must have model access control enabled.
- Authentication: Organization API key (Basic auth). Reads require
models:read. Writes requiremodels:*. Keys withadmin:*work for both.members:*,usage:*, andread:*keys cannot call these routes. - Team containment: Every
teamIdmust be linked to the organization. On single-team routes, unknown or unlinked teams return 404. On bulk routes, unlinked teams are HTTP 200 error rows. - Configuration first: Provider and model reads and writes return 409 while that team is still
unrestricted(orlegacy). Create a custom policy withPUT /organizations/teams/{teamId}/model-access/configurationfirst (or the bulk configuration route). The first defaults PUT seeds catalog defaults; it does not clone another team's on/off map. - Return to unrestricted: Send
{ "state": "unrestricted" }on the per-team or bulk configuration PUT. - Bulk partial success: Bulk routes accept up to 100
teamIdsand always return HTTP 200 when the batch is processed, even if some rows fail. InspecterrorCountand everyresults[].status. Successful rows are not rolled back. Operations are idempotent per team, so retry only the failedteamIds. A 4xx or 5xx response rejects the whole request and applies no changes. Response shape matches/organizations/team-memberships/sync. - Rate limits: 20 requests per minute. Writes appear in team audit logs as
team_settingsevents. See rate limits and best practices.
List Model Access Configuration
/organizations/teams/model-access/configurationList model-access configuration for linked teams. Use this to find unrestricted vs custom policy drift. For on/off drift, GET each team's providers and compare.
If a linked team does not have model access control enabled, that row is still HTTP 200 and includes errorMessage instead of state / defaults. Per-team GET and write routes for that team return 403.
Query parameters
page number
pageSize number
teamIds string
7,8,9.curl -X GET "https://api.cursor.com/organizations/teams/model-access/configuration?page=1&pageSize=50" \ -u YOUR_ORGANIZATION_API_KEY:Response:
{ "teams": [ { "teamId": 7, "teamName": "Platform", "state": "custom", "newProviderDefault": "disabled", "newModelDefault": "enabled" }, { "teamId": 8, "teamName": "Mobile", "state": "custom", "newProviderDefault": "disabled", "newModelDefault": "enabled" }, { "teamId": 9, "teamName": "Data", "state": "unrestricted", "newProviderDefault": null, "newModelDefault": null }, { "teamId": 10, "teamName": "Research", "errorMessage": "Model access control is not available for this team" } ], "pagination": { "page": 1, "pageSize": 50, "totalCount": 4, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false }}Get Team Model Access Configuration
/organizations/teams/:teamId/model-access/configurationGet configuration for one linked team.
Parameters
teamId number Required
curl -X GET https://api.cursor.com/organizations/teams/7/model-access/configuration \ -u YOUR_ORGANIZATION_API_KEY:Update Team Model Access Configuration
/organizations/teams/:teamId/model-access/configurationCreate or update configuration for one linked team, or return that team to unrestricted. Same body and seeding behavior as the team route.
Parameters
teamId number Required
Request body
state string
unrestricted to clear policy. Omit when sending defaults.newProviderDefault string
enabled or disabled. Required when creating or updating a custom policy; omit when state is unrestricted.newModelDefault string
enabled or disabled. Required when creating or updating a custom policy; omit when state is unrestricted.curl -X PUT https://api.cursor.com/organizations/teams/7/model-access/configuration \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "newProviderDefault": "disabled", "newModelDefault": "enabled" }'Return one linked team to unrestricted:
curl -X PUT https://api.cursor.com/organizations/teams/7/model-access/configuration \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "state": "unrestricted" }'Bulk Update Model Access Configuration
/organizations/teams/model-access/configurationCreate or update configuration, or return teams to unrestricted, across many linked teams. Up to 100 teamIds per request.
HTTP 200 means the batch was processed, not that every row succeeded. Check errorCount and each results[].status. Successful teams keep their new configuration. The operation is idempotent per team, so retry only failed teamIds. A 4xx or 5xx response rejects the whole request and applies no changes.
Request body
teamIds number[] Required
state string
unrestricted to clear policy on each team. Omit when sending defaults.newProviderDefault string
enabled or disabled. Required when creating or updating custom policies; omit when state is unrestricted.newModelDefault string
enabled or disabled. Required when creating or updating custom policies; omit when state is unrestricted.Seed custom policy defaults on many teams:
curl -X PUT https://api.cursor.com/organizations/teams/model-access/configuration \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "teamIds": [7, 8, 9], "newProviderDefault": "disabled", "newModelDefault": "enabled" }'Return many teams to unrestricted:
curl -X PUT https://api.cursor.com/organizations/teams/model-access/configuration \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "teamIds": [7, 8, 9], "state": "unrestricted" }'Response:
{ "results": [ { "teamId": 7, "status": "success" }, { "teamId": 8, "status": "success" }, { "teamId": 9, "status": "error", "errorMessage": "Team is not linked to this organization" } ], "successCount": 2, "errorCount": 1}Get Team Model Access Providers
/organizations/teams/:teamId/model-access/providersList providers and models for one linked team, including per-model parameters (same shape as the team providers route). Returns 409 when the team does not have a custom policy.
Parameters
teamId number Required
curl -X GET https://api.cursor.com/organizations/teams/7/model-access/providers \ -u YOUR_ORGANIZATION_API_KEY:Update Team Model Access Provider
/organizations/teams/:teamId/model-access/providers/:providerEnable or disable a provider on one linked team. Returns 409 when the team does not have a custom policy.
Parameters
teamId number Required
provider string Required
openai).Request body
enabled boolean Required
curl -X PUT https://api.cursor.com/organizations/teams/7/model-access/providers/openai \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{"enabled": false}'Update Team Model Access Model
/organizations/teams/:teamId/model-access/providers/:provider/models/:modelEnable or disable a model on one linked team, and optionally set per-model parameters (same body as the team model route). Returns 409 when the team does not have a custom policy.
Parameters
teamId number Required
provider string Required
anthropic).model string Required
claude-opus-4-6).Request body
enabled boolean Required
parameters object
{ allowedValues, defaultValue }. Omitted fields are unchanged. allowedValues: null clears a restriction. defaultValue: null restores the catalog default. See the team Update Model Access Model docs.Disable Fast on one linked team:
curl -X PUT https://api.cursor.com/organizations/teams/7/model-access/providers/anthropic/models/claude-opus-4-6 \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "parameters": { "fast": { "allowedValues": ["false"] } } }'Set default reasoning effort:
curl -X PUT https://api.cursor.com/organizations/teams/7/model-access/providers/openai/models/gpt-5.4 \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "parameters": { "reasoning": { "allowedValues": ["low", "medium", "high"], "defaultValue": "high" } } }'Bulk Update Model Access Provider
/organizations/teams/model-access/providers/:providerEnable or disable a provider on many linked teams. Up to 100 teamIds per request.
HTTP 200 means the batch was processed, not that every row succeeded. Inspect errorCount and every results[].status. Successful rows are not rolled back. The operation is idempotent per team, so retry only failed teamIds. A 4xx or 5xx response rejects the whole request and applies no changes.
Parameters
provider string Required
openai).Request body
enabled boolean Required
teamIds number[] Required
curl -X PUT https://api.cursor.com/organizations/teams/model-access/providers/openai \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "teamIds": [7, 8, 9], "enabled": false }'Response:
{ "results": [ { "teamId": 7, "status": "success" }, { "teamId": 8, "status": "success" }, { "teamId": 9, "status": "error", "errorMessage": "Team has no model access policy. Create one with PUT /teams/model-access/configuration, or enable model access in Team Settings → Models." } ], "successCount": 2, "errorCount": 1}In this example HTTP status is still 200 because the batch completed. Teams 7 and 8 keep the provider disabled; only retry team 9 after creating its configuration.
Bulk Update Model Access Model
/organizations/teams/model-access/providers/:provider/models/:modelEnable or disable a model on many linked teams, optionally with the same parameters map as the single-team model PUT. Up to 100 teamIds per request.
HTTP 200 means the batch was processed, not that every row succeeded. Inspect errorCount and every results[].status. Successful rows are not rolled back. The operation is idempotent per team, so retry only failed teamIds. A 4xx or 5xx response rejects the whole request and applies no changes.
Parameters
provider string Required
anthropic).model string Required
claude-opus-4-6).Request body
enabled boolean Required
teamIds number[] Required
parameters object
allowedValues: null clears a restriction. defaultValue: null restores the catalog default.Disable Fast across linked teams:
curl -X PUT https://api.cursor.com/organizations/teams/model-access/providers/anthropic/models/claude-opus-4-6 \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "teamIds": [7, 8, 9], "enabled": true, "parameters": { "fast": { "allowedValues": ["false"] } } }'Pin the default reasoning effort across linked teams:
curl -X PUT https://api.cursor.com/organizations/teams/model-access/providers/openai/models/gpt-5.4 \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "teamIds": [7, 8, 9], "enabled": true, "parameters": { "reasoning": { "allowedValues": ["low", "medium", "high"], "defaultValue": "high" } } }'Response:
{ "results": [ { "teamId": 7, "status": "success" }, { "teamId": 8, "status": "success" }, { "teamId": 9, "status": "error", "errorMessage": "Team has no model access policy. Create one with PUT /teams/model-access/configuration, or enable model access in Team Settings → Models." } ], "successCount": 2, "errorCount": 1}Errors
Error bodies use:
{ "code": "error", "message": "…" }| Status | When |
|---|---|
401 | Bad key, or missing models:read / models:* (or admin:*) |
403 | Model access control is not available for that team (single-team routes) |
404 | Team is not linked to the organization (single-team routes) |
409 | Provider or model read or single-team write while that team's state is unrestricted or legacy |
400 | Unknown provider, model, parameter id, or parameter value; invalid body; empty allowedValues; default outside allowedValues; settings that resolve to no valid model variant; or a Smart Auto required model would be blocked |
Bulk org routes (PUT .../providers/:provider, PUT .../providers/:provider/models/:model, and PUT .../configuration with teamIds) return HTTP 200 when the batch is processed, even if some rows fail. A non-zero errorCount is still a successful HTTP response. Unlinked teams and public errors such as missing configuration appear as status: "error" rows. Successful rows are not rolled back. Operations are idempotent per team, so retry only the failed teamIds. Any 4xx or 5xx response means the whole request was rejected and no changes were applied. The list route also returns HTTP 200 with an errorMessage row when a linked team cannot load configuration.
Organization Groups
Organization groups organize members across teams linked to the same organization. For dashboard setup and group-level controls, see Organization Groups.
- Authentication: Organization API key (Basic auth). Read routes require the
members:*scope. Write routes also requiremembers:*. Keys withadmin:*also work because admin implies members. - Group IDs: Organization group IDs use the
g_prefix. - Pagination: List routes accept
pageandpageSize. Both values must be positive integers.
List Organization Groups
/organizations/groupsRetrieve organization groups for the organization attached to your API key.
Query parameters
page number
pageSize number
curl -X GET "https://api.cursor.com/organizations/groups?page=1&pageSize=50" \ -u YOUR_ORGANIZATION_API_KEY:Response:
{ "groups": [ { "id": "g_PDSPmvukpYgZEDXsoNirw3CFhy", "name": "Engineering", "createdAt": "2026-01-15T10:30:00.000Z", "updatedAt": "2026-01-20T14:22:00.000Z" }, { "id": "g_kljUvI0ASZORvSEXf9hV0ydcso", "name": "Design", "createdAt": "2026-01-16T09:00:00.000Z", "updatedAt": "2026-01-16T09:00:00.000Z" } ], "pagination": { "page": 1, "pageSize": 50, "totalCount": 2, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false }}Get Organization Group
/organizations/groups/:groupIdRetrieve one organization group.
Parameters
groupId string Required
g_ prefix.curl -X GET https://api.cursor.com/organizations/groups/g_PDSPmvukpYgZEDXsoNirw3CFhy \ -u YOUR_ORGANIZATION_API_KEY:Response:
{ "group": { "id": "g_PDSPmvukpYgZEDXsoNirw3CFhy", "name": "Engineering", "createdAt": "2026-01-15T10:30:00.000Z", "updatedAt": "2026-01-20T14:22:00.000Z" }}List Organization Group Members
/organizations/groups/:groupId/membersRetrieve members in an organization group.
Parameters
groupId string Required
g_ prefix.Query parameters
page number
pageSize number
curl -X GET "https://api.cursor.com/organizations/groups/g_PDSPmvukpYgZEDXsoNirw3CFhy/members?page=1&pageSize=50" \ -u YOUR_ORGANIZATION_API_KEY:Response:
{ "members": [ { "userId": "user_abc123", "name": "Alex Developer", "email": "alex@company.com", "joinedAt": "2026-01-15T10:30:00.000Z" }, { "userId": "user_def456", "name": "Sam Engineer", "email": "sam@company.com", "joinedAt": "2026-01-16T09:15:00.000Z" } ], "pagination": { "page": 1, "pageSize": 50, "totalCount": 2, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false }}Add Organization Group Members
/organizations/groups/:groupId/members/bulk-addAdd members to an organization group.
Parameters
groupId string Required
g_ prefix.Request body
userIds string[] Required
user_ prefix. A single request may include up to 100 users.curl -X POST https://api.cursor.com/organizations/groups/g_PDSPmvukpYgZEDXsoNirw3CFhy/members/bulk-add \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "userIds": ["user_abc123", "user_def456"] }'Response:
{ "addedCount": 2}Remove Organization Group Members
/organizations/groups/:groupId/members/bulk-removeRemove members from an organization group.
Parameters
groupId string Required
g_ prefix.Request body
userIds string[] Required
user_ prefix. A single request may include up to 100 users.curl -X POST https://api.cursor.com/organizations/groups/g_PDSPmvukpYgZEDXsoNirw3CFhy/members/bulk-remove \ -u YOUR_ORGANIZATION_API_KEY: \ -H "Content-Type: application/json" \ -d '{ "userIds": ["user_def456"] }'Response:
{ "removedCount": 1}