Users (REST)
Read and search anonymous and identified users, update their saved properties, or schedule privacy exports and deletions. List, profile, timeline and privacy operations use a WorkOS access token and the required workspace role. Property updates use the separate SDK or scoped backend authentication described below.
List
GET /v1/apps/:appId/users
Authorization: Bearer <workos_access_token>
# Query params
?q=[email protected]
&limit=50
&cursor=cur_xyz{
"data": {
"items": [
{
"subjectId": "0198d5c3-…",
"anonymousId": "device_a1b2c3",
"anonymousIds": ["device_a1b2c3"],
"externalId": "user_42",
"firstSeenAt": "2026-01-12T00:00:00.000Z",
"lastSeenAt": "2026-05-13T09:11:00.000Z",
"eventCount": 42,
"topProperties": { "plan": "pro" },
"country": "DE",
"platform": "ios"
}
],
"total": 1,
"nextCursor": null
}
}Get one
GET /v1/apps/:appId/users/:anonymousId
Authorization: Bearer <workos_access_token>Returns the user profile + device list + most-recent properties.
Timeline
GET /v1/apps/:appId/users/:anonymousId/events?limit=100&cursor=…Returns paginated events for that user.
Update user properties
For the active app user, prefer the React Native or web SDK's
setUserProperties(values, unsetKeys?). Its endpoint requires both the public
write key and the current subject token:
POST /v1/sdk/user-properties
Authorization: Bearer <write_key>
X-UserGist-Subject-Token: <subject_token>
Content-Type: application/json
{
"anonymousId": "device_a1b2c3",
"mutationId": "00000000-0000-4000-8000-000000000002",
"set": { "first_name": "Ava", "country": "IL" },
"unset": ["old_plan"]
}The anonymous alias must belong to the authenticated subject. This works for anonymous and identified users and requires analytics consent.
For backend-owned state, such as a currently eligible resume target, use an API
token with users.properties.write in the app's workspace:
PATCH /v1/apps/:appId/users/properties
Authorization: Bearer <api_token_with_users.properties.write>
Content-Type: application/json
{
"subject": { "externalId": "user_42" },
"update": {
"mutationId": "00000000-0000-4000-8000-000000000003",
"set": {
"resume_show_id": "00123",
"resume_show_title": "Midnight Orbit",
"resume_position_seconds": 1234
}
}
}Supply exactly one subject reference: externalId, anonymousId or subjectId.
The backend endpoint also enforces analytics consent and the app's privacy
allow-list. Keep its API token on your server.
Both forms use the same update contract:
mutationIdis a UUID. Reuse it with the same payload and alias for retries; reusing it with changed data returns a conflict.setaccepts at most 64 flat properties: strings up to 8,192 characters, finite numbers, booleans or null. Keys are literal, at most 120 characters.unsetaccepts at most 64 keys and explicitly removes those values. A key cannot appear in bothsetandunset; an empty update is rejected.- A successful response contains
data.appliedanddata.filteredKeys. A replay returnsapplied: false. InspectfilteredKeysfor values removed by privacy policy; do not assume every submitted property was stored.
Send related current-state values in one update. Null is treated as missing by
personalization; use unset when removing a stored property. See
Personalize messages for typed fields,
fallbacks and JSON destinations.
Privacy export
POST /v1/apps/:appId/gdpr/export
Authorization: Bearer <workos_access_token>
Content-Type: application/json
{ "externalId": "user_42" }Provide either externalId or anonymousId. This requires the admin role and
returns an exportId. Poll the status endpoint until it returns completed and
a short-lived download URL:
GET /v1/apps/:appId/gdpr/exports/:exportId
Authorization: Bearer <workos_access_token>Delete (GDPR)
POST /v1/apps/:appId/gdpr/delete
Authorization: Bearer <workos_access_token>
Content-Type: application/json
{ "anonymousId": "device_a1b2c3" }Provide either externalId or anonymousId. This requires the admin role and
queues an auditable deletion job. The job resolves every linked anonymous and
identified alias, removes operational records and push tokens, and does not
report completion until analytics-store deletion has succeeded.