Skip to main content

Settings — Technical Reference

← Back to Settings

Where this lives

Backend

Frontend

Technical implementation checklist

  • privacySettings/updatePrivacySettings/resetPrivacySettings + extendedPrivacySettings/updateExtendedPrivacySettings — both resolver pairs wired; AccountPrivacyPage.tsx fully wired to both
  • storyHiddenUsers/updateStoryHiddenUsers — wired; UI inside StoryLivePage.tsx
  • mediaPreferences/updateMediaPreferences — wired; replaces the old single mediaQuality string enum with 3 booleans
  • sharingSettings, hiddenWords, followersInvitesSettings + mutations — each wired independently; FollowersAndInvitesPage.tsx's invite channels beyond "share link" are UI-only "coming soon" rows
  • accountStandingStatus — wired, reads real user.isRestricted/user_warnings/user_moderation_logs
  • notificationSettings/updateNotificationSettings + extendedNotificationSettings/updateExtendedNotificationSettings — both wired
  • reelsTranslationSettings/updateReelsTranslationSettings — wired; separate JSONB key (settings.reels_translation) from the UI-language i18next setting
  • timeManagementSettings/usageStats/updateTimeManagementSettings/recordUsageHeartbeat — wired end-to-end including the heartbeat client
  • archivedPosts/recentlyDeletedPosts/archivePost/unarchivePost/restorePost/permanentlyDeletePost — wired; relies on Post.isArchived + Sequelize paranoid soft-delete
  • myLikedPosts/myComments/myReposts/watchHistory/searchHistory/linkHistory/notInterestedContent + mutations — wired; "Sticker responses"/"Reviews" intentionally unexposed (no backing model)
  • taggedPosts/pendingTagReviews/approvePostTag/rejectPostTag — wired; named distinctly from an older, unrelated, unfinished approveTag/rejectTag pair on the same Mutation type to avoid a field-name collision
  • Crossposting — fully wired (crosspost.type.js / crosspost.resolver.js): myCrosspostConnections, crosspostAuthorizeUrl, connectCrosspostAccount, disconnectCrosspostAccount, setCrosspostEnabled. OAuth to each provider is gated on server env (X_CLIENT_ID/FACEBOOK_APP_ID/TIKTOK_CLIENT_KEY …); CrosspostConnection.configured reflects whether the provider's app is set up. Auto-share on publish.
  • Restrict — fully wired (restrict.type.js / restrict.resolver.js / restrict.manager.js): restrictedAccounts, isAccountRestricted, restrictAccount, unrestrictAccount; persisted via the UserRestriction model (user_restriction table, restrictor_id/restricted_id). Enforced in post-comment.manager.js alongside Limit interactions.
  • Limit interactions — fully wired (interaction-limit.type.js / interaction-limit.resolver.js / interaction-limit.manager.js): myInteractionLimit, setInteractionLimit; persisted on user.limit_interactions_enabled/limit_interactions_scope/limit_interactions_until (migration 20260719030000-add-interaction-limits-to-user.js).
  • Insights & tools — InsightsAndToolsPage.tsx is wired, but to creator monetization analytics rather than the supervised-account tooling the name implies: myPostPurchaseEarnings, subscriberCount, subscriberRetention (pre-existing fields on post-purchase.resolver.js / user-subscription.resolver.js).
  • Family Center — frontend-only stub, no resolver/type/manager exists

Crossposting API

Lets a user link external social accounts (X, Facebook, TikTok) and auto-share new posts to them. OAuth apps are configured server-side; a provider only appears connectable when its credentials are present (configured: true).

type CrosspostConnection {
provider: String! # x | facebook | tiktok
connected: Boolean! # a token is stored (account linked)
enabled: Boolean! # auto-share to this provider is on
providerUsername: String
configured: Boolean! # the server has this provider's OAuth app set up
}

extend type Query {
myCrosspostConnections: [CrosspostConnection!]!
crosspostAuthorizeUrl(provider: String!, redirectUri: String!): String # null if not configured
}
extend type Mutation {
connectCrosspostAccount(provider: String!, code: String!, redirectUri: String!): Boolean! # finish OAuth
disconnectCrosspostAccount(provider: String!): Boolean!
setCrosspostEnabled(provider: String!, enabled: Boolean!): Boolean! # toggle auto-share
}

Flow: call crosspostAuthorizeUrl → send the user to the provider's OAuth consent → the callback returns a codeconnectCrosspostAccount(provider, code, redirectUri) stores the token → setCrosspostEnabled toggles auto-share. Env vars: X_CLIENT_ID/X_CLIENT_SECRET, FACEBOOK_APP_ID/FACEBOOK_APP_SECRET, TIKTOK_CLIENT_KEY/TIKTOK_CLIENT_SECRET (see environment setup).

Restrict & Limit interactions

Two distinct, user-controlled "soft moderation" tools, both enforced in post-comment.manager.js (a comment is hidden from the post owner if the commenter is either restricted or limited by the owner). Neither is related to block/mute or to admin moderation.

# Restrict — persisted via the UserRestriction model (user_restriction table)
type RestrictedAccount { user: User! restrictedAt: DateTime! }
query RestrictedAccounts($limit: Int, $offset: Int) { restrictedAccounts(limit: $limit, offset: $offset) { user { id username } restrictedAt } }
query IsAccountRestricted($userId: ID!) { isAccountRestricted(userId: $userId) }
mutation RestrictAccount($userId: ID!) { restrictAccount(userId: $userId) }
mutation UnrestrictAccount($userId: ID!) { unrestrictAccount(userId: $userId) }

# Limit interactions — persisted on user.limit_interactions_enabled/_scope/_until
type InteractionLimit { enabled: Boolean! scope: String! until: DateTime active: Boolean! }
query MyInteractionLimit { myInteractionLimit { enabled scope until active } }
mutation SetInteractionLimit($enabled: Boolean!, $scope: String, $durationDays: Int) {
setInteractionLimit(enabled: $enabled, scope: $scope, durationDays: $durationDays) { enabled until active }
}

durationDays sets until to now + N days (omit for indefinite); myInteractionLimit.active is true only while enabled and not yet expired. The only enforced scope today is non_followers.

Insights & tools

InsightsAndToolsPage.tsx (route /settings/insights-tools) is not supervised-account tooling despite its name — it's creator monetization analytics, reusing pre-existing fields rather than adding new ones:

query MyPostPurchaseEarnings { myPostPurchaseEarnings { totalSales totalRevenue totalEarnings platformFees } }
query SubscriberCount($creatorId: ID!) { subscriberCount(creatorId: $creatorId) }
query SubscriberRetention($months: Int) {
subscriberRetention(months: $months) { month newSubscribers churned activeAtEnd churnRate retentionRate }
}

myPostPurchaseEarnings comes from post-purchase.resolver.js; subscriberCount/subscriberRetention come from user-subscription.resolver.js. The page renders exclusive-post sales/earnings stat tiles, an active-subscriber count, and a monthly retention/churn bar chart, plus a CSV export button.

Routes

Account & privacy

RouteComponentDescription
/settings/profileEditProfilePageName, username, bio, avatar, cover photo
/settings/account-privacyAccountPrivacyPagePrivate account, visibility, mentions, activity, extended privacy toggles
/settings/tags-and-mentionsTagsAndMentionsPageWho can mention/tag you, tag-approval requirement
/settings/tags-and-mentions/reviewTagReviewPageApprove/reject tags awaiting your review
/settings/tagsTagsScreenPageTagged grid (posts you're tagged in) + Pending tags tab
/settings/friends-activityFriendsActivityPageVisibility of your activity in the Friends feed
/settings/followers-and-invitesFollowersAndInvitesPageAuto-confirm followers, invite friends
/settings/close-friendsCloseFriendsPageClose Friends list management
/settings/favoritesFavoritesPageFavorite accounts — see Favorites
/settings/blockedBlockedPageBlocked accounts
/settings/restricted-accountsRestrictedAccountsPageRestricted accounts list with per-row unrestrict — wired to restrictedAccounts/unrestrictAccount (restrict.resolver.js)
/settings/limit-interactionsLimitInteractionsPageTurn interaction limiting on/off with optional auto-expiry — wired to myInteractionLimit/setInteractionLimit (interaction-limit.resolver.js)
/settings/mutedMutedPageMuted accounts
/settings/hidden-wordsHiddenWordsPageWords/phrases auto-hidden from comments and message requests
/settings/story-liveStoryLivePageStory/Live audience defaults, location sharing, "Hide story from" list
/settings/account-statusAccountStatusPageModeration standing summary (distinct from account lifecycle status)
/settings/accountAccountManagementPageDeactivate/delete account — see Account Management
/profile?tab=saved(ProfilePage, saved tab)Saved posts — the Settings hub links here rather than duplicating the feature

Messages & activity

RouteComponentDescription
/settings/messages-and-story-repliesMessagesAndStoryRepliesPageHub linking to the four sub-pages below
/settings/messages-and-story-replies/message-requestsMessageRequestsPageWho can message you, group invites, hide unwanted requests
/settings/messages-and-story-replies/story-repliesStoryRepliesPageWho can reply to your stories
/settings/messages-and-story-replies/read-receiptsReadReceiptsPageShow read receipts toggle
/settings/messages-and-story-replies/nudity-protectionNudityProtectionPageNudity protection in DMs toggle
/settings/notificationsNotificationsSettingsPageNotifications hub — pause all, sleep mode, per-category list — see Notifications
/settings/notifications/[category]NotificationCategoryPageGranular toggles for one notification category
/settings/notifications/sleep-modeSleepModePageScheduled do-not-disturb window
/settings/time-managementTimeManagementPageDaily usage chart, daily limit, sleep mode shortcut
/settings/archiveArchivePageArchived posts/stories/instants
/settings/recently-deletedRecentlyDeletedPageSoft-deleted posts, recoverable for 30 days
/settings/activityYourActivityPage"Your activity" hub
/settings/activity/interactionsInteractionsPageLikes, comments, reposts
/settings/activity/watch-historyWatchHistoryPageRecently viewed posts/clips
/settings/activity/recent-searchesRecentSearchesPageSearch history
/settings/activity/link-historyLinkHistoryPageRecently clicked external links
/settings/activity/suggested-contentSuggestedContentPage"Not interested" content preferences

Discovery, content, and support

RouteComponentDescription
/settings/languageLanguagePageApp language + Reels translation preferences
/settings/content-preferencesContentPreferencesPageHub linking to Suggested content, Accessibility, Media quality, Language
/settings/accessibilityAccessibilityPageReduce motion / autoplay videos (device-local, localStorage)
/settings/commentsCommentsPageAllow comments toggle (mirrors the field also on Account privacy)
/settings/sharingShareSettingsPageAllow resharing your posts to others' Stories
/settings/crosspostingCrosspostingPageConnect/disconnect/toggle X, Facebook, TikTok — wired to crosspost.resolver.js (gated per provider on server OAuth env)
/settings/download-dataDownloadDataPageGDPR data export — see Data Export
/settings/device-permissionsDevicePermissionsPageCamera/Mic/Location/Notifications via Permissions API; Contacts/Photos informational only
/settings/app-website-permissionsAppAndWebsitePermissionsPageConnected apps/websites
/settings/family-centerFamilyCenterPage⚠️ UI stub
/settings/insights-toolsInsightsAndToolsPageCreator earnings + subscriber analytics (exclusive-post sales, subscriber count, retention/churn) — despite the route name, this is not supervised-account tooling
/settings/sessionsSessionsSettingsPageActive sessions — see Sessions
/settings/securitySecuritySettingsPage2FA, password — see Security
/settings/verificationVerificationRequestPageRequest a verified badge — see Verification
/settings/subscriptionsSubscriptionsSettingsPageManage subscribers/subscriptions
/payments(PaymentsPage)Linked from the hub as "Your orders and fundraisers"
/settings/more-info-supportMoreInfoSupportPageHelp, Privacy Center, Terms, links to About and Account status
/settings/aboutAboutPageApp version, legal links

Privacy settings

The base PrivacySettings type (declared in user.type.js) covers the original privacy fields:

type PrivacySettings {
isPrivate: Boolean!
showActivityStatus: Boolean!
allowMessagesFrom: String! # "everyone" | "followers" | "nobody"
allowTagsFrom: String!
showFollowers: Boolean!
showFollowing: Boolean!
allowComments: Boolean!
showInExplore: Boolean!
showInSearch: Boolean!
allowStorySharing: Boolean!
saveStoryToArchive: Boolean!
shareLocation: Boolean!
allowGroupInvitesFrom: String!
}
query PrivacySettings {
privacySettings {
isPrivate showActivityStatus allowMessagesFrom allowTagsFrom
showFollowers showFollowing allowComments showInExplore
}
}
mutation UpdatePrivacySettings($input: PrivacySettingsInput!) {
updatePrivacySettings(input: $input) { isPrivate showActivityStatus }
}
mutation ResetPrivacySettings { resetPrivacySettings { settings { settingName value } } }

Every newer Instagram-parity toggle lives in a separate ExtendedPrivacySettings type rather than being merged into PrivacySettings — two same-named GraphQL types previously got silently field-unioned by @graphql-tools/merge when a duplicate PrivacySettingsInput existed, corrupting the schema (see GraphQL Architecture). Every new type introduced follows the "own brand-new type name" rule to avoid repeating that bug.

type ExtendedPrivacySettings {
whoCanMention: String!
requireApprovalForTags: Boolean!
friendsActivityVisibility: String! # "followers_you_follow_back" | "no_one"
hideLikeAndShareCounts: Boolean!
suggestPostsToNonFollowers: Boolean! # renamed from a mislabeled "previewsEnabled" — see note below
locationHistory: Boolean!
allowPersonalization: Boolean!
hideNotificationPreviews: Boolean!
autoDeleteMessages: Boolean!
messageDeletionDays: Int!
hideUnwantedMessageRequests: Boolean!
storyRepliesFrom: String! # "followers_you_follow_back" | "no_one"
readReceiptsEnabled: Boolean!
nudityProtectionEnabled: Boolean!
}

query ExtendedPrivacySettings { extendedPrivacySettings { whoCanMention requireApprovalForTags suggestPostsToNonFollowers storyRepliesFrom readReceiptsEnabled nudityProtectionEnabled } }
mutation UpdateExtendedPrivacySettings($input: ExtendedPrivacySettingsInput!) { updateExtendedPrivacySettings(input: $input) { suggestPostsToNonFollowers } }

"Previews" naming fix: an earlier pass added a previewsEnabled field meaning "suggest your posts to non-followers" and labeled it "Previews" — that's not what Instagram's real "Previews" setting means. It's been renamed to suggestPostsToNonFollowers. The real "Previews" toggle (show message-content previews in notifications) is ExtendedNotificationSettings.previewsEnabled, backed by a notification_preview field that already existed in the notification manager but was never exposed until now.

Story privacy — hidden users

query StoryHiddenUsers { storyHiddenUsers { id username profilePicture } }
mutation UpdateStoryHiddenUsers($userIds: [ID!]!) { updateStoryHiddenUsers(userIds: $userIds) { id username } }

Media quality

Replaces the old single mediaQuality string enum with three independent booleans:

type MediaPreferences {
dataSaverCellular: Boolean! # reduce quality on cellular
uploadQualityHigh: Boolean! # upload in high vs. standard quality
hdrEnabled: Boolean! # upload/display HDR where supported
}
query MediaPreferences { mediaPreferences { dataSaverCellular uploadQualityHigh hdrEnabled } }
mutation UpdateMediaPreferences($input: MediaPreferencesInput!) { updateMediaPreferences(input: $input) { dataSaverCellular } }

Sharing, Hidden Words, Followers & invites

# Sharing — resharing this account's *posts* into other users' Stories
# (distinct from PrivacySettings.allowStorySharing, which governs resharing this account's *Stories*)
query SharingSettings { sharingSettings { allowResharingToStories } }
mutation UpdateSharingSettings($input: SharingSettingsInput!) { updateSharingSettings(input: $input) { allowResharingToStories } }

# Hidden Words — free-form word/phrase list, filters comments and message requests
query HiddenWords { hiddenWords }
mutation UpdateHiddenWords($words: [String!]!) { updateHiddenWords(words: $words) }

# Followers and invites
query FollowersInvitesSettings { followersInvitesSettings { autoConfirmFollowers } }
mutation UpdateFollowersInvitesSettings($input: FollowersInvitesSettingsInput!) { updateFollowersInvitesSettings(input: $input) { autoConfirmFollowers } }

Account standing

A moderation-standing summary, deliberately a different query from the pre-existing accountStatus (which reports isActive/isDeactivated/isDeleted lifecycle state, used by Account Management). restrictionLevel is derived from two sources: suspended/banned come from the real user.accountStatus field (set by suspendUser/banUser), while limited comes from the separate, mostly-dead user.isRestricted/restrictedUntil columns — this doc previously attributed the whole query to isRestricted/restrictedUntil alone; corrected. reason and suspendedUntil were added this pass (only populated while actually suspended/banned) to power the restriction-appeal flow — see Verification → Restriction appeals.

type AccountStandingStatus {
hasViolations: Boolean!
restrictionLevel: String! # "none" | "limited" | "suspended" | "banned"
isEligibleForMonetization: Boolean! # moderation-standing signal only, not full payout eligibility
recentStrikes: Int!
lastActionAt: DateTime
reason: String # suspension/ban reason - only populated while suspended/banned
suspendedUntil: DateTime # only populated while suspended/banned
}
query AccountStandingStatus {
accountStandingStatus { hasViolations restrictionLevel recentStrikes lastActionAt reason suspendedUntil }
}

AccountStatusPage.tsx also queries myLatestAppeal alongside accountStandingStatus in the same request, so it can render the right appeal state (form / pending / denied) without a second round-trip — see Verification → Restriction appeals for that query and the admin review side.

Notifications

See Notifications for the base notificationSettings API. The Settings hub also exposes a much larger ExtendedNotificationSettings surface covering per-category granularity (stories, live, follows, mentions, tags, messages, groups, reactions, friend activity, birthdays, suggestions, trending, post/story interactions), quiet hours, a one-tap "Pause all" / "Messages only" mode, and the real "Previews" toggle:

query ExtendedNotificationSettings {
extendedNotificationSettings {
pauseAll messagesOnlyMode
quietHoursEnabled quietHoursStart quietHoursEnd
notifyFollows notifyMentions notifyTags notifyMessages notifyGroupMessages
notifyStoryViews notifyStoryLikes notifyFriendActivity notifyLiveVideos
likesNotificationScope taggedPhotoNotificationScope
previewsEnabled
}
}
mutation UpdateExtendedNotificationSettings($input: ExtendedNotificationSettingsInput!) {
updateExtendedNotificationSettings(input: $input) { pauseAll previewsEnabled }
}

Language & Reels translations

The language selector (i18next, EN/ES) is unchanged. Reels translation preferences are new and stored separately (JSONB settings.reels_translation, not tied to the app UI language):

type ReelsTranslationSettings {
reelsAutoTranslateText: Boolean!
reelsAutoTranslateVoice: Boolean!
reelsPreferredLanguages: [String!]!
}
query ReelsTranslationSettings { reelsTranslationSettings { reelsAutoTranslateText reelsAutoTranslateVoice reelsPreferredLanguages } }
mutation UpdateReelsTranslationSettings($input: ReelsTranslationSettingsInput!) { updateReelsTranslationSettings(input: $input) { reelsAutoTranslateText } }

The language selector persists the app-UI language choice to localStorage and applies instantly. Supported: English (en), Spanish (es).

Time management

type TimeManagementSettings { dailyLimitMinutes: Int sleepModeEnabled: Boolean! sleepModeStart: String! sleepModeEnd: String! }
type UsageStats { days: Int! dailyAverageSeconds: Int! todaySeconds: Int! series: [UsageDayEntry!]! }

query TimeManagementSettings { timeManagementSettings { dailyLimitMinutes sleepModeEnabled sleepModeStart sleepModeEnd } }
query UsageStats($days: Int) { usageStats(days: $days) { dailyAverageSeconds todaySeconds series { date totalSeconds } } }
mutation UpdateTimeManagementSettings($input: TimeManagementSettingsInput!) { updateTimeManagementSettings(input: $input) { dailyLimitMinutes } }
mutation RecordUsageHeartbeat($seconds: Int!) { recordUsageHeartbeat(seconds: $seconds) }

recordUsageHeartbeat is called every 30 seconds by components/UsageHeartbeat.tsx, mounted inside Navigation.tsx while the tab is visible and the user is authenticated — this is what powers the daily usage chart (rendered with Recharts).

Archive & Recently Deleted

Reuses the existing Post GraphQL type — Stories and Instants are just Post rows with type: "story" / type: "clip", and archiving is a boolean flag (isArchived) rather than a separate model.

query ArchivedPosts($type: String, $limit: Int, $offset: Int) { archivedPosts(type: $type, limit: $limit, offset: $offset) { id type createdAt } }
query RecentlyDeletedPosts($limit: Int, $offset: Int) { recentlyDeletedPosts(limit: $limit, offset: $offset) { id deletedAt } }

mutation ArchivePost($postId: ID!) { archivePost(postId: $postId) { id isArchived } }
mutation UnarchivePost($postId: ID!) { unarchivePost(postId: $postId) { id isArchived } }
mutation RestorePost($postId: ID!) { restorePost(postId: $postId) { id deletedAt } }
mutation PermanentlyDeletePost($postId: ID!){ permanentlyDeletePost(postId: $postId) }

recentlyDeletedPosts relies on Post's Sequelize paranoid: true soft-delete — posts stay recoverable for 30 days before permanent deletion. Post.deletedAt is exposed on the GraphQL type specifically to power the "N days left" countdown on this screen.

Your activity

query MyLikedPosts($limit: Int, $offset: Int) { myLikedPosts(limit: $limit, offset: $offset) { id } }
query MyComments($limit: Int, $offset: Int) { myComments(limit: $limit, offset: $offset) { id text } }
query MyReposts($limit: Int, $offset: Int) { myReposts(limit: $limit, offset: $offset) { id } }

query WatchHistory($limit: Int, $offset: Int) { watchHistory(limit: $limit, offset: $offset) { id post { id } viewedAt } }
mutation RecordPostView($postId: ID!) { recordPostView(postId: $postId) }
mutation ClearWatchHistory { clearWatchHistory }

query SearchHistory($limit: Int) { searchHistory(limit: $limit) { id query createdAt } }
mutation RecordSearch($query: String!, $targetType: String, $targetId: ID) { recordSearch(query: $query, targetType: $targetType, targetId: $targetId) { id } }
mutation DeleteSearchHistoryItem($id: ID!) { deleteSearchHistoryItem(id: $id) }
mutation ClearSearchHistory { clearSearchHistory }

query LinkHistory($limit: Int) { linkHistory(limit: $limit) { id url createdAt } }
mutation RecordLinkClick($url: String!, $sourceType: String, $sourceId: ID) { recordLinkClick(url: $url, sourceType: $sourceType, sourceId: $sourceId) { id } }
mutation ClearLinkHistory { clearLinkHistory }

query NotInterestedContent($limit: Int, $offset: Int) { notInterestedContent(limit: $limit, offset: $offset) { id targetType targetId } }
mutation SetContentPreference($targetType: String!, $targetId: ID!, $preference: String!) { setContentPreference(targetType: $targetType, targetId: $targetId, preference: $preference) { id } }
mutation RemoveContentPreference($targetType: String!, $targetId: ID!) { removeContentPreference(targetType: $targetType, targetId: $targetId) }

"Sticker responses" and "Reviews" (two rows Instagram shows under Interactions) have no backing model anywhere in the codebase and are intentionally not exposed — the frontend shows an honest "not available" state instead of faking data.

Tags

Distinct from Tags & mentions (privacy controls for who can tag you). This screen shows the actual tagged-content grid:

query PendingTagReviews { pendingTagReviews { id user { username } media { url } positionX positionY } }
mutation ApprovePostTag($mentionId: ID!) { approvePostTag(mentionId: $mentionId) }
mutation RejectPostTag($mentionId: ID!) { rejectPostTag(mentionId: $mentionId) }

query TaggedPosts($userId: ID!, $limit: Int, $offset: Int) { taggedPosts(userId: $userId, limit: $limit, offset: $offset) { id } }

approvePostTag/rejectPostTag are deliberately not named approveTag/rejectTag — those names are already taken by an older, unrelated, never-finished "generic tag permissions" mutation pair on the same Mutation type. PostMention rows carry a status (approved | pending | rejected); only approved mentions appear on taggedPosts.

Stub screens

These screens are UI-only — no GraphQL type, resolver, or manager exists for them anywhere in the backend. They render honestly (visible but disabled, or clearly labeled "coming soon") rather than faking working state:

ScreenRoute
Family Center/settings/family-center

Restricted accounts, Limit interactions, Crossposting, and Insights & tools were previously stubs but are now fully wired — see the checklist above.

Frontend components

All components live under page-components/settings/ unless noted.

ComponentBackendNotes
EditProfilePage
AccountPrivacyPageBase + Extended privacy settings
TagsAndMentionsPage
TagReviewPage
TagsScreenPageTagged grid + Pending tags tabs
FriendsActivityPage
FollowersAndInvitesPage✅ (partial)Auto-confirm is real; invite channels beyond "share link" are "coming soon"
CloseFriendsPage
FavoritesPageWired to favoriteUsers/addToFavorites/etc., backed by the real UserFavorite model (user_favorites table) — getFavoritesStats's mutualFavorites/recentAdditions/mostFavoritedUser are still hardcoded placeholders, see Favorites
BlockedPage
RestrictedAccountsPageWired to restrictedAccounts/unrestrictAccount
LimitInteractionsPageWired to myInteractionLimit/setInteractionLimit
MutedPage
HiddenWordsPage
StoryLivePageIncludes "Hide story from" list (storyHiddenUsers)
AccountStatusPage
AccountManagementPage
MessagesAndStoryRepliesPageHub, no query of its own
MessageRequestsPage
StoryRepliesPage
ReadReceiptsPage
NudityProtectionPage
NotificationsSettingsPage
NotificationCategoryPage
SleepModePage
TimeManagementPage
ArchivePage
RecentlyDeletedPage
YourActivityPageHub
InteractionsPage
WatchHistoryPage
RecentSearchesPage
LinkHistoryPage
SuggestedContentPage
LanguagePageIncludes Reels translations
ContentPreferencesPageHub, links to other pages
AccessibilityPagedevice-locallocalStorage, not backend-synced
CommentsPage
ShareSettingsPage
CrosspostingPageWired to crossposting API; per-provider connect gated on server OAuth env
DownloadDataPage
DevicePermissionsPagebrowser APIsCamera/Mic/Location/Notifications via Permissions API; Contacts/Photos informational only
AppAndWebsitePermissionsPage
FamilyCenterPage❌ stub
InsightsAndToolsPageCreator earnings/subscriber analytics, not supervised-account tooling — wired to myPostPurchaseEarnings/subscriberCount/subscriberRetention
SessionsSettingsPage
SecuritySettingsPage
VerificationRequestPage
SubscriptionsSettingsPage
MoreInfoSupportPageStatic links
AboutPageStatic
components/LanguageSelector.tsxLanguage switcher
components/ThemeToggle.tsxdevice-localDark / Light mode
components/UsageHeartbeat.tsxPowers Time management usage stats

Discovery restrictions

hideFromSuggestions removes the user from "People you may know" / "Suggested users" lists.

mutation HideFromSuggestions($enabled: Boolean!) { hideFromSuggestions(enabled: $enabled) }

Privacy recommendations

getPrivacyRecommendations returns a ranked list of suggested improvements (e.g. "Enable 2FA," "Set account to private"), each with an isApplied flag.

query PrivacyRecommendations {
getPrivacyRecommendations { id recommendationType title description priority isApplied }
}