Skip to content

Users & Profiles

apps/api/src/router/userRoutes.ts and followRoutes.ts. Every endpoint requires authentication.

Current user

MethodPathPurpose
GET/userThe authenticated user
GET/meAlias for /user
PUT/userUpdate profile fields
POST/user/avatarUpload avatar (multipart/form-data)
POST/user/bannerUpload banner (multipart/form-data)

Both aliases exist because the mobile app and the web app were written against different names. Prefer /me.

Profiles

MethodPathPurpose
GET/profileYour own full profile
GET/profile/:idAnother user's profile
PUT/profile/usernameChange username
PUT/profile/display-nameChange display name
PUT/profile/bioChange bio
PUT/profile/priceChange monthly subscription price (cents)

Each profile field has its own endpoint rather than one PATCH, which mirrors the mobile app's one-field-per-screen editing flow (app/(app)/(tabs)/me/edit/).

PUT /profile/price sets User.monthlyPrice, the amount fans pay for a creator subscription and the figure anansi multiplies by the active subscriber count at payout time.

Following

MethodPathPurpose
POST/user/:id/followFollow a user
POST/user/:id/unfollowUnfollow

Follow has @@unique([followerId, followingId]), so a duplicate follow cannot create a second row. Following triggers a NEW_FOLLOWER notification.

Security & sessions

MethodPathPurpose
GET/user/sessionsActive login sessions
GET/user/login-historyHistorical logins
POST/auth/logout-allRevoke every token
PUT/auth/change-passwordChange password
DELETE/user/accountDelete the account

/user/sessions and /user/login-history both read AuthSession rows — device name, IP, user agent, timestamp. Remember that deactivating a session row does not revoke anything on its own; only /auth/logout-all does. See Authentication.

DELETE /user/account has to unwind relations explicitly, because most foreign keys restrict rather than cascade. Verify what it actually removes before treating it as a GDPR erasure path.

Blocking

MethodPathPurpose
GET/user/blockedList blocked users
POST/user/block/:userIdBlock
DELETE/user/block/:userIdUnblock

Blocks are stored in the Block model with a unique constraint on the pair. Feed, search, and messaging queries are expected to filter against it — check the specific query if you are relying on that.

Notification preferences

MethodPathPurpose
GET/user/notificationsRead preferences
PUT/user/notificationsUpdate preferences

Note the collision with the notification inbox at /notifications/user/notifications is preferences, /notifications is the list. Easy to confuse. Fields are documented in Notifications.

Push tokens

MethodPathPurpose
POST/user/push-tokenRegister an Expo push token
DELETE/user/push-tokenRemove one

Tokens are validated with Expo.isExpoPushToken before use, and automatically removed when Expo reports DeviceNotRegistered.

Games

MethodPathPurpose
GET/gamesThe game catalog
PUT/user/gamesSet favourite games

The catalog is populated by igdb-heartbeat. Favourites are chosen during onboarding and feed into discovery.

Verification

MethodPathPurpose
POST/verification-assetUpload an identity document
POST/verify-identitySubmit for review

Part of creator onboarding.

Internal documentation — PlayPalz platform