Users
List and inspect end users in your project, including their streaks, entitlements, and event history.
These endpoints support searching users, filtering by segment, and drilling into a single user's gamification + entitlement + event state.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects/:projectId/users | Admin-create an app user (seed / migration attribution). |
| GET | /admin/projects/:projectId/users | List / search users, optionally filtered by segment. |
| GET | /admin/projects/:projectId/users/:userId | Fetch one user with streaks + entitlements. |
| GET | /admin/projects/:projectId/users/:userId/events | Paginated engagement-event history for a user. |
| GET | /admin/projects/:projectId/users/export | Stream all users as CSV/NDJSON. |
| GET | /admin/projects/:projectId/users/:userId/events/export | Stream a single user's events as CSV/NDJSON. |
| GET | /admin/projects/:projectId/users/:userId/export | Stream one user's GDPR data bundle (NDJSON): profile, records, events. |
| POST | /admin/projects/:projectId/users/bulk-update | Apply property + segment updates to up to 1000 users. |
| POST | /admin/projects/:projectId/users/cohort | Mint N anonymous users sharing properties (and optional segments). |
| POST | /admin/projects/:projectId/users/:userId/entitlements | Grant or refresh a user's entitlement (Stripe / Paddle / manual). |
| DELETE | /admin/projects/:projectId/users/:userId/reset | Reset one user's state to zero (optionally also delete the user). |
| DELETE | /admin/projects/:projectId/users/:userId | Delete one user and every row that belongs to them. |
| DELETE | /admin/projects/:projectId/users?anonymous_id=… | Delete one user by anonymous_id. |
POST /admin/projects/:projectId/users
Admin-create an app user from the developer / agent side. Use this to mint a
system / seed user to attribute migrated rows to — for example after resetting a
sandbox. All fields are optional; with no fields it mints an anonymous user (a
random anonymous_id is generated so the row is a valid, usable anonymous user rather
than an identity-less orphan).
Request
| Field | Type | Required | Description |
|---|---|---|---|
external_id | string | no | Your stable external identifier (unique). |
anonymous_id | string | no | Anonymous device id (unique). Auto-generated if no identity given. |
email | string | no | Email address. |
phone | string | no | Phone number. |
display_name | string | no | Display name. |
avatar_url | string | no | Avatar URL. |
properties | object | no | Arbitrary user properties. |
Response 201
The created user row (id, email, phone, display_name, avatar_url,
external_id, anonymous_id, auth_providers, properties, timestamps).
Errors
400 INVALID_BODY— body isn't a JSON object, orpropertiesisn't an object.409 USER_ALREADY_EXISTS— a user with the sameexternal_id,anonymous_id, oremailalready exists.500 CREATE_FAILED.
Over MCP this is amba_users_create.
Curl:
GET /admin/projects/:projectId/users
List users, newest-active first. Supports limit, offset, optional search across email / external_id / display_name (ILIKE, metacharacters escaped), and optional segment_id to filter to members of a segment.
Query
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Page size. |
offset | int | 0 | Offset. |
search | string | — | ILIKE match on email / external_id / display_name. |
segment_id | uuid | — | Only users in this segment. |
Response 200
Errors
500 LIST_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/userscurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users'Curl:
GET /admin/projects/:projectId/users/:userId
Fetch a single user with their current streaks (each inlined with its streak-definition name and qualifying_event) and entitlements.
Response 200
Errors
404 NOT_FOUND— user does not exist.500 FETCH_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7Dcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D'Curl:
GET /admin/projects/:projectId/users/:userId/events
Paginated event history for a user, most recent first.
Query
| Param | Default | Description |
|---|---|---|
limit | 50 | Page size. |
offset | 0 | Offset. |
Response 200
Errors
500 LIST_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/eventscurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/events'Curl:
GET /admin/projects/:projectId/users/export
Stream every user in the your database as CSV (default) or NDJSON. Streamed via a streaming cursor with batch size 500 — no full result set in memory. Each row includes id, email, anonymous_id, created_at, last_seen_at, properties, platform_strings (pipe-joined for CSV), and push_token_count.
Query
| Param | Type | Default | Description |
|---|---|---|---|
format | csv | ndjson | csv | Output format. |
since | ISO-8601 | — | Optional lower bound on created_at. |
Response 200
Content-Type: text/csv; charset=utf-8 or application/x-ndjson; charset=utf-8. CSV includes a header row; NDJSON emits one JSON object per line.
Errors
400 INVALID_SINCE—sinceis not a parseable ISO-8601 timestamp.502 TENANT_UNAVAILABLE— your database validation failed before the stream opened.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/exportcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/export'Curl:
GET /admin/projects/:projectId/users/:userId/events/export
Stream a single user's engagement events as CSV (default) or NDJSON. The user's existence is verified before the response stream commits to a 200 — a missing user returns the standard JSON error envelope.
Query
| Param | Type | Default | Description |
|---|---|---|---|
format | csv | ndjson | csv | Output format. |
since | ISO-8601 | — | Lower bound on occurred_at. |
until | ISO-8601 | — | Upper bound on occurred_at. |
event_name | string | — | Filter to a specific event_name. |
Response 200
Content-Type: text/csv; charset=utf-8 or application/x-ndjson; charset=utf-8. Columns: id, app_user_id, event_name, properties, occurred_at.
Errors
400 INVALID_SINCE/INVALID_UNTIL— unparseable timestamps.400 INVALID_USER_ID—userIdis not a UUID.404 NOT_FOUND— user does not exist.500 FETCH_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/events/exportcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/events/export'Curl:
GET /admin/projects/:projectId/users/:userId/export
Stream a single user's data bundle as NDJSON. This is the canonical "GDPR right-to-access" export: the user's profile, one section per kind of record the project keeps about the user (listed under Sections), and a closing export_complete line. Every section streams through a database cursor in batches of 500 rows with no row cap, so a power user with millions of events does not exhaust the API's memory.
The user's existence is verified before the response stream commits to a 200, so a missing user returns the standard JSON error envelope rather than a truncated stream.
Output format
NDJSON only (no CSV variant — the bundle is sectioned and CSV cannot model nested sections cleanly). Each line is a JSON object of shape:
Lines of the collections section add a collection key with the collection's name. The last line is the closing line.
Content-Type: application/x-ndjson; charset=utf-8. Content-Disposition: attachment; filename="user-<userId>-export.ndjson".
Sections (in emit order)
Each section is named after the table its rows come from. Each line carries the stored row with every column, except the credential columns listed under Left out.
| Section | The user's rows |
|---|---|
app_users | The profile, one line: every column (email, phone, timezone, email_verified, phone_verified, …) except password_hash. |
account_links | Anonymous ids linked to the account. |
push_tokens | Device tokens registered for push. |
user_entitlements | Subscription and paid-grant rows. |
segment_memberships | Segments the user belongs to. |
user_streaks | One row per streak the user has started. |
streak_events | Streak state transitions, oldest first. |
user_achievements | Unlocked and in-progress achievements. |
leaderboard_entries | Rank rows across leaderboards. |
app_sessions | App sessions the SDK reported: platform, app version, device model, IP address. |
app_user_sessions | Sign-in sessions: IP address, user agent, expiry, revocation. |
magic_link_tokens | Magic links issued to the user, plus links requested for the user's email address before the account existed. |
email_otp_challenges | One-time email codes requested for the user's email address. |
sms_otp_challenges | One-time SMS codes requested for the user's phone number. |
sms_outbound_messages | Texts sent to the user's phone number, with their delivery status. |
user_xp | XP total and level. |
xp_ledger | Every XP grant and deduction. |
user_challenges | Challenge progress. |
league_memberships | League cohorts the user was placed in. |
user_balances | Currency balances. |
currency_transactions | Every currency movement. |
user_inventory | Items the user holds. |
currency_grant_ledger | Currency the project's grant rules gave the user. |
entitlement_cascade_ledger | Rewards that fired when the user was granted an entitlement. |
agent_checkouts | Agent-initiated checkouts made for the user. |
user_onboarding | Onboarding progress and step data. |
user_roles | Roles assigned to the user. |
user_trust_levels | Moderation trust level. |
sync_checkpoints | Offline-sync checkpoints. |
experiment_assignments | Experiment variants assigned to the user. |
experiment_exposures | Experiment exposures logged for the user. |
push_deliveries | Push notifications sent to the user, with their status. |
push_topic_subscriptions | Push topics the user subscribed to. |
friendships | Friendships on either side; each line carries the other person's user id (see Shared spaces for blocks). |
groups | Groups the user owns. |
group_members | The user's own group memberships. |
conversation_participants | The user's own conversation memberships. |
messages | Messages the user sent. |
message_attachments | Attachments on the messages the user sent. |
message_reactions | Reactions the user left. |
feed_items | Feed items the user's actions created. |
reviews | Reviews the user wrote. |
moderation_queue | Reports the user filed, with their outcome. |
referral_codes | The user's referral codes. |
referral_claims | Claims on either side: someone used the user's code, or the user used someone else's. |
link_clicks | Tracked-link clicks. ip_address is null: clicks store no IP address, and the database update that shipped with that change clears earlier ones. |
affiliate_touchpoints | Affiliate code binds and link clicks recorded under the user's id, or under an anonymous id that only this user holds. |
content_items | Content items the user authored. |
media_assets | Files the user uploaded; each line carries the file's storage_url. |
email_deliveries | Emails sent to the user, by user id or to the user's email address. |
email_suppressions | The suppression record of the user's email address (bounce, complaint or manual). |
audit_events | Audit events recorded for the user. |
_amba_write_idempotency | Idempotency keys sent with the user's collection inserts, with the collection and the id of the row each one created. |
collections | The user's rows in every collection (see Collection rows). |
engagement_events | The user's engagement events, oldest first. |
telemetry_events | Events tracked with { telemetry: true }, oldest first. The last data section. |
export_complete | The closing line. |
Rows keyed to an address (magic_link_tokens requested before the account existed, email_otp_challenges, sms_otp_challenges, sms_outbound_messages, email_deliveries sent to the address alone, email_suppressions) match the user's current email address or phone number when the user has proven it: email_verified or phone_verified is true on the profile (a profile marked legacy_password has an unproven email). Two accounts can hold the same unproven address, and that address's sign-in codes and mail belong to whoever proved it. The one-time codes and texts are stored under a hash of the address, and the bundle matches them with the same hash. agent_checkouts comes from the project's account records; every other section comes from the project's database.
A section the user has no rows in contributes zero data lines and a 0 in the closing line. A table the project's database has not created yet (a migration still to apply) also contributes zero lines, with no marker.
Each event line carries the stored row: id, app_user_id, event_name, properties ({} when the event was tracked without properties), and occurred_at (the timestamp sent with the event, or the time the API received it). Engagement events add external_id (the event_id idempotency key a server-side track call sent, or null) and received_at (when the event was recorded, or null on older events). Telemetry events add ingested_at (when the API stored the event). The two event sections can hold the most rows, so they come last among the data sections, telemetry after engagement.
Shared spaces
In conversations, groups and feeds the bundle carries what the user wrote and the user's own membership rows: the user's messages, the attachments on them, the user's reactions, the user's conversation_participants and group_members rows, and the feed items the user's actions created. Each line carries the conversation_id, group_id or parent_message_id it belongs to as context. Other people's messages, reactions and memberships in the same spaces are their data and appear in their own exports. A blocked friendship in which the user is the addressee is left out: the row records no blocker, and when someone else blocked the user it names that person, whose choice the app keeps from the person blocked. Blocks the user made, with the user as requester, are exported.
Collection rows
One line per row whose user_id is the user, in every collection, with every column. In a group-owned collection the group owns the row and user_id names the member who created it, so the section carries the rows the user created. Soft-deleted rows are still stored, so they are exported too, with their deleted_at. A row another user owns that points at this user through a reference column you declared is that other user's row and appears in their export.
Left out
The bundle leaves out five credential columns: password_hash (app_users), refresh_token_hash (app_user_sessions), token_hash (magic_link_tokens), and code_hash (email_otp_challenges, sms_otp_challenges). Each is a hash that proves possession of a password, a session, a sign-in link or a one-time code, and a copy in a file would let whoever holds the file attack it offline. Every other column is the user's data and is exported.
Closing line
The last line of a whole bundle:
sections— the data lines written per section, every section listed (0when the user has none).total_rows— their sum, the profile line included.failed_sections— the sections that wrote a partial-export marker.generated_at— when the API finished writing the bundle.
A bundle cut off by a dropped connection or the API's request time limit ends without this line; request the export again. A bundle that ends with export_complete and an empty failed_sections holds every row the sections above match, and each section's line count equals its entry in sections. Address-keyed rows are matched on the user's current, proven address, so rows sent to an address the user held earlier are outside the bundle.
Partial-export markers
If a single section's cursor errors mid-stream (e.g. a transient DB blip), the bundle carries on: the handler logs server-side and emits one marker line (a failed collection read adds its collection key):
Rows already streamed before the marker are valid; the bundle continues with the next section, and the closing line lists the section in failed_sections. Treat the marker as "this section is partial up to this line".
Response 200
Sample (truncated for clarity):
Errors
400 INVALID_USER_ID—userIdis not a valid UUID.404 NOT_FOUND— user does not exist.500 EXPORT_FAILED— pre-stream lookup failed (DB unreachable, etc).502 TENANT_UNAVAILABLE— your database resolution failed before the stream opened.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/exportcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/export'Curl:
POST /admin/projects/:projectId/users/bulk-update
Apply property merges and segment add/remove to up to 1000 users in a single transactional call. Properties are merged with jsonb concatenation (top-level merge, mirrors track() semantics). Segment add uses ON CONFLICT DO NOTHING, so it is idempotent. The whole request runs in one transaction — partial failures cannot leave a user with merged properties but missing segment tags.
Request
| Field | Type | Required | Description |
|---|---|---|---|
user_ids | string[] | yes | App-user UUIDs (1-1000). |
updates.properties | object | conditional | Top-level keys merged into each user's properties. |
updates.add_segment_ids | string[] | conditional | Segment UUIDs to add (idempotent). |
updates.remove_segment_ids | string[] | conditional | Segment UUIDs to remove. |
At least one of properties, add_segment_ids, remove_segment_ids must be present.
Response 200
failed lists user_ids that did not exist in the your database; nothing is updated for those rows. The transaction still commits — non-existent users do not block the rest.
Errors
400 INVALID_BODY— body is not JSON.400 MISSING_USER_IDS—user_idsempty or absent.400 INVALID_USER_IDS—user_idscontains non-strings.400 BATCH_TOO_LARGE— more than 1000 user_ids;error.detailsincludeslimitandsupplied.400 EMPTY_UPDATES— none ofproperties,add_segment_ids,remove_segment_idsset.400 INVALID_UUID— the database rejected one of the supplied IDs as a malformed UUID.500 BULK_UPDATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/bulk-updatecurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/bulk-update' \
-H 'Content-Type: application/json' \
-d '{
"user_ids": [
"<app_user_id>"
],
"updates": {
"properties": {
"plan": "pro"
}
}
}'Curl:
POST /admin/projects/:projectId/users/:userId/entitlements
Grant or refresh a user's entitlement directly from a server-side caller. This is the path to use for any flow that does not come through a RevenueCat or Superwall webhook — Stripe / Paddle web checkout, manual support grants, gift codes, ToS-violation revokes, and migrations from legacy systems.
For mobile in-app purchases, prefer the RevenueCat webhook — it is push-only and remains the source of truth for App Store / Play Store subscriptions.
Upserts on (user, entitlement_id), so calling this endpoint a second time for the same pair refreshes the existing entitlement in place — matching the RevenueCat webhook's semantics.
Update semantics
For every nullable column, the handler tracks both the value and whether the caller actually supplied the field. This lets the upsert distinguish three cases on refresh:
| Caller behaviour | Effect on existing row |
|---|---|
| Field omitted from request body | Preserve the existing column value. |
Field supplied as null | Clear the column to NULL. |
| Field supplied as a value | Set the column to that value. |
is_active is always written from the body (defaulting to true when omitted on a fresh grant). raw_data is NOT NULL DEFAULT '{}' at the schema level, so passing null resets it to {} rather than SQL NULL.
Request
| Field | Type | Required | Description |
|---|---|---|---|
entitlement_id | string | yes | Logical identifier (e.g. "premium"). Non-empty. |
is_active | boolean | no | Defaults to true on a fresh grant. |
product_id | string | null | no | Store product identifier. null clears. |
store | string | null | no | One of app_store, play_store, stripe. null clears. |
purchase_date | string | null | no | ISO-8601 timestamp. null clears. |
expiration_date | string | null | no | ISO-8601 timestamp. null clears (treated as lifetime). |
period_type | string | null | no | One of trial, intro, normal. null clears. |
raw_data | object | null | no | Opaque JSON for audit (the source receipt, the support ticket id, etc). null clears to {}. |
Response 200
The status is always 200, including the first-grant case (the upsert returns the inserted or updated row uniformly).
Errors
400 INVALID_BODY— body is not JSON.400 INVALID_ENTITLEMENT_ID—entitlement_idis missing or empty.400 INVALID_PRODUCT_ID—product_idis not a string or null.400 INVALID_STORE—storeis not one ofapp_store,play_store,stripe(or null).400 INVALID_PERIOD_TYPE—period_typeis not one oftrial,intro,normal(or null).400 INVALID_PURCHASE_DATE—purchase_dateis not parseable ISO-8601.400 INVALID_EXPIRATION_DATE—expiration_dateis not parseable ISO-8601.400 INVALID_RAW_DATA—raw_datais not an object or null (arrays / scalars rejected).400 INVALID_USER_ID—userIdis not a valid UUID.404 USER_NOT_FOUND— noapp_userwith the givenuserIdin this project.500 GRANT_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/entitlementscurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/entitlements'Curl — fresh Stripe grant:
Curl — revoke (e.g. ToS violation):
Curl — clear an expiration_date (extend to lifetime):
POST /admin/projects/:projectId/users/cohort
Mint N anonymous users in one call, all stamped with the same properties
(and optionally enrolled into the same segments). The fast path for seeding a
test cohort, populating a leaderboard, or dry-running a push segment —
POST /users mints one user, bulk-update mutates existing ones, this fills
the "create 50 users tagged cohort: beta" gap. Each minted user is
anonymous (a random anonymous_id), since a shared-properties cohort has no
per-user external identity.
Request
| Field | Type | Required | Description |
|---|---|---|---|
count | integer | yes | How many users to mint. 1–500. |
properties | object | no | Stamped identically onto every minted user. |
segment_ids | uuid[] | no | Segment UUIDs to enrol every minted user into. |
Response 201
Each minted user fires a user.created event. A user.created subscriber
sees cohort: true in the event data.
Errors
400 INVALID_BODY— body isn't a JSON object, orproperties/segment_idshave the wrong shape.400 INVALID_COUNT—countis not an integer in 1–500.400 INVALID_BODY— asegment_idreferences a segment that doesn't exist.500 COHORT_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/cohortcurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/cohort' \
-H 'Content-Type: application/json' \
-d '{
"count": 25,
"properties": {
"cohort": "beta"
}
}'Curl:
DELETE /admin/projects/:projectId/users/:userId/reset
Reset one user back to a brand-new state — clear their gamification,
economy, engagement, participation, and session state — without deleting the
user. Their identity (email, anonymous_id, properties) survives. This is the
per-user analogue of the all-or-nothing sandbox reset, so resetting a probe
user doesn't nuke a deliberately-seeded cohort.
Developer-authored configuration (currency / achievement / catalog definitions and other project config) is never touched — a reset returns the user to zero, it doesn't reconfigure the project. The whole sweep runs in one transaction: a mid-sweep failure leaves the user's state intact.
Query
| Param | Type | Default | Notes |
|---|---|---|---|
delete | boolean | false | ?delete=true also removes the app_users row at the end. |
Response 200
tables_cleared maps each touched table to the number of rows removed (tables
with nothing to clear are omitted). A reset emits user.reset; a
?delete=true reset emits user.deleted instead. A state-only reset keeps the
user's media, tracked-link clicks, and affiliate touchpoints. A
?delete=true reset purges the media, clears the user id on the clicks, and
clears the anonymous id on the affiliate touchpoints like
DELETE /admin/projects/:projectId/users/:userId, and adds the same
media_purge field to the response.
Errors
400 INVALID_USER_ID—userIdis not a valid UUID.404— no such user (an already-deleted id).503 MEDIA_PURGE_UNAVAILABLE—?delete=trueonly: the media purge could not be scheduled, so nothing was reset or deleted. Retry the call.500 RESET_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/resetcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/reset'Curl:
DELETE /admin/projects/:projectId/users/:userId
Permanently delete one user, for example to honor an account-deletion request. In one transaction the delete removes the user with their sessions, push tokens, segment memberships, XP, achievements, streaks, balances, inventory, entitlements, friendships, group memberships, the messages they sent, and all of their events, engagement and telemetry alike. After the delete, none of the user's events appear in event lists, counts, analytics, funnels, or exports.
The delete also reaches rows other users share with them: groups the user owns
are deleted with their members. Replies other users wrote to the user's messages
stay. Each such reply keeps its body, sender, attachments, and other users'
reactions, and its parent_message_id becomes null, so it reads as a
top-level message in the conversation. The user's own replies to other users' messages are deleted with
the rest of their messages. Some records stay. Content items the user owns,
referral codes, and moderation reports the user filed keep their row with the
user's id cleared. The user's affiliate touchpoints (the affiliate link clicks
and code binds recorded through POST /v1/client/affiliate/touchpoint) stay
for the affiliate program's click counts: the delete clears the anonymous id
each one was recorded under (the user's own and any linked to the user) and
keeps its affiliate code, source, and time. The user's tracked-link clicks stay
as anonymous clicks: the delete clears each click's user id and keeps its
link, platform, and time (clicks store no IP address), so the link's total_clicks and
clicks_by_platform stay the same and unique_users counts the users who
still exist (see
link stats).
The email delivery log and audit events keep the user's id. A delete emits
user.deleted.
The delete also purges the media the user uploaded, in every bucket. In the same transaction the user's assets leave every media read and media usage, and the media catalog entries that point at them are removed. Right after the commit each asset's stored file is deleted, then its record. A file that cannot be deleted at once (a storage error) is retried in the background until it is gone, and an asset registered in the six hours before the delete has its file deleted again six hours after registration, which also removes the file of an upload still streaming at the delete that finishes within that time; the user delete itself completes either way. The user's session can no longer get an upload URL. Assets the project uploaded and other users' assets stay. See Cascade on user delete.
To delete by the anonymous id your app holds, call
DELETE /admin/projects/:projectId/users?anonymous_id=<id>. It removes the same
rows.
Response 200
mau_count is the project's monthly active users after the delete. mau_cap is
omitted on plans without a cap. The anonymous_id form also returns
deleted_anonymous_id.
media_purge describes the media purge: assets is the number of media
assets the user had uploaded, purged the assets whose stored file and record
are gone, and pending the assets the background retry still has to finish.
Pending assets are already hidden from every read.
Errors
400 INVALID_USER_ID—userIdis not a valid UUID.400 MISSING_IDENTIFIER— theanonymous_idform was called withoutanonymous_id.404 USER_NOT_FOUND— no such user (an already-deleted id).503 MEDIA_PURGE_UNAVAILABLE— the media purge could not be scheduled, so nothing was deleted. Retry the delete.500 DELETE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7Dcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D'Curl: