Discord rank-to-role sync
Platform guild Role rows (ranks) can map to multiple Discord role snowflakes for the linked Organization.discordServerId. Sync uses the existing bot (DiscordService.assignRole / removeRole). Event-based Discord roles (Event.discordRoleId, signup flows) stay separate: rank sync only touches roles listed in RoleDiscordRole.
Data modelโ
RoleDiscordRole:organizationRoleIdโdiscordRoleId, optionalsortOrder. Unique(organizationRoleId, discordRoleId).DiscordManagedRoleSuppression:userId,organizationId,discordRoleId(optionaldiscordGuildId). Unique(userId, organizationId, discordRoleId). Means: do not auto-add this Discord role for this user in this org until cleared.
Triggersโ
- Rank change โ
membership.role.changedrunssyncOnRankChange: removes Discord roles that belonged only to the previous rank, adds roles for the new rank (skips adds if suppressed). - Discord account linked โ After Better Auth updates
User.discordId,notifyDiscordLinkedrunsreconcileAllForUser(full reconcile per active membership in orgs with a linked Discord server). guildMemberUpdateโ If a managed Discord role (one mapped to the memberโs current platform rank) disappears from the member, a suppression row is upserted so the next sync does not re-add it (mod or self-removal are indistinguishable without audit log).- Manual โ
POST /api/v1/organizations/:guildId/discord-rank-roles/reconcile(officers). - Scheduled โ Daily at 05:00 UTC,
reconcileAllOrganizationsWithRankMappingsfor orgs that have at least oneRoleDiscordRolerow.
Pausing sync: Organization.settings.discordRankRoleSyncEnabled โ when explicitly false (Integrations toggle), triggers 1โ5 skip Discord role changes for that guild; POST .../reconcile returns 400. Officers can still edit mappings and clear suppressions. Unset is treated as enabled for backward compatibility.
Reconcile vs rank changeโ
- Rank change uses a diff between previous and new rank mappings (efficient).
- Reconcile loads the memberโs current Discord roles, intersects with all rank-managed role IDs for the org, then removes extras and adds missing (respecting suppressions).
Officer APIโ
GET /organizations/:guildId/discord-rank-rolesโ mappings keyed by platform role id.PUT /organizations/:guildId/discord-rank-roles/roles/:roleIdโ body{ discordRoleIds: string[] }(replace).POST .../clear-suppressionsโ{ userId }or{ clearAll: true }.POST .../reconcileโ full org reconcile.
Requires Update Guild (same as other guild settings).
Operational constraintsโ
- The botโs highest role in Discord must be above any role it assigns.
- Bot needs Manage Roles (already required for announcements/events).
Product policyโ
Sticky suppression (default): once a managed role is removed in Discord, it is not re-added until an officer clears suppression for that user (or clearAll for the org).
Optional future: clear suppressions on platform rank change (not implemented; would โrefreshโ Discord packages on promotion).