Where does the bug appear (feature/product)?
Somewhere else…
Describe the Bug
The Cursor Admin API includes fields in response schemas that document how removed users should be represented. However, when users are removed from an organization, the actual API responses exclude critical identifying information for those users’ historical data:
-
/teams/membersendpoint: The documentation specifies anisRemovedboolean field that should indicate whether a team member has been removed. However, the endpoint only returns currently active members and completely omits removed users from the response, making it impossible to distinguish between users who were never members and users who have been removed. -
/teams/filtered-usage-eventsendpoint: The documentation listsuserEmailas a required response field for all usage events. However, when querying historical usage events for users who have subsequently been removed, theuserEmailfield is missing from the response, even though the events were generated while the user was active.
This creates data integrity issues when attempting to build a monthly report of our teams usage as historical usage data becomes incomplete and difficult to attribute after user removal
Steps to Reproduce
Prerequisites
- Team with Admin API access and an API key (API overview)
- A test user who can be removed without violating team minimums (at least one paid member and one admin remain)
jq,curl, and permission to remove a member
Replace placeholders: YOUR_API_KEY, REMOVED_USER_EMAIL, ACTIVE_USER_EMAIL, date range epoch ms.
1. Establish baseline while user is active
export API_KEY="YOUR_API_KEY"
export USER_EMAIL="REMOVED_USER_EMAIL"
# Member directory
curl -sS -u "$API_KEY:" https://api.cursor.com/teams/members \
| jq --arg e "$USER_EMAIL" '.teamMembers[] | select(.email | ascii_downcase == ($e | ascii_downcase))'
# Usage in a recent window (adjust dates to your billing period)
curl -sS -u "$API_KEY:" https://api.cursor.com/teams/filtered-usage-events \
-H "Content-Type: application/json" \
-d "{
\"startDate\": 1746057600000,
\"endDate\": 1748735999999,
\"email\": \"$USER_EMAIL\",
\"page\": 1,
\"pageSize\": 10
}" | jq '{total: .totalUsageEventsCount, sample: .usageEvents[0]}'
Note: Record whether the member’s id is a number or a user_… string, and whether sample events include any user id field.
2. Remove the user
curl -sS -u "$API_KEY:" https://api.cursor.com/teams/remove-member \
-H "Content-Type: application/json" \
-d "{\"email\": \"$USER_EMAIL\"}" | jq .
Record userId from the response.
3. Re-check member directory
curl -sS -u "$API_KEY:" https://api.cursor.com/teams/members \
| jq --arg e "$USER_EMAIL" '
.teamMembers[]
| select(.email | ascii_downcase == ($e | ascii_downcase))
| {id, email, name, role, isRemoved}
'
Expected (per docs): One object with isRemoved: true and stable id.
Failure modes:
- No row returned for that email (user omitted from
teamMembers). isRemovedmissing orfalseafter removal.idtype/shape unlikeuserIdfrom step 2.
4. Fetch historical usage for the same period
curl -sS -u "$API_KEY:" https://api.cursor.com/teams/filtered-usage-events \
-H "Content-Type: application/json" \
-d "{
\"startDate\": 1746057600000,
\"endDate\": 1748735999999,
\"email\": \"$USER_EMAIL\",
\"page\": 1,
\"pageSize\": 25
}" | jq '{
total: .totalUsageEventsCount,
hasUserIdOnEvents: ([.usageEvents[] | has("userId")] | any),
emails: ([.usageEvents[].userEmail] | unique)
}'
Expected: Events still returned for pre-removal activity; each event carries userId or step 3 provides a joinable member row.
Failure mode: Events exist (non-zero totalUsageEventsCount) but step 3 did not return a member—client cannot map email → stable id.
5. Attempt userId filter using member id
Using the id from step 3 (or userId from step 2):
export MEMBER_ID="user_PDSPmvukpYgZEDXsoNirw3CFhy" # example encoded id
curl -sS -u "$API_KEY:" https://api.cursor.com/teams/filtered-usage-events \
-H "Content-Type: application/json" \
-d "{
\"startDate\": 1746057600000,
\"endDate\": 1748735999999,
\"userId\": \"$MEMBER_ID\",
\"page\": 1,
\"pageSize\": 10
}" | jq '{total: .totalUsageEventsCount, error: .error}'
Expected: Filter accepts the same identifier shape as /teams/members and returns the same event set as the email filter (for that user’s historical window).
Failure modes: 400/empty results when passing encoded string ids; only numeric ids accepted despite live members returning strings.
6. Control: active member still enriches
Repeat steps 1 and 4 for ACTIVE_USER_EMAIL who remains on the team. Compare whether user_id resolution succeeds for active but not removed users—indicates a removed-member-specific directory gap rather than a generic integration bug.
Expected Behavior
Directory (GET /teams/members)
GIVEN a user was previously on the team and accrued usage in the current or a past billing period
AND the user was later removed (via admin UI or POST /teams/remove-member)
WHEN a client calls GET /teams/members with a valid team Admin API key
THEN the user appears in teamMembers
AND isRemoved is true
AND id is the same stable identifier used elsewhere in the Admin API (encoded user_… form, as used in audit-log user filters and billing groups)
AND email matches the userEmail on that user’s historical usage events (after normalising case and surrounding whitespace).
Usage events (POST /teams/filtered-usage-events)
GIVEN a date range that includes activity from a since-removed team member
WHEN a client calls POST /teams/filtered-usage-events for that range (with or without email / userId filters)
THEN events for that member are returned with the same userEmail as when they were active
AND each event includes a stable userId or the documentation guarantees that /teams/members always returns a joinable row for that email (including isRemoved: true).
Cross-endpoint consistency
GIVEN the Admin API documents userId as a filter on /teams/filtered-usage-events
WHEN a client obtains id from GET /teams/members
THEN that value is accepted by the userId filter and matches the identifier returned from POST /teams/remove-member (userId in the remove-member response).
Operating System
MacOS
Version Information
Cursor Admin API: 2026-05-26
Does this stop you from using Cursor
No - Cursor works, but with this issue