API contract scope - AnnabiGihed/RaidManager GitHub Wiki
Generated from
docs/reference/api-contracts.md. Edit that file in the repository: changes made in the wiki are overwritten.
The RaidManager application programming interface (API) publishes its operations in an OpenAPI document at
/openapi/v1.json, which is the source of truth for request shapes, response codes, and security.
Failures are returned as ProblemDetails; validation failures list each field under errors.
In Development, the interactive reference page at https://localhost:55366/scalar renders that document and lets you
try each operation (ADR-0013). The Aspire dashboard links to it as
"API reference" on the api resource. To call a website-only operation, enter the website key in the page's
authentication panel; dotnet user-secrets list --project src/Containers/Aspire/Hosting/RaidManager.AppHost shows it
as Parameters:website-service-key in your own terminal.
| Operation | Caller | Purpose |
|---|---|---|
POST /internal/identity/discord-sign-in |
Website only | Resolve a Discord identity confirmed by OAuth to its single local user id. |
GET /internal/users/{userId}/character-claims/pending |
Website only | List the player's pending and conflicted character claims, oldest first. |
POST /internal/users/{userId}/character-claims/{characterId}/approve |
Website only | Approve a pending claim. Returns 409 and keeps the claim in conflict review when another player owns the character. |
POST /internal/users/{userId}/character-claims/{characterId}/reject |
Website only | Reject a pending claim, so the character never becomes the player's signup option. |
GET /internal/users/{userId}/characters |
Website only | List the characters the player owns through an approved claim, by realm and name, with the primary loadout, the raid saves that haven't reset and the latest sync. |
GET /internal/users/{userId}/characters/{characterId} |
Website only | Get the profile of one of the player's characters: data sources, professions, loadouts with their equipment, current raid saves and the visibility. Returns 404 for a character the player doesn't own. |
DELETE /internal/users/{userId}/characters |
Website only, dev and test | Remove all the player's characters to start a test over: a character only the player owns or claims is deleted with its claims and loadouts, and the player's claim on any other is withdrawn; 200 with the number removed. Each of the player's active companions is asked to send every character again. Mapped only on Development, Dev and Test, so production refuses it (#597). |
POST /internal/communities |
Website only | Link a Discord server as a community, with the user who added the bot as its Administrator. Returns 201 with the community id, or 409 when the server is already linked. |
GET /internal/communities/{communityId} |
Website only | Get a community's Discord server, realm and Administrator. |
GET /internal/communities/by-discord-server/{discordGuildId} |
Website only | Get the community a Discord server links to, or 404 when it isn't linked. |
POST /internal/communities/by-discord-servers |
Website only | At sign-in, list the linked communities of the Discord servers the user is in, by name; servers that aren't linked are left out. |
GET /internal/users/{userId}/communities?memberOf={communityId} |
Website only | List the communities the user administers, then those in memberOf (repeatable), the ones the user's servers matched at sign-in; each group by name. |
GET /internal/users/{userId}/communities/{communityId}/roles |
Website only | Read the community's roles from Discord: the mappable roles, and rows for the Administrator, each role (with its id and permissions) and Member, each with its Discord roles and member count. Says whether the user may change roles, may let a role manage roles, and may change each role. Only a member of the server may; refreshes the stored server name. |
GET /internal/users/{userId}/communities/{communityId}/members |
Website only | List the server's members from Discord, without bots: each one's display name, avatar, Discord roles and roles (Administrator, the roles their Discord roles give, or Member), Administrator first, with the time Discord was asked. Only a member of the server may; refreshes the stored server name. |
POST /internal/users/{userId}/communities/{communityId}/roles |
Website only | Create a role with a name (at most 50 characters, unique in the community, 409 otherwise) and permissions (ManageRaids, BuildRosters, RunRaidNight, ReviewConflicts, ManageCommunityRoles); 201 with the roleId. The Administrator or a role manager may; only the Administrator can include ManageCommunityRoles. |
PUT /internal/users/{userId}/communities/{communityId}/roles/{roleId} |
Website only | Rename a role and change what it allows. Only the Administrator changes a role that grants ManageCommunityRoles or lets one grant it. |
DELETE /internal/users/{userId}/communities/{communityId}/roles/{roleId} |
Website only | Delete a role; its mappings go with it. Only the Administrator deletes a role that grants ManageCommunityRoles. |
PUT /internal/users/{userId}/communities/{communityId}/role-mappings/{roleId}/{discordRoleId} |
Website only | Make a Discord role give one of the community's roles; 404 for a role the community doesn't have. One Discord role can give several roles. The Administrator or a role manager may; only the Administrator maps a role that grants ManageCommunityRoles. |
DELETE /internal/users/{userId}/communities/{communityId}/role-mappings/{roleId}/{discordRoleId} |
Website only | Stop a Discord role giving that role; any other role it gives stays. The same people may as for mapping. |
POST /companion/pairings |
Companion, anonymous | Start pairing: returns a device code to poll with, a pairing code such as K7M-4QX to show, its expiry 10 minutes later and the polling interval of 5 seconds. Takes a computerLabel of at most 200 characters, kept to 64. |
POST /companion/pairings/token |
Companion, anonymous | Poll with the deviceCode: 400 CompanionPairing.Pending until the player confirms, then 200 with the companionId, the deviceToken and the player's name, once. Afterwards, and for an unknown device code, 400 CompanionPairing.Invalid; after expiry, 400 CompanionPairing.Expired. |
GET /companion/me |
Companion, device token | Check the pairing: the companion's id and computer label, and syncAgainRequestedAtUtc, when the player last asked it to send every character again (set by the removal below; the companion checks once a minute, #615). |
POST /companion/snapshots |
Companion, device token | Upload one character snapshot of the addon file: 202 Imported when it was applied, 200 AlreadyCurrent when a snapshot captured at the same time or later was already applied (see Character snapshots). |
GET /internal/users/{userId}/companion-pairings/{pairingCode} |
Website only | Describe the pairing a code belongs to (code, computer label, request time, expiry) for the player to check before confirming. |
POST /internal/users/{userId}/companion-pairings/{pairingCode}/confirm |
Website only | Confirm the code: binds the computer to the player, whose companion gets its token at its next poll. |
GET /internal/users/{userId}/companions |
Website only | List the player's companions, revoked ones included, oldest first, with when each was paired, last used and last uploaded a snapshot, and its status: Active, Revoked or Expired. |
POST /internal/users/{userId}/companions/{companionId}/revoke |
Website only | Revoke a companion: its next request gets 401. What it uploaded stays. |
Operations under /internal/ require the website key in the X-RaidManager-Service-Key header, as
ADR-0011 describes. A missing or wrong key returns 401. The website
passes the signed-in player's user id from its session in the route.
A claim decision returns 204 on success, 404 when the character or claim doesn't exist, and 409 when the claim was
already decided or another player owns the character. Claims carry realm, class, race and claim state as names, such
as Icecrown or Conflict.
A community carries its realm as a name too. Linking needs a server id that is a Discord snowflake, a name of at most 100 characters, one of the realm names and a known user; anything else returns 400 naming the field, and an unknown user returns 404.
The officer roles operations ask Discord with the bot (ADR-0022).
Someone outside the Discord server, or anyone but the Administrator changing a mapping, gets 403. A Discord role that
isn't one of the server's mappable roles returns 400: @everyone and roles of other bots can't be mapped. Discord not
answering returns 503, and a server the bot was removed from returns 409.
The companion pairs as ADR-0030 decides. The public API
hostnames forward only /companion/ (owner decision on #369).
-
Device token. Routes under
/companion/other than the two pairing routes need the device token inAuthorization: Bearer <token>. The API keeps only its SHA-256 hash and checks it on every request. A missing or unknown token gets 401Companion.TokenUnknown, a revoked companion 401Companion.Revoked, and one unused for more than 180 days 401Companion.Expired, each withWWW-Authenticate: Bearer error="invalid_token". Last use is recorded at most once an hour. -
Pairing codes are six characters from
23456789ABCDEFGHJKMNPQRSTUVWXYZ, shown asXXX-XXX; the routes accept them with or without the dash and in any case. Looking a code up or confirming it answers 404 for an unknown code, and 409CompanionPairing.ExpiredorCompanionPairing.AlreadyConfirmed. - Revocation answers 404 for a companion the player doesn't have and 409 when it was already revoked.
-
Rate limits answer 429
RateLimit.ExceededwithRetry-After: 10 pairing starts per address per 10 minutes, 60 token polls per address per minute, 10 code checks (looking a code up or confirming it) per player per 10 minutes, and 120 snapshot uploads per companion per minute. TheCompanion:RateLimitsconfiguration section changes them.
A paired companion uploads each character of the addon's RaidManager.lua with POST /companion/snapshots, one
character per request, as JSON in the shape of the addon snapshot contract:
{ "schemaVersion": 1, "addonVersion": "0.1.0", "character": { "realm": "Icecrown", "name": "Arthasdk", ... } }, where
character is one entry of the file's characters table with the same field names.
-
Identity of a snapshot. A snapshot is identified by its character and its
capturedAt. The API applies a snapshot only when it is newer than the last one applied to the character, so retrying the same snapshot is safe: it answers 202Importedwhen it applied the snapshot and 200AlreadyCurrentotherwise. The companion drops the snapshot from its queue on either answer. -
New characters are imported with a pending claim for the companion's player, who reviews it on the website
(ADR-0002). A character another player owns keeps its
owner, and the uploading player's claim goes to conflict review. A new character needs its
identitysection: without it the API answers 400Character.Snapshot.IdentityUnavailable. -
Sections. Only an
observedsection changes the character; anunavailableone keeps what was known. Only a complete saved-instance scan from an English client (enUSorenGB) replaces the raid saves, because instance names are localized; RaidManager keeps the current saves of the WotLK raids it plans and ignores dungeons, older raids and expired saves. - Loadouts are the character's talent groups (owner decision on #384): each has its group's talents and glyphs, a name and a role from its tree with the most points spent. The active group gets the gear worn at the capture, keeping its known item in a slot the game didn't describe; the other group keeps its gear. The first addon loadout of a character without a primary loadout becomes primary when it is the active group. Equipment sets aren't imported. GearScore, combat statistics and item levels stay empty until an item catalog exists (#552).
-
Validation. A snapshot that breaks the contract (another schema version, an unknown realm, class, race or
faction, a capture time in the future, a section without its status or observation time) answers 400 with the
failing fields under
errors.
- User-scoped website calls, session, and profile access.
- Monitored-installation status.
- Resolving an ownership conflict between two players.
- Character profiles, loadouts, synchronization timestamps, and raid lockouts.
- Community membership and officer permissions, raid templates, recurrence, scheduling, and publication.
- Confirmed, tentative, late, and declined signups, presets, compositions, bench, boss assignments, gear-readiness warnings, and raid-buff coverage.
- Eligibility results per scheduled raid target, including verdict, source, freshness, reason, and reset time.
- Recorded officer exceptions for unknown data, reminders, roster-change notifications, attendance history, and addon roster export.
Transport models must stay separate from Domain types. The web app and Discord bot must receive the same eligibility decision from the application layer.