mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
refactor: simplify account standing and verification levels (#3130)
This commit is contained in:
@@ -140,7 +140,7 @@ The acting credential must already have every value in `acls`, unless it has `*`
|
||||
| --- | --- | --- |
|
||||
| name<sup>1</sup> | string | The name given to the key (1-100 characters) |
|
||||
| expires_in_days?<sup>2</sup> | integer | The number of days until the key expires (1-365) |
|
||||
| acls<sup>3</sup> | array[string] | The [ACLs](/admin-api/#acl-registry) stored on the key, each a registry value (at most 111) |
|
||||
| acls<sup>3</sup> | array[string] | The [ACLs](/admin-api/#acl-registry) stored on the key, each a registry value (at most 107) |
|
||||
|
||||
<sup>1</sup> A value that is empty after trimming is rejected, so whitespace alone is not a name
|
||||
|
||||
@@ -218,7 +218,7 @@ An update never rotates the credential, and no field on this route changes the e
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| name?<sup>1</sup> | string | The replacement name for the key (1-100 characters) |
|
||||
| acls?<sup>2</sup> | array[string] | The complete replacement set of [ACLs](/admin-api/#acl-registry), each a registry value (at most 111) |
|
||||
| acls?<sup>2</sup> | array[string] | The complete replacement set of [ACLs](/admin-api/#acl-registry), each a registry value (at most 107) |
|
||||
|
||||
<sup>1</sup> A value that is empty after trimming is rejected
|
||||
|
||||
|
||||
@@ -40,10 +40,6 @@ Each value is the exact `task` discriminator.
|
||||
|
||||
ACL `bulk:update:user_flags`. Adds and removes account flags on every targeted user. Runs as `bulkUpdateUserFlags`.
|
||||
|
||||
### `update_suspicious_activity_flags`
|
||||
|
||||
ACL `bulk:update:suspicious_activity`. Adds and removes verification requirements on every targeted user. Runs as `bulkUpdateSuspiciousActivityFlags`.
|
||||
|
||||
### `update_guild_features`
|
||||
|
||||
ACL `bulk:update:guild_features`. Adds and removes features on every targeted guild. Runs as `bulkUpdateGuildFeatures`.
|
||||
@@ -88,19 +84,6 @@ The `task` discriminator selects one of these structures. Every ID array has an
|
||||
|
||||
<sup>1</sup> Each entry is one 64-bit flag value written as an unsigned decimal string, such as `1024`. Body validation rejects a symbolic name. Additions are applied before removals, so a value named in both arrays ends up cleared
|
||||
|
||||
#### Update suspicious activity flags structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| task | string | The discriminator selecting this variant, `update_suspicious_activity_flags` |
|
||||
| user_ids | array[snowflake] | The users to update (max 1000 entries) |
|
||||
| add_flags?<sup>1</sup> <sup>2</sup> | array[string] | [Suspicious activity flag](/admin-api/users/#suspicious-activity-flags) names to add (max 32, default empty) |
|
||||
| remove_flags?<sup>1</sup> | array[string] | [Suspicious activity flag](/admin-api/users/#suspicious-activity-flags) names to remove (max 32, default empty) |
|
||||
|
||||
<sup>1</sup> Each entry is the symbolic flag name such as `REQUIRE_VERIFIED_PHONE`. An entry naming no known flag is ignored, and additions are applied before removals
|
||||
|
||||
<sup>2</sup> Adding `REQUIRE_VERIFIED_PHONE` or `REQUIRE_REVERIFIED_PHONE` also clears the deferred phone bit 1 << 16, which turns a deferred phone requirement into an immediate one
|
||||
|
||||
#### Update guild features structure
|
||||
|
||||
| Field | Type | Description |
|
||||
@@ -150,12 +133,10 @@ Entities are processed in the submitted order. [Cancel job](/admin-api/jobs/#can
|
||||
|
||||
Every task updates its progress before work starts and at completion. Every task except `schedule_user_deletion` also updates its progress after every 25 entities, and `schedule_user_deletion` updates its progress after every 10 accounts. The final `progress_message` has the successful and failed counts.
|
||||
|
||||
Every task writes one summary Admin audit entry when it finishes, with the action `bulk_update_user_flags`, `bulk_update_suspicious_activity_flags`, `bulk_update_guild_features`, `bulk_add_guild_members`, `bulk_schedule_deletion`, `bulk_ban_file_shas`, or `bulk_delete_user_messages`. The summary has the audit reason, the entity count, the operation-specific parameters, the job identifier, and the processed, successful, and failed counts. Its `target_type` is `bulk_job` and its `target_id` is the job identifier, except for `add_guild_members`, which targets the guild. A failed job writes no summary entry. A cancelled job writes one, marked `cancelled`, covering the entities it processed before it stopped.
|
||||
Every task writes one summary Admin audit entry when it finishes, with the action `bulk_update_user_flags`, `bulk_update_guild_features`, `bulk_add_guild_members`, `bulk_schedule_deletion`, `bulk_ban_file_shas`, or `bulk_delete_user_messages`. The summary has the audit reason, the entity count, the operation-specific parameters, the job identifier, and the processed, successful, and failed counts. Its `target_type` is `bulk_job` and its `target_id` is the job identifier, except for `add_guild_members`, which targets the guild. A failed job writes no summary entry. A cancelled job writes one, marked `cancelled`, covering the entities it processed before it stopped.
|
||||
|
||||
`update_user_flags` writes one `update_flags` entry for each account and dispatches [User Update](/gateway/events/#user-update) to the account's sessions. A change to a publicly visible flag also dispatches [Guild Member Update](/gateway/events/#guild-member-update) to every guild the account is in.
|
||||
|
||||
`update_suspicious_activity_flags` rewrites each account's verification requirements and dispatches [User Update](/gateway/events/#user-update). No [Guild Member Update](/gateway/events/#guild-member-update) follows. The task writes one `update_suspicious_activity_flags` entry for each account, with the audit reason. An unknown flag name fails the job before any account is changed.
|
||||
|
||||
`update_guild_features` writes one `update_features` entry for each guild, dispatches [Guild Update](/gateway/events/#guild-update), and reindexes the guild for search. Fluxer reconciles a guild that already has a discovery application record against the new feature set, so gaining `DISCOVERABLE` approves the record and losing it marks the record removed. A guild with no discovery record is left alone.
|
||||
|
||||
`add_guild_members` bypasses the ban check that a normal join runs. The task suppresses the join system message, records the join source as an Admin force add, and dispatches [Guild Member Add](/gateway/events/#guild-member-add) to the guild and [Guild Create](/gateway/events/#guild-create) to the added account's sessions. The task still enforces the per-account guild cap and the guild member cap, so an account at either ceiling is counted as failed. An account that is already a member is left unchanged and counted as successful, with no second membership and no Dispatch. Adding a bot account also records a `BOT_ADD` guild audit log entry attributed to the acting Admin.
|
||||
|
||||
@@ -55,14 +55,13 @@ A body with none of those fields resolves to no ACL at all and applies no change
|
||||
[Queue bulk job](/admin-api/bulk-jobs/#queue-bulk-job) maps its `task` discriminator to one ACL and requires that one.
|
||||
|
||||
- `update_user_flags` maps to `bulk:update:user_flags`.
|
||||
- `update_suspicious_activity_flags` maps to `bulk:update:suspicious_activity`.
|
||||
- `update_guild_features` maps to `bulk:update:guild_features`.
|
||||
- `add_guild_members` maps to `bulk:add:guild_members`.
|
||||
- `schedule_user_deletion` maps to `bulk:delete:users`.
|
||||
|
||||
Fluxer checks each ACL an Admin grants against the ACLs that Admin holds. [Set user ACLs](/admin-api/users/#set-user-acls) and [Create Admin API key](/admin-api/api-keys/#create-admin-api-key) both refuse to write an ACL the acting Admin does not itself hold, with 403 `MISSING_ACL`. A wildcard holder is exempt. Set user ACLs also refuses the acting Admin's own account with 403 `ACCESS_DENIED`, and it looks up the target account before it checks the granted ACLs, so an unknown ID fails first with 404 `UNKNOWN_USER`.
|
||||
|
||||
[Set user ACLs](/admin-api/users/#set-user-acls), [Create Admin API key](/admin-api/api-keys/#create-admin-api-key), and [Update Admin API key](/admin-api/api-keys/#update-admin-api-key) each accept at most 111 ACLs and validate every entry against the registry, so a value outside it returns 400 `INVALID_FORM_BODY`.
|
||||
[Set user ACLs](/admin-api/users/#set-user-acls), [Create Admin API key](/admin-api/api-keys/#create-admin-api-key), and [Update Admin API key](/admin-api/api-keys/#update-admin-api-key) each accept at most 107 ACLs and validate every entry against the registry, so a value outside it returns 400 `INVALID_FORM_BODY`.
|
||||
|
||||
:::caution[`*` satisfies every present and future ACL]
|
||||
A key whose owning account holds `*` skips the owner check. An Admin holding `*` can grant any ACL.
|
||||
@@ -330,7 +329,6 @@ An entry with any other action has `access` set to `write`.
|
||||
| bulk_delete_user_messages | A queued bulk message deletion job was applied |
|
||||
| bulk_schedule_deletion | A bulk account deletion schedule was queued or applied |
|
||||
| bulk_update_guild_features | A bulk guild feature change was queued or applied |
|
||||
| bulk_update_suspicious_activity_flags | A bulk suspicious activity flag change was queued or applied |
|
||||
| bulk_update_user_flags | A bulk account flag change was queued or applied |
|
||||
| cancel_bulk_message_deletion | A bulk message deletion an account scheduled for itself was cancelled |
|
||||
| cancel_deletion | A scheduled account deletion was cancelled |
|
||||
@@ -351,7 +349,6 @@ An entry with any other action has `access` set to `write`.
|
||||
| delete_voice_server | A registered voice server was deleted |
|
||||
| delete_webauthn_credential | A WebAuthn credential was removed from an account |
|
||||
| disable_mfa | Multi-factor authentication was disabled on an account |
|
||||
| disable_suspicious_activity | An account flagged for suspicious activity was disabled |
|
||||
| force_add_to_guild | An account was added to a guild while bypassing the ban check |
|
||||
| generate_gift_codes | Gift codes were generated |
|
||||
| kick_member | A member was removed from a guild |
|
||||
@@ -402,13 +399,11 @@ An entry with any other action has `access` set to `write`.
|
||||
| update_features | Guild features were added or removed |
|
||||
| update_guild | A guild update with no field group was sent |
|
||||
| update_flags | Account flags were changed |
|
||||
| update_has_verified_phone | Verified phone state was changed |
|
||||
| update_instance_config | The instance configuration was updated |
|
||||
| update_limit_config | The limit configuration was replaced |
|
||||
| update_name | A guild name was changed |
|
||||
| update_premium_flags | Premium flags were changed |
|
||||
| update_settings | General guild settings were changed |
|
||||
| update_suspicious_activity_flags | Suspicious activity flags were replaced |
|
||||
| update_vanity | A guild custom invite code was changed |
|
||||
| update_voice_region | A voice region was updated |
|
||||
| update_voice_server | A registered voice server was updated |
|
||||
@@ -481,7 +476,7 @@ Returns every Admin permission string the instance recognises, in [ACL registry]
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| acls<sup>1</sup> | array[string] | The permission strings the Admin API recognises (at most 111 entries) |
|
||||
| acls<sup>1</sup> | array[string] | The permission strings the Admin API recognises (at most 107 entries) |
|
||||
|
||||
<sup>1</sup> The response is the registry itself and does not vary with the caller's own ACL set
|
||||
|
||||
@@ -548,7 +543,6 @@ The registry is returned in this order by [List ACLs](#list-acls). A value outsi
|
||||
| bulk:delete:users | Queues the bulk account deletion task |
|
||||
| bulk:delete:user_messages | Queues the bulk message deletion task |
|
||||
| bulk:update:guild_features | Queues the bulk guild feature task |
|
||||
| bulk:update:suspicious_activity | Queues the bulk suspicious activity flag task |
|
||||
| bulk:update:user_flags | Queues the bulk account flag task |
|
||||
| csam:submit_ncmec | Submits an attachment through the NCMEC reporting integration |
|
||||
| discovery:remove | Removes a guild from [discovery](/admin-api/discovery/) |
|
||||
@@ -584,7 +578,6 @@ The registry is returned in this order by [List ACLs](#list-acls). A value outsi
|
||||
| system_dm:send | Sends an official direct message from the instance to one or many accounts |
|
||||
| user:cancel:bulk_message_deletion | Cancels the bulk message deletion an account scheduled for itself |
|
||||
| user:delete | Schedules or cancels account deletion |
|
||||
| user:disable:suspicious | Disables a user for suspicious activity |
|
||||
| user:list:dm_channels | Reads a user's direct message and group direct message channels |
|
||||
| user:list:guilds | Reads a user's guild memberships |
|
||||
| user:list:relationships | Reads a user's relationships |
|
||||
@@ -601,9 +594,7 @@ The registry is returned in this order by [List ACLs](#list-acls). A value outsi
|
||||
| user:update:email | Updates the email address, marks the address verified, resends verification, and sends a password reset |
|
||||
| user:update:flags | Updates account flags and premium flags, refreshes in-app purchases, and ends every login session of an account |
|
||||
| user:update:mfa | Reads and removes a user's WebAuthn credentials, and removes MFA state |
|
||||
| user:update:phone | Updates verified phone state |
|
||||
| user:update:profile | Clears profile fields |
|
||||
| user:update:suspicious_activity | Replaces suspicious activity flags |
|
||||
| user:update:traits | Replaces account traits |
|
||||
| user:update:username | Updates usernames and discriminators |
|
||||
| voice:region:create | Creates [voice](/admin-api/voice/) regions |
|
||||
|
||||
@@ -178,7 +178,7 @@ The ALTCHA proof-of-work check that the API issues and verifies itself on gated
|
||||
| cost | integer | PBKDF2 iterations per solving attempt (1000-20000, default 5000) |
|
||||
| max_counter | integer | Upper bound of the hidden counter a client searches for (100-20000, default 1000) |
|
||||
|
||||
Every field is present on read. An absent document or missing field uses the defaults above, so the check is on until an administrator sets `enabled` to false. Setting `enabled` to false turns the check off for the whole instance. Phone sends that the [phone verification service](/http-api/users/phone-verification/#send-phone-verification) challenges are refused instead.
|
||||
Every field is present on read. An absent document or missing field uses the defaults above, so the check is on until an administrator sets `enabled` to false. Setting `enabled` to false turns the check off for the whole instance.
|
||||
|
||||
Each challenge hides its counter between half of `max_counter` and `max_counter`, and a client tries counters from 0 upward. Solve time grows with `cost` times `max_counter`. Fluxer spends one attempt at `cost` to issue each challenge.
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ The queue drops a job 7 days after it was queued. A job still recorded as `queue
|
||||
|
||||
Use the returned `task_type` value to filter [List jobs](#list-jobs).
|
||||
|
||||
The tasks queued by [Bulk jobs](/admin-api/bulk-jobs/) are `bulkUpdateUserFlags`, `bulkUpdateSuspiciousActivityFlags`, `bulkUpdateGuildFeatures`, `bulkAddGuildMembers`, and `bulkScheduleUserDeletion`. `harvestUserData` and `harvestGuildData` build [archives](/admin-api/archives/), `sendSystemDm` delivers a [system DM broadcast](/admin-api/system-dms/), and `refreshSearchIndex` rebuilds a [search index](/admin-api/search-indexes/). `removeChannelFollowers` runs on the `crosspost` lane after an announcement channel is deleted or converted into a text channel, and removes the channel follower webhooks that follow it.
|
||||
The tasks queued by [Bulk jobs](/admin-api/bulk-jobs/) are `bulkUpdateUserFlags`, `bulkUpdateGuildFeatures`, `bulkAddGuildMembers`, and `bulkScheduleUserDeletion`. `harvestUserData` and `harvestGuildData` build [archives](/admin-api/archives/), `sendSystemDm` delivers a [system DM broadcast](/admin-api/system-dms/), and `refreshSearchIndex` rebuilds a [search index](/admin-api/search-indexes/). `removeChannelFollowers` runs on the `crosspost` lane after an announcement channel is deleted or converted into a text channel, and removes the channel follower webhooks that follow it.
|
||||
|
||||
## List jobs
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ When the caller lacks `user:view:email`, `user:view:dob`, or `user:view:ip`, Flu
|
||||
| email<sup>3</sup> | ?string | The registered email address, or null when the account has none |
|
||||
| email_verified<sup>3</sup> | boolean | Whether the registered address has been verified |
|
||||
| email_bounced<sup>3</sup> | boolean | Whether delivery to the registered address hard bounced |
|
||||
| has_verified_phone | boolean | Whether the account is treated as having completed phone verification |
|
||||
| date_of_birth<sup>4</sup> | ?string | The date of birth in `YYYY-MM-DD` form, or null when none is stored |
|
||||
| locale | ?string | The saved [locale](/topics/locales/#supported-locales), or null when the account has never set one |
|
||||
| premium_type | ?integer | [Premium type](#premium-types) |
|
||||
@@ -50,24 +49,22 @@ When the caller lacks `user:view:email`, `user:view:dob`, or `user:view:ip`, Flu
|
||||
| premium_until | ?ISO8601 timestamp | The time the premium subscription expires, or null when the account has none |
|
||||
| premium_grace_ends_at | ?ISO8601 timestamp | The time the payment grace period ends, or null when no grace period is running |
|
||||
| premium_lifetime_sequence | ?integer | The sequence number of the lifetime purchase, or null when the account holds none |
|
||||
| suspicious_activity_flags | integer | [Suspicious activity flags](#suspicious-activity-flags) |
|
||||
| phone_verification_deferred<sup>5</sup> | boolean | Whether a stored phone requirement is deferred and not enforced |
|
||||
| temp_banned_until<sup>6</sup> | ?ISO8601 timestamp | The time the ban expires, or null when no ban stands |
|
||||
| temp_banned_until<sup>5</sup> | ?ISO8601 timestamp | The time the ban expires, or null when no ban stands |
|
||||
| pending_deletion_at | ?ISO8601 timestamp | The time the scheduled deletion runs, or null when none is scheduled |
|
||||
| pending_bulk_message_deletion_at<sup>7</sup> | ?ISO8601 timestamp | The time the account's own scheduled bulk message deletion runs |
|
||||
| pending_bulk_message_deletion_at<sup>6</sup> | ?ISO8601 timestamp | The time the account's own scheduled bulk message deletion runs |
|
||||
| deletion_reason_code | ?integer | [Deletion reason](#deletion-reasons) |
|
||||
| deletion_public_reason | ?string | The reason shown to the account holder, or null when none was supplied |
|
||||
| deletion_audit_log_reason | ?string | The private reason stored with the pending deletion, and null without `audit_log:view` |
|
||||
| deletion_scheduled_by | ?snowflake | The ID of the account that scheduled the pending deletion, or null when it was not recorded |
|
||||
| deletion_scheduled_at | ?ISO8601 timestamp | The time the pending deletion was scheduled, or null when it was not recorded |
|
||||
| acls<sup>8</sup> | array[string] | Effective [Admin ACLs](/admin-api/#acl-registry), with at most 111 entries |
|
||||
| traits<sup>9</sup> | array[string] | The free-form operator labels set on the account, with at most 100 entries |
|
||||
| has_totp<sup>10</sup> | boolean | Whether a TOTP authenticator is registered |
|
||||
| authenticator_types<sup>10</sup> <sup>13</sup> | array[integer] | Registered [authenticator types](/http-api/users/#authenticator-types), with at most 10 entries |
|
||||
| acls<sup>7</sup> | array[string] | Effective [Admin ACLs](/admin-api/#acl-registry), with at most 107 entries |
|
||||
| traits<sup>8</sup> | array[string] | The free-form operator labels set on the account, with at most 100 entries |
|
||||
| has_totp<sup>9</sup> | boolean | Whether a TOTP authenticator is registered |
|
||||
| authenticator_types<sup>9</sup> <sup>12</sup> | array[integer] | Registered [authenticator types](/http-api/users/#authenticator-types), with at most 10 entries |
|
||||
| last_active_at | ?ISO8601 timestamp | The time of the last recorded activity, or null when none is recorded |
|
||||
| last_active_ip<sup>11</sup> | ?string | The IP address the account was last active from |
|
||||
| last_active_ip_reverse<sup>11</sup> <sup>12</sup> | ?string | The reverse DNS name of that IP address |
|
||||
| last_active_location<sup>11</sup> <sup>12</sup> | ?string | The approximate location of that IP address |
|
||||
| last_active_ip<sup>10</sup> | ?string | The IP address the account was last active from |
|
||||
| last_active_ip_reverse<sup>10</sup> <sup>11</sup> | ?string | The reverse DNS name of that IP address |
|
||||
| last_active_location<sup>10</sup> <sup>11</sup> | ?string | The approximate location of that IP address |
|
||||
|
||||
<sup>1</sup> An unpadded JSON number here, unlike the zero-padded string `discriminator` of the [Admin user summary](/admin-api/#admin-user-summary-object) and the [Admin resolved user](#admin-resolved-user-object) embedded in other Admin objects
|
||||
|
||||
@@ -77,23 +74,21 @@ When the caller lacks `user:view:email`, `user:view:dob`, or `user:view:ip`, Flu
|
||||
|
||||
<sup>4</sup> Requires `user:view:dob`, and without it the field is null
|
||||
|
||||
<sup>5</sup> Derived from bit `1 << 16` of `suspicious_activity_flags`, which sits outside the [suspicious activity flag](#suspicious-activity-flags) registry
|
||||
<sup>5</sup> Set by [Ban user](#ban-user) for both ban modes. An expiry that has already passed is reported as null, so an elapsed temporary ban reads the same as no ban
|
||||
|
||||
<sup>6</sup> Set by [Ban user](#ban-user) for both ban modes. An expiry that has already passed is reported as null, so an elapsed temporary ban reads the same as no ban
|
||||
<sup>6</sup> Written when the account holder schedules its own bulk message deletion, and cleared by [Cancel scheduled message deletion](#cancel-scheduled-message-deletion)
|
||||
|
||||
<sup>7</sup> Written when the account holder schedules its own bulk message deletion, and cleared by [Cancel scheduled message deletion](#cancel-scheduled-message-deletion)
|
||||
<sup>7</sup> The set written by [Set user ACLs](#set-user-acls), returned in stored order. This set alone decides whether the account can reach the Admin API, and the `STAFF` [account flag](#account-flags) plays no part in that
|
||||
|
||||
<sup>8</sup> The set written by [Set user ACLs](#set-user-acls), returned in stored order. This set alone decides whether the account can reach the Admin API, and the `STAFF` [account flag](#account-flags) plays no part in that
|
||||
<sup>8</sup> Sorted in ascending order, unlike `acls`
|
||||
|
||||
<sup>9</sup> Sorted in ascending order, unlike `acls`
|
||||
<sup>9</sup> Never redacted, and returned in full to any caller the operation admitted
|
||||
|
||||
<sup>10</sup> Never redacted, and returned in full to any caller the operation admitted
|
||||
<sup>10</sup> Requires `user:view:ip`, and without it the field is null and no network lookup is attempted
|
||||
|
||||
<sup>11</sup> Requires `user:view:ip`, and without it the field is null and no network lookup is attempted
|
||||
<sup>11</sup> Resolved live from `last_active_ip` for each response, and null when the lookup fails or returns nothing. The reverse DNS result is cached for one day
|
||||
|
||||
<sup>12</sup> Resolved live from `last_active_ip` for each response, and null when the lookup fails or returns nothing. The reverse DNS result is cached for one day
|
||||
|
||||
<sup>13</sup> `WEBAUTHN` is present only while the account chose passkeys as a second factor. A registered credential does not add it, so `has_totp` false with an empty array still describes an account holding passkeys
|
||||
<sup>12</sup> `WEBAUTHN` is present only while the account chose passkeys as a second factor. A registered credential does not add it, so `has_totp` false with an empty array still describes an account holding passkeys
|
||||
|
||||
### Example
|
||||
|
||||
@@ -111,10 +106,7 @@ When the caller lacks `user:view:email`, `user:view:dob`, or `user:view:ip`, Flu
|
||||
"email": "[email protected]",
|
||||
"email_verified": true,
|
||||
"email_bounced": false,
|
||||
"has_verified_phone": false,
|
||||
"date_of_birth": "1998-03-21",
|
||||
"suspicious_activity_flags": 0,
|
||||
"phone_verification_deferred": false,
|
||||
"temp_banned_until": null,
|
||||
"pending_deletion_at": null,
|
||||
"deletion_reason_code": null,
|
||||
@@ -143,19 +135,18 @@ The `flags` field of the [Admin user object](#admin-user-object) is a 64-bit bit
|
||||
| 1 << 6 | SPAMMER | Account is flagged as a spammer |
|
||||
| 1 << 33 | HIGH_GLOBAL_RATE_LIMIT | Account has elevated global rate limits |
|
||||
| 1 << 34 | DELETED | Account has been deleted |
|
||||
| 1 << 35 | DISABLED_SUSPICIOUS_ACTIVITY | Account is disabled for suspicious activity |
|
||||
| 1 << 36 | SELF_DELETED | Account was self-deleted |
|
||||
| 1 << 38 | DISABLED | Account is disabled |
|
||||
| 1 << 39 | HAS_SESSION_STARTED | Account has started a session |
|
||||
| 1 << 47 | RATE_LIMIT_BYPASS | Account can bypass rate limits |
|
||||
| 1 << 48 | REPORT_BANNED | Account is banned from reporting |
|
||||
| 1 << 49 | VERIFIED_NOT_UNDERAGE | Account is verified as not underage |
|
||||
| 1 << 50 | ACCOUNT_LIMITED | Account is [limited](/http-api/users/#account-limitation) |
|
||||
| 1 << 51 | HAS_DISMISSED_PREMIUM_ONBOARDING | Account has dismissed premium onboarding |
|
||||
| 1 << 53 | APP_STORE_REVIEWER | Account belongs to an app store reviewer |
|
||||
| 1 << 57 | STAFF_HIDDEN | Staff status is hidden from public flags |
|
||||
| 1 << 60 | AGE_VERIFIED_ADULT | Account has verified its age as an adult through card verification |
|
||||
| 1 << 61 | FORCE_INBOUND_PHONE_VERIFICATION | Account is forced through inbound phone verification |
|
||||
| 1 << 62 | NOT_SUSPICIOUS | Account is permanently exempt from automatic suspicious activity flagging |
|
||||
| 1 << 62 | LIMIT_EXEMPT | Account is never limited |
|
||||
|
||||
## Premium flags
|
||||
|
||||
@@ -179,30 +170,6 @@ The `flags` field of the [Admin user object](#admin-user-object) is a 64-bit bit
|
||||
| 1 | SUBSCRIPTION | Active premium subscription |
|
||||
| 2 | LIFETIME | Lifetime premium subscription |
|
||||
|
||||
## Suspicious activity flags
|
||||
|
||||
A 32-bit bitfield of verification requirements applied to an account. [Update suspicious activity flags](#update-suspicious-activity-flags) and [Disable user for suspicious activity](#disable-user-for-suspicious-activity) both write the complete value, so a bit the request omits is cleared.
|
||||
|
||||
| Value | Name | Description |
|
||||
| --- | --- | --- |
|
||||
| 1 << 0 | REQUIRE_VERIFIED_EMAIL<sup>1</sup> | Require a verified email |
|
||||
| 1 << 1 | REQUIRE_REVERIFIED_EMAIL<sup>1</sup> | Require a reverified email |
|
||||
| 1 << 2 | REQUIRE_VERIFIED_PHONE<sup>2</sup> | Require a verified phone |
|
||||
| 1 << 3 | REQUIRE_REVERIFIED_PHONE<sup>3</sup> | Require a reverified phone |
|
||||
| 1 << 4 | REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE<sup>1</sup> | Require verified email or verified phone |
|
||||
| 1 << 5 | REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE<sup>1</sup> | Require reverified email or verified phone |
|
||||
| 1 << 6 | REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE<sup>1</sup> | Require verified email or reverified phone |
|
||||
| 1 << 7 | REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE<sup>1</sup> | Require reverified email or reverified phone |
|
||||
| 1 << 8 | REQUIRE_INBOUND_PHONE_VERIFICATION<sup>2</sup> | Require inbound SMS verification, where the account holder texts a code to the instance's inbound SMS number |
|
||||
|
||||
<sup>1</sup> Cleared automatically whenever the account's email becomes verified, either through ordinary verification or through [Verify user email](#verify-user-email)
|
||||
|
||||
<sup>2</sup> Cleared automatically by [Update user phone verification](#update-user-phone-verification) when it sets `has_verified_phone` to true, together with the deferral bit `1 << 16`
|
||||
|
||||
<sup>3</sup> Deferrable alongside `REQUIRE_VERIFIED_PHONE`. [Update user phone verification](#update-user-phone-verification) leaves it set
|
||||
|
||||
Bit `1 << 16` sits outside this registry. It defers a phone requirement so it is stored but not enforced, and the [Admin user object](#admin-user-object) reports it as `phone_verification_deferred`. [Update suspicious activity flags](#update-suspicious-activity-flags) and [Disable user for suspicious activity](#disable-user-for-suspicious-activity) bound `flags` only as a non-negative 32-bit integer, and neither masks the submitted value, so a request with that bit sets it directly.
|
||||
|
||||
## Deletion reasons
|
||||
|
||||
| Value | Name | Description |
|
||||
@@ -400,14 +367,14 @@ One entry for each direct message or group direct message channel the account ha
|
||||
|
||||
## Admin user change log object
|
||||
|
||||
One recorded change to the account's email address, phone verification state, or username and discriminator. The account holder's own changes appear here, and so do [Change user username](#change-user-username) and [Change user email](#change-user-email).
|
||||
One recorded change to the account's email address or its username and discriminator. The account holder's own changes appear here, and so do [Change user username](#change-user-username) and [Change user email](#change-user-email).
|
||||
|
||||
### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| event_id<sup>1</sup> | string | The ID of the change entry as an unsigned 64-bit decimal string |
|
||||
| field | string | The field that changed, one of `email`, `has_verified_phone`, or `fluxer_tag` |
|
||||
| field | string | The field that changed, either `email` or `fluxer_tag` |
|
||||
| old_value<sup>2</sup> | ?string | The value before the change, or null when the field was unset |
|
||||
| new_value<sup>2</sup> | ?string | The value after the change, or null when the field was cleared |
|
||||
| reason<sup>3</sup> | ?string | The recorded reason for the change, or null when unrecorded |
|
||||
@@ -799,7 +766,7 @@ The operation accepts no request body and never clears verification, so the one
|
||||
|
||||
### Side effects
|
||||
|
||||
`email_verified` becomes true and `email_bounced` becomes false. The same write clears each of these [suspicious activity flag](#suspicious-activity-flags) bits: `REQUIRE_VERIFIED_EMAIL`, `REQUIRE_REVERIFIED_EMAIL`, `REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE`, `REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE`, `REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE`, and `REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE`.
|
||||
`email_verified` becomes true and `email_bounced` becomes false.
|
||||
|
||||
[User Update](/gateway/events/#user-update) is emitted to the account's own sessions. The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `verify_email`, target type `user`, and a metadata key `email` with the address as it stood before the write, or the literal `null` when the account had none.
|
||||
|
||||
@@ -814,7 +781,7 @@ The operation accepts no request body and never clears verification, so the one
|
||||
Requests a new verification email for the account. Requires `user:update:email`. Returns an empty 204 response.
|
||||
|
||||
:::caution[An already verified account gets no email]
|
||||
When the account's email is already verified and none of the [suspicious activity flag](#suspicious-activity-flags) bits `REQUIRE_REVERIFIED_EMAIL`, `REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE`, `REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE`, or `REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE` is set, Fluxer returns 204 without storing a token or sending anything.
|
||||
When the account's email is already verified, Fluxer returns 204 without storing a token or sending anything.
|
||||
:::
|
||||
|
||||
:::note[Delivery is dropped silently]
|
||||
@@ -1093,7 +1060,7 @@ A path naming the acting account fails with 403 `ACCESS_DENIED` before the accou
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| acls<sup>1</sup> | array[string] | Replacement [Admin ACLs](/admin-api/#acl-registry), with at most 111 values |
|
||||
| acls<sup>1</sup> | array[string] | Replacement [Admin ACLs](/admin-api/#acl-registry), with at most 107 values |
|
||||
|
||||
<sup>1</sup> Required. A value outside the [ACL registry](/admin-api/#acl-registry) fails body validation, and a repeated value is collapsed
|
||||
|
||||
@@ -1211,6 +1178,8 @@ Additions are applied before removals, so a flag named in both arrays ends up cl
|
||||
|
||||
[User Update](/gateway/events/#user-update) is emitted to the account's own sessions. A change to the account's public flags also sends [Guild Member Update](/gateway/events/#guild-member-update) to the account's guilds.
|
||||
|
||||
Removing `ACCOUNT_LIMITED` or adding `LIMIT_EXEMPT` also lifts any [new conversation limit](/http-api/users/private-channels/#new-conversation-limit) on the account.
|
||||
|
||||
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_flags`, target type `user`, and the metadata keys `add_flags`, `remove_flags`, and `new_flags`. An empty array is omitted from the metadata map.
|
||||
|
||||
### Rate limit
|
||||
@@ -1332,151 +1301,6 @@ The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-obje
|
||||
|
||||
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
||||
|
||||
## Update user phone verification
|
||||
|
||||
<RouteHeader method="PUT" path="/v1/admin/users/{user_id}/phone-verification" auditReason />
|
||||
|
||||
Sets whether the account is treated as having completed phone verification, and returns the resulting account. Requires `user:update:phone`.
|
||||
|
||||
This operation is the one way to set `has_verified_phone` back to false once it is true.
|
||||
|
||||
### Path parameters
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| user_id | snowflake | The ID of the target account |
|
||||
|
||||
### JSON body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| has_verified_phone<sup>1</sup> | boolean | Whether the account counts as phone verified |
|
||||
|
||||
<sup>1</sup> Required. Setting it to true also clears the `REQUIRE_VERIFIED_PHONE` and `REQUIRE_INBOUND_PHONE_VERIFICATION` [suspicious activity flags](#suspicious-activity-flags) together with the deferral bit `1 << 16`, and setting it to false clears no flag
|
||||
|
||||
### Response body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| user | [Admin user](#admin-user-object) object | The resulting account |
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | response body | Phone verification state was set |
|
||||
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER`, because the account does not exist |
|
||||
|
||||
`REQUIRE_REVERIFIED_PHONE` is outside the cleared set, so an account under a reverification requirement keeps it after this operation marks it verified.
|
||||
|
||||
### Side effects
|
||||
|
||||
[User Update](/gateway/events/#user-update) is emitted to the account's own sessions. The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_has_verified_phone`, target type `user`, and a metadata key `has_verified_phone`. When suspicious activity flags were also cleared, the entry also has `suspicious_activity_flags_before` and `suspicious_activity_flags_after`.
|
||||
|
||||
### Rate limit
|
||||
|
||||
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
||||
|
||||
## Update suspicious activity flags
|
||||
|
||||
<RouteHeader method="PUT" path="/v1/admin/users/{user_id}/suspicious-activity-flags" auditReason />
|
||||
|
||||
Replaces the account's [suspicious activity flags](#suspicious-activity-flags) and returns the resulting account. Requires `user:update:suspicious_activity`.
|
||||
|
||||
The operation sets verification requirements without disabling the account. [Disable user for suspicious activity](#disable-user-for-suspicious-activity) also locks the account out.
|
||||
|
||||
### Path parameters
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| user_id | snowflake | The ID of the target account |
|
||||
|
||||
### JSON body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| flags<sup>1</sup> | integer | Replacement [suspicious activity flags](#suspicious-activity-flags) |
|
||||
|
||||
<sup>1</sup> Required. The value replaces the complete stored bitfield, so an omitted bit is cleared and a value of zero sets no requirement at all
|
||||
|
||||
### Response body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| user | [Admin user](#admin-user-object) object | The resulting account |
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | response body | Flags were replaced |
|
||||
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER`, because the account does not exist |
|
||||
|
||||
The stored deferral bit `1 << 16` is preserved only when it was already set and the submitted value sets the same `REQUIRE_VERIFIED_PHONE` and `REQUIRE_REVERIFIED_PHONE` bits as the stored value, with at least one of them set. Any other submitted value clears the deferral bit, so any phone requirement in the submitted value is enforced immediately.
|
||||
|
||||
[Bulk jobs](/admin-api/bulk-jobs/) applies the same change to up to 1,000 accounts as a queued `update_suspicious_activity_flags` task.
|
||||
|
||||
### Side effects
|
||||
|
||||
[User Update](/gateway/events/#user-update) is emitted to the account's own sessions.
|
||||
|
||||
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_suspicious_activity_flags`, target type `user`, and a metadata key `flags` with the submitted value. A preserved deferral bit makes that value differ from the flags the account ends up with.
|
||||
|
||||
### Rate limit
|
||||
|
||||
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
||||
|
||||
## Disable user for suspicious activity
|
||||
|
||||
<RouteHeader method="PUT" path="/v1/admin/users/{user_id}/suspicious-activity-disablement" auditReason />
|
||||
|
||||
Disables the account, replaces its [suspicious activity flags](#suspicious-activity-flags), destroys its password, and returns the resulting account. Requires `user:disable:suspicious`.
|
||||
|
||||
:::caution[The stored password is destroyed]
|
||||
The account holder cannot sign in with their old password after regaining access, and recovery requires [Send password reset](#send-password-reset) or the ordinary self-service reset flow.
|
||||
:::
|
||||
|
||||
### Path parameters
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| user_id | snowflake | The ID of the target account |
|
||||
|
||||
### JSON body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| flags<sup>1</sup> | integer | Replacement [suspicious activity flags](#suspicious-activity-flags) |
|
||||
|
||||
<sup>1</sup> Required. The value replaces the complete stored bitfield. Unlike [Update suspicious activity flags](#update-suspicious-activity-flags), the deferral bit is never preserved
|
||||
|
||||
### Response body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| user | [Admin user](#admin-user-object) object | The resulting account |
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | response body | The account was disabled |
|
||||
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER`, because the account does not exist |
|
||||
|
||||
Fluxer adds the `DISABLED_SUSPICIOUS_ACTIVITY` [account flag](#account-flags), so every other stored flag survives. No dedicated operation clears it. [Unban user](#unban-user) clears only `DISABLED`, and [Update user flags](#update-user-flags) can remove the bit like any other.
|
||||
|
||||
### Side effects
|
||||
|
||||
The account is marked with `DISABLED_SUSPICIOUS_ACTIVITY`, its suspicious activity flags are replaced, and its password hash is set to null. Every authentication session is then deleted, so the account is signed out on every device.
|
||||
|
||||
Fluxer emails the account holder when the account has an email address.
|
||||
|
||||
[User Update](/gateway/events/#user-update) is emitted after the sessions have already been deleted, so no connection of the account remains to receive it. The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `disable_suspicious_activity`, target type `user`, and a metadata key `flags`.
|
||||
|
||||
### Rate limit
|
||||
|
||||
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
||||
|
||||
## Ban user
|
||||
|
||||
<RouteHeader method="PUT" path="/v1/admin/users/{user_id}/ban" auditReason />
|
||||
@@ -1570,7 +1394,7 @@ The notification email quotes the `X-Audit-Log-Reason` value verbatim as the rea
|
||||
|
||||
### Side effects
|
||||
|
||||
`temp_banned_until` is cleared and `DISABLED` is removed from the account flags. `DISABLED_SUSPICIOUS_ACTIVITY` is a different flag and is not cleared, so an account disabled by [Disable user for suspicious activity](#disable-user-for-suspicious-activity) stays disabled.
|
||||
`temp_banned_until` is cleared and `DISABLED` is removed from the account flags.
|
||||
|
||||
Deleted sessions are not restored. The account holder is emailed when the account has an email address. [User Update](/gateway/events/#user-update) is emitted to the account's own sessions.
|
||||
|
||||
|
||||
@@ -124,24 +124,7 @@ Enforcement applies at those operations only, and does not gate password change
|
||||
|
||||
## Account state gates
|
||||
|
||||
An ordinary authenticated operation rejects an account that has an unmet suspicious activity requirement with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`. Each set flag in the response is one requirement the account has not met. A flag no longer appears in the response once the account meets that requirement.
|
||||
|
||||
### Account suspicious activity body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| data | object | An object whose `suspicious_activity_flags` member is the integer [suspicious activity flag](/admin-api/users/#suspicious-activity-flags) bitfield still outstanding |
|
||||
|
||||
A route that explicitly admits an account with an unmet suspicious activity requirement still accepts its credential. These stay reachable while a requirement is outstanding:
|
||||
|
||||
- [Get current user](/http-api/users/current-user/#get-current-user) and [Modify current user](/http-api/users/current-user/#modify-current-user).
|
||||
- [Get current user settings](/http-api/users/settings/#get-current-user-settings).
|
||||
- The [email change flow](/http-api/users/email-and-password/) with its bounced-address variants, and the email verification resend.
|
||||
- The [phone verification](/http-api/users/phone-verification/) flow.
|
||||
- Session listing and session termination.
|
||||
- The application and authorisation management operations in [Applications](/http-api/applications/) and [OAuth2](/http-api/oauth2/).
|
||||
|
||||
An Admin operation applies no suspicious activity gate.
|
||||
A [limited account](/http-api/users/#account-limitation) keeps its credentials and sessions. The operations listed there return 403 `ACCOUNT_LIMITED`, and every other operation accepts the account as usual.
|
||||
|
||||
No shared gate rejects a deleted or disabled account. Each operation that reads account state applies its own rule, and login, password, and email operations refuse a deleted account outright.
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ Fluxer accepts a non-string `status`. Null and a Boolean publish the session as
|
||||
|
||||
`offline` is normalised to `invisible`, so a Presence Update cannot publish a session as offline while it is connected.
|
||||
|
||||
`custom_status` is replaced only when the key is present. Omitting the key keeps the current custom status, `null` clears it, and a value that is neither an object nor null is ignored. An object the backend rejects, such as an `emoji_id` that names no emoji or an `expires_at` in the past, leaves the current custom status in place. The connection stays open.
|
||||
`custom_status` is replaced only when the key is present. Omitting the key keeps the current custom status, `null` clears it, and a value that is neither an object nor null is ignored. An object the backend rejects, such as an `emoji_id` that names no emoji, an `expires_at` in the past, or any object from a [limited account](/http-api/users/#account-limitation), leaves the current custom status in place. The connection stays open.
|
||||
|
||||
The published presence has a [custom status](#custom-status-object) object and no activities.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ An application is an OAuth2 client owned by one user account, and every bot acco
|
||||
|
||||
## Access rules
|
||||
|
||||
Every route except [Get public application](#get-public-application) requires a credential. A user-only route rejects a bot token and an OAuth2 bearer with 403 `ACCESS_DENIED`. Every route on this page accepts an account that has an outstanding [required action](/http-api/users/#required-actions).
|
||||
Every route except [Get public application](#get-public-application) requires a credential. A user-only route rejects a bot token and an OAuth2 bearer with 403 `ACCESS_DENIED`.
|
||||
|
||||
The sudo-gated routes read `X-Fluxer-Sudo-Mode-JWT`. A valid sudo token in that header, issued to the authenticated account, satisfies [sudo mode](/http-api/users/mfa/#sudo-mode) with no body proof. Fluxer returns the same token in the `X-Fluxer-Sudo-Mode-JWT` response header. Fluxer issues no token to an account holding neither a TOTP secret nor a registered WebAuthn credential, so that account proves sudo mode with `password` in the body.
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ The Authentication resource covers signing up, signing in, recovering an account
|
||||
|
||||
## Shared behaviour
|
||||
|
||||
[Authentication](/authentication/) and [HTTP authentication](/http-api/#authentication) define credential syntax and the credential namespaces. Changing the credentials of an account that is already signed in is covered by [Email and password changes](/http-api/users/email-and-password/), [Multi-factor authentication](/http-api/users/mfa/), and [Phone verification](/http-api/users/phone-verification/).
|
||||
[Authentication](/authentication/) and [HTTP authentication](/http-api/#authentication) define credential syntax and the credential namespaces. Changing the credentials of an account that is already signed in is covered by [Email and password changes](/http-api/users/email-and-password/) and [Multi-factor authentication](/http-api/users/mfa/).
|
||||
|
||||
:::caution[Authentication material is confidential and opaque]
|
||||
A client MUST treat every session token, one-use token, ticket, SSO state, and handoff code as confidential. It MUST NOT parse one, expose it to untrusted code, or write it to logs, analytics, or a URL the operation did not assign.
|
||||
:::
|
||||
|
||||
Most operations need no `Authorization` credential, because the request has a one-use token, MFA ticket, WebAuthn assertion, or account credential of its own. [List authentication sessions](#list-authentication-sessions), [terminate authentication sessions](#terminate-authentication-sessions), [resend email verification](#resend-email-verification), [log out](#log-out), [complete desktop handoff](#complete-desktop-handoff), and [create origin handoff](#create-origin-handoff) are the only operations on this page that read an `Authorization` credential. Complete desktop handoff reads the header only when its body omits the `token` field. The first three and create origin handoff require an ordinary user session, and each rejects a bot token and an OAuth2 bearer token with 403 `ACCESS_DENIED`. All four still admit a session whose account is flagged for suspicious activity.
|
||||
Most operations need no `Authorization` credential, because the request has a one-use token, MFA ticket, WebAuthn assertion, or account credential of its own. [List authentication sessions](#list-authentication-sessions), [terminate authentication sessions](#terminate-authentication-sessions), [resend email verification](#resend-email-verification), [log out](#log-out), [complete desktop handoff](#complete-desktop-handoff), and [create origin handoff](#create-origin-handoff) are the only operations on this page that read an `Authorization` credential. Complete desktop handoff reads the header only when its body omits the `token` field. The first three and create origin handoff require an ordinary user session, and each rejects a bot token and an OAuth2 bearer token with 403 `ACCESS_DENIED`.
|
||||
|
||||
Every route has a route bucket and is also subject to the [global HTTP limit](/topics/rate-limits/). A route bucket is keyed by the authenticated user when the request has a resolvable credential and by the client IP address otherwise. Bucket or global denial returns 429 `RATE_LIMITED`.
|
||||
|
||||
@@ -834,7 +834,7 @@ Success consumes the challenge, clears an expired temporary suspension, and crea
|
||||
|
||||
Revokes the session identified by the user session token in the `Authorization` header. Returns 204 with no body.
|
||||
|
||||
The request has no body. The operation requires a credential that resolves to an account, and it admits one flagged for suspicious activity. An absent, malformed, or unknown token returns 401 `UNAUTHORIZED`. An OAuth2 bearer token returns 403 `ACCESS_DENIED`. A bot holds no session, so a bot token is accepted and revokes nothing.
|
||||
The request has no body. The operation requires a credential that resolves to an account. An absent, malformed, or unknown token returns 401 `UNAUTHORIZED`. An OAuth2 bearer token returns 403 `ACCESS_DENIED`. A bot holds no session, so a bot token is accepted and revokes nothing.
|
||||
|
||||
:::note[Logging out revokes exactly the calling session]
|
||||
The account's other sessions, its OAuth2 grants, and any outstanding MFA ticket, IP authorisation ticket, or desktop handoff all survive. Use [terminate authentication sessions](#terminate-authentication-sessions) to revoke other sessions.
|
||||
@@ -890,7 +890,7 @@ Fluxer never compares the account's address against the one the token was issued
|
||||
|
||||
### Side effects
|
||||
|
||||
The operation marks the current address verified, clears its bounced state, and clears every suspicious activity flag that verifying or reverifying an email address satisfies. It emits a [User Update](/gateway/events/#user-update) Gateway Dispatch to the account's own sessions.
|
||||
The operation marks the current address verified and clears its bounced state. It emits a [User Update](/gateway/events/#user-update) Gateway Dispatch to the account's own sessions.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -902,7 +902,7 @@ The operation marks the current address verified, clears its bounced state, and
|
||||
|
||||
Issues and sends a new email verification token for the authenticated account. Requires a user session token for an ordinary user. Returns 204 with no body.
|
||||
|
||||
The request has no body. When the current address is already verified and no reverification suspicious activity flag is set, the operation returns 204 and sends nothing.
|
||||
The request has no body. When the current address is already verified, the operation returns 204 and sends nothing.
|
||||
|
||||
### Response
|
||||
|
||||
@@ -1592,7 +1592,7 @@ The sending origin encrypts the state and keeps the key. Fluxer never receives t
|
||||
| nonce_hash | string | The SHA-256 digest of the receiving origin's nonce, as 64 lowercase hex characters |
|
||||
| payload | string | The encrypted client state as base64url, 1 to 8388608 characters |
|
||||
|
||||
A bot token and an OAuth2 bearer token both return 403 `ACCESS_DENIED`. A session whose account is flagged for suspicious activity is admitted.
|
||||
A bot token and an OAuth2 bearer token both return 403 `ACCESS_DENIED`.
|
||||
|
||||
### Response
|
||||
|
||||
|
||||
@@ -654,6 +654,7 @@ Fluxer evaluates the target's admission policy in this order.
|
||||
| 400 | [error response](/http-api/#error-response) | The group is already full and the request returns `MAX_GROUP_DM_RECIPIENTS` |
|
||||
| 400 | [error response](/http-api/#error-response) | The channel is not a group direct message and the request returns `INVALID_CHANNEL_TYPE` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is not a recipient, or the target's admission policy rejects the caller, each returning `MISSING_ACCESS` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Channel does not exist and the request returns `UNKNOWN_CHANNEL` |
|
||||
|
||||
The `MAX_GROUP_DM_RECIPIENTS` body has `max_recipients`, the exact ceiling that was reached. That ceiling is the deployment's [`max_group_dm_recipients`](/http-api/instance/#limit-keys) limit resolved for the caller, and it defaults to 50.
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A connection links a Fluxer account to a verified domain or Bluesky account. Visible connections appear in `connected_accounts` on the [full user profile object](/http-api/users/#full-user-profile-object).
|
||||
|
||||
Connection routes require a user token. Bot tokens and OAuth2 bearers receive 403 `ACCESS_DENIED`, except that [List connections](#list-connections) accepts a bearer with the `connections` [scope](/http-api/oauth2/#oauth2-scopes). Accounts with an outstanding [required action](/http-api/users/#required-actions) receive 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`. Connection IDs are not snowflakes.
|
||||
Connection routes require a user token. Bot tokens and OAuth2 bearers receive 403 `ACCESS_DENIED`, except that [List connections](#list-connections) accepts a bearer with the `connections` [scope](/http-api/oauth2/#oauth2-scopes). Connection IDs are not snowflakes.
|
||||
|
||||
[Bluesky client metadata](#get-bluesky-client-metadata) and [JWKS](#get-bluesky-jwks) are public and contain no account data.
|
||||
|
||||
@@ -167,6 +167,7 @@ Only the `domain` type is accepted. A Bluesky connection is created through [Sta
|
||||
| --- | --- | --- |
|
||||
| 201 | [connection verification](#connection-verification-object) object | Verification was started |
|
||||
| 400 | [error response](/http-api/#error-response) | The requested type is bsky and the request returns `BLUESKY_OAUTH_NOT_ENABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
| 409 | [error response](/http-api/#error-response) | A connection of the same type already exists for that identifier, compared case-insensitively, and the request returns `CONNECTION_ALREADY_EXISTS` |
|
||||
|
||||
### Side effects
|
||||
@@ -201,6 +202,7 @@ Use the initiation token returned for this domain and account. The connection li
|
||||
| 201 | [connection](#connection-object) object | Proof succeeded and the connection was created |
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | The initiation token is not usable and the request returns `CONNECTION_INITIATION_TOKEN_INVALID` |
|
||||
| 403 | [error response](/http-api/#error-response) | The proof failed and the request returns `CONNECTION_VERIFICATION_FAILED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
| 409 | [error response](/http-api/#error-response) | The identifier is already linked (`CONNECTION_ALREADY_EXISTS`), or the connection list changed during the request (`CONFLICT`). Fetch the list before retrying |
|
||||
|
||||
<sup>1</sup> Invalid, expired or wrong-account initiation tokens return the same code
|
||||
@@ -343,6 +345,7 @@ Starts or renews a Bluesky connection. Bluesky must be enabled on the instance.
|
||||
| --- | --- | --- |
|
||||
| 200 | [Bluesky authorisation](#bluesky-authorisation-object) object | Send the user to `authorize_url` |
|
||||
| 400 | [error response](/http-api/#error-response) | Unavailable (`BLUESKY_OAUTH_NOT_ENABLED`) or failed (`BLUESKY_OAUTH_AUTHORIZATION_FAILED`) |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
|
||||
### Completing the flow
|
||||
|
||||
|
||||
@@ -297,7 +297,6 @@ The caller needs no permission and no relationship to the matched guilds.
|
||||
| 200 | [discovery search result](#discovery-search-result-object) object | Search completed, possibly with no match |
|
||||
| 400 | [error response](/http-api/#error-response) | Discovery is disabled for the instance and the request returns `DISCOVERY_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential or a revoked account and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | No search backend is configured for the instance and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
|
||||
### Rate limit
|
||||
@@ -319,7 +318,7 @@ A client that wants a translated label supplies its own translation keyed on `id
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | array[[discovery category](#discovery-category-object) object] | Categories were returned |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential or a revoked account and the request returns `ACCESS_DENIED`, or the account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential or a revoked account and the request returns `ACCESS_DENIED` |
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -353,7 +352,7 @@ A pending, rejected, removed, or absent application fails with 400 `DISCOVERY_NO
|
||||
| 400 | [error response](/http-api/#error-response) | The caller already holds the maximum number of guilds and the request returns `MAX_GUILDS` |
|
||||
| 400 | [error response](/http-api/#error-response) | The guild is full and the request returns `MAX_GUILD_MEMBERS` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot, presents a bearer credential, or holds a revoked account and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is limited and not already a member, and the request returns `ACCOUNT_LIMITED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The guild has the invites-disabled feature and the request returns `INVITES_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is banned from the guild directly or by address and the request returns `USER_BANNED_FROM_GUILD` or `USER_IP_BANNED_FROM_GUILD` |
|
||||
| 404 | [error response](/http-api/#error-response) | The approved application names a guild whose record no longer exists and the request returns `UNKNOWN_GUILD` |
|
||||
@@ -415,7 +414,6 @@ Its submission time is also its review time, it gains the discoverable feature,
|
||||
| 400 | [error response](/http-api/#error-response) | Discovery is disabled for the instance and the request returns `DISCOVERY_DISABLED`, or the guild has fewer members than the instance requires and the request returns `DISCOVERY_INSUFFICIENT_MEMBERS` |
|
||||
| 400 | [error response](/http-api/#error-response) | The caller cannot use [MANAGE_GUILD](/http-api/permissions/) because of the guild MFA level and the request returns `TWO_FACTOR_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential or a revoked account and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The guild is marked unavailable and the request returns `MISSING_ACCESS` |
|
||||
| 403 | [error response](/http-api/#error-response) | The description matches a content blocklist and the request returns `CONTENT_BLOCKED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller lacks [MANAGE_GUILD](/http-api/permissions/) and the request returns `MISSING_PERMISSIONS` |
|
||||
@@ -471,7 +469,6 @@ Every field is optional, and an omitted field preserves the stored value.
|
||||
| 400 | [error response](/http-api/#error-response) | Discovery is disabled for the instance and the request returns `DISCOVERY_DISABLED` |
|
||||
| 400 | [error response](/http-api/#error-response) | The caller cannot use [MANAGE_GUILD](/http-api/permissions/) because of the guild MFA level and the request returns `TWO_FACTOR_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential or a revoked account and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The guild is marked unavailable and the request returns `MISSING_ACCESS` |
|
||||
| 403 | [error response](/http-api/#error-response) | The description matches a content blocklist and the request returns `CONTENT_BLOCKED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller lacks [MANAGE_GUILD](/http-api/permissions/) and the request returns `MISSING_PERMISSIONS` |
|
||||
@@ -511,7 +508,6 @@ The stored review time, review reason, removal time, and removal reason are dele
|
||||
| 400 | [error response](/http-api/#error-response) | Discovery is disabled for the instance and the request returns `DISCOVERY_DISABLED` |
|
||||
| 400 | [error response](/http-api/#error-response) | The caller cannot use [MANAGE_GUILD](/http-api/permissions/) because of the guild MFA level and the request returns `TWO_FACTOR_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential or a revoked account and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The guild is marked unavailable and the request returns `MISSING_ACCESS` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller lacks [MANAGE_GUILD](/http-api/permissions/) and the request returns `MISSING_PERMISSIONS` |
|
||||
| 404 | [error response](/http-api/#error-response) | Guild does not exist and the request returns `UNKNOWN_GUILD` |
|
||||
@@ -546,7 +542,6 @@ The route answers even while discovery is disabled for the instance, reporting `
|
||||
| 200 | [discovery status](#discovery-status-object) object | Status was returned |
|
||||
| 400 | [error response](/http-api/#error-response) | The caller cannot use [MANAGE_GUILD](/http-api/permissions/) because of the guild MFA level and the request returns `TWO_FACTOR_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential or a revoked account and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The guild is marked unavailable and the request returns `MISSING_ACCESS` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller lacks [MANAGE_GUILD](/http-api/permissions/) and the request returns `MISSING_PERMISSIONS` |
|
||||
| 404 | [error response](/http-api/#error-response) | Guild does not exist and the request returns `UNKNOWN_GUILD` |
|
||||
|
||||
@@ -202,6 +202,7 @@ The validation entry has no member naming which duration bound the clip crossed.
|
||||
| --- | --- | --- |
|
||||
| 201 | [entrance sound](#entrance-sound-object) object | Clip was stored |
|
||||
| 400 | [error response](/http-api/#error-response) | Name or audio fails [validation](#validation) |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -303,6 +304,7 @@ A `guild:{guild_id}` scope does not require current guild membership. A non-null
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | Selection was written or cleared |
|
||||
| 400 | [error response](/http-api/#error-response) | Scope is unrecognised and the request returns `INVALID_FORMAT` at the `scope_id` path, or the account owns no such clip and the request returns `ENTRANCE_SOUND_NOT_FOUND` at the `sound_id` path |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
|
||||
@@ -132,6 +132,10 @@ Several source messages append a further recovery sentence with a template value
|
||||
|
||||
You don't have access to this resource or feature
|
||||
|
||||
### `ACCOUNT_LIMITED`
|
||||
|
||||
Your account is limited
|
||||
|
||||
### `ACCOUNT_SUSPENDED_PERMANENTLY`
|
||||
|
||||
This account has been permanently suspended
|
||||
@@ -140,10 +144,6 @@ This account has been permanently suspended
|
||||
|
||||
This account has been temporarily suspended
|
||||
|
||||
### `ACCOUNT_SUSPICIOUS_ACTIVITY`
|
||||
|
||||
Your account is locked due to suspicious activity
|
||||
|
||||
### `ACCOUNT_TOO_NEW_FOR_GUILD`
|
||||
|
||||
Your account is too new to send messages in this community
|
||||
@@ -484,10 +484,6 @@ You don't have permission to create communities on this instance
|
||||
|
||||
Email verification is required for this action
|
||||
|
||||
### `GUILD_PHONE_VERIFICATION_REQUIRED`
|
||||
|
||||
You need to add a phone number to send messages in this community
|
||||
|
||||
### `GUILD_TEMPLATE_INVALID`
|
||||
|
||||
The community template data is invalid or malformed
|
||||
@@ -592,14 +588,6 @@ Permissions must be a valid integer
|
||||
|
||||
Permissions must be non-negative
|
||||
|
||||
### `INVALID_PHONE_NUMBER`
|
||||
|
||||
Invalid phone number
|
||||
|
||||
### `INVALID_PHONE_VERIFICATION_CODE`
|
||||
|
||||
Invalid phone verification code
|
||||
|
||||
### `INVALID_REQUEST`
|
||||
|
||||
Invalid request
|
||||
@@ -612,10 +600,6 @@ Invalid stream key format
|
||||
|
||||
Invalid stream thumbnail payload
|
||||
|
||||
### `INVALID_SUSPICIOUS_FLAGS_FORMAT`
|
||||
|
||||
Invalid suspicious flags format
|
||||
|
||||
### `INVALID_SYSTEM_FLAG`
|
||||
|
||||
Invalid system flag
|
||||
@@ -828,50 +812,6 @@ NSFW content is age restricted
|
||||
|
||||
Passkey authentication failed
|
||||
|
||||
### `PHONE_ADD_NOT_ELIGIBLE`
|
||||
|
||||
You are not eligible to add a phone number to your account
|
||||
|
||||
### `PHONE_ALREADY_USED`
|
||||
|
||||
Phone number is already in use
|
||||
|
||||
### `PHONE_COUNTRY_NOT_SUPPORTED`
|
||||
|
||||
We don't send verification texts to this country. Use a mobile number from another country, or email support@fluxer.app and a person will review your account
|
||||
|
||||
### `PHONE_GATE_ESCAPE_UNAVAILABLE`
|
||||
|
||||
This account cannot postpone the phone verification check
|
||||
|
||||
### `PHONE_INBOUND_VERIFICATION_REQUIRED`
|
||||
|
||||
This number is verified by texting us instead of us texting you. Start phone verification again to get the code and the number to text
|
||||
|
||||
### `PHONE_LOOKUP_UNAVAILABLE`
|
||||
|
||||
Our phone number check is down right now, so we stopped before sending your code. This is on us, not your number. Wait a few minutes and try the same number again
|
||||
|
||||
### `PHONE_NUMBER_NOT_IN_SERVICE`
|
||||
|
||||
Your carrier says this number isn't in service. Check the number and try again, or email support@fluxer.app if it's correct
|
||||
|
||||
### `PHONE_NUMBER_NOT_MOBILE`
|
||||
|
||||
This isn't a mobile number, so it can't receive our text. Use a mobile number, or email support@fluxer.app if you think that's wrong
|
||||
|
||||
### `PHONE_RATE_LIMIT_EXCEEDED`
|
||||
|
||||
Phone rate limit exceeded
|
||||
|
||||
### `PHONE_VERIFICATION_NEEDS_REVIEW`
|
||||
|
||||
We couldn't verify this number automatically. Email support@fluxer.app and a person will review your account
|
||||
|
||||
### `PHONE_VERIFICATION_REQUIRED`
|
||||
|
||||
Phone verification is required
|
||||
|
||||
### `PREMIUM_PURCHASE_BLOCKED`
|
||||
|
||||
No active subscription
|
||||
@@ -964,10 +904,6 @@ You cannot leave the community for this instance
|
||||
|
||||
Slowmode rate limited
|
||||
|
||||
### `SMS_VERIFICATION_UNAVAILABLE`
|
||||
|
||||
Service unavailable
|
||||
|
||||
### `SSO_REQUIRED`
|
||||
|
||||
Invalid request
|
||||
@@ -1208,10 +1144,6 @@ Unknown sticker
|
||||
|
||||
Unknown store purchase
|
||||
|
||||
### `UNKNOWN_SUSPICIOUS_FLAG`
|
||||
|
||||
Unknown suspicious flag
|
||||
|
||||
### `UNKNOWN_USER`
|
||||
|
||||
User wasn't found
|
||||
@@ -1945,10 +1877,6 @@ String length must be between {min} and {max} characters
|
||||
|
||||
Password isn't set
|
||||
|
||||
### `PHONE_NUMBER_INVALID_FORMAT`
|
||||
|
||||
Phone number must be in E.164 format (for example, +1234567890)
|
||||
|
||||
### `PRECEDING_CHANNEL_MUST_SHARE_PARENT`
|
||||
|
||||
Preceding channel must share the same parent as the moved channel
|
||||
|
||||
@@ -170,7 +170,6 @@ Fluxer does not stem, expand, or spell-correct the term. A term that matches not
|
||||
| 200 | array[[GIF](#gif-object) object] | Results were returned, possibly as an empty array |
|
||||
| 400 | [error response](/http-api/#error-response) | `q` is absent, empty, or longer than 256 characters, or `locale` is outside the registry, each returning `INVALID_FORM_BODY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The instance has bound no provider key and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 503 | [error response](/http-api/#error-response) | The provider failed, the request outlived its deadline, or the `gifs` service answered with a payload the API could not read, each returning `SERVICE_UNAVAILABLE` |
|
||||
|
||||
@@ -210,7 +209,6 @@ Fluxer resolves the category list against the country `US` for every request, wh
|
||||
| 200 | response body | Featured GIFs and categories were returned |
|
||||
| 400 | [error response](/http-api/#error-response) | `locale` is outside the registry and the request returns `INVALID_FORM_BODY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The instance has bound no provider key and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 503 | [error response](/http-api/#error-response) | The provider failed, the request outlived its deadline, or the `gifs` service answered with a payload the API could not read, each returning `SERVICE_UNAVAILABLE` |
|
||||
|
||||
@@ -239,7 +237,6 @@ Returns at most 50 trending [GIF objects](#gif-object) for the resolved locale a
|
||||
| 200 | array[[GIF](#gif-object) object] | Trending GIFs were returned, possibly as an empty array |
|
||||
| 400 | [error response](/http-api/#error-response) | `locale` is outside the registry and the request returns `INVALID_FORM_BODY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The instance has bound no provider key and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 503 | [error response](/http-api/#error-response) | The provider failed, the request outlived its deadline, or the `gifs` service answered with a payload the API could not read, each returning `SERVICE_UNAVAILABLE` |
|
||||
|
||||
@@ -271,7 +268,6 @@ Suggestions depend on the locale and query, not the requesting address.
|
||||
| 200 | array[string] | Suggestions were returned, possibly as an empty array |
|
||||
| 400 | [error response](/http-api/#error-response) | `q` is absent, empty, or longer than 256 characters, or `locale` is outside the registry, each returning `INVALID_FORM_BODY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The instance has bound no provider key and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 503 | [error response](/http-api/#error-response) | The provider failed, the request outlived its deadline, or the `gifs` service answered with a payload the API could not read, each returning `SERVICE_UNAVAILABLE` |
|
||||
|
||||
@@ -308,7 +304,6 @@ The deadline on this route is 3 seconds.
|
||||
| 204 | empty | The provider accepted the registration |
|
||||
| 400 | [error response](/http-api/#error-response) | `id` is absent, empty, or longer than 300 characters, `q` is longer than 256 characters, or `locale` is outside the registry, each returning `INVALID_FORM_BODY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The instance has bound no provider key and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 503 | [error response](/http-api/#error-response) | The provider rejected the registration, the `gifs` service did not answer within the deadline, or it answered with something other than an acknowledgement, each returning `SERVICE_UNAVAILABLE` |
|
||||
|
||||
|
||||
@@ -271,6 +271,7 @@ The body is a [guild member update object](#guild-member-update-object) without
|
||||
| 403 | [error response](/http-api/#error-response) | Guild is unavailable, the caller is not a member, or the email address is unverified |
|
||||
| 403 | [error response](/http-api/#error-response) | Blocked content was supplied, the caller is timed out, or a required voice permission is absent |
|
||||
| 403 | [error response](/http-api/#error-response) | `communication_disabled_until` was supplied |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the body changes a profile field, and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Guild does not exist, or the membership record or account is absent |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -283,14 +283,13 @@ A verification level gates member participation. Fluxer evaluates it when a memb
|
||||
| 0 | NONE | No verification requirement |
|
||||
| 1 | LOW<sup>1</sup> | The account is claimed and its email address verified |
|
||||
| 2 | MEDIUM | The LOW requirement, and the account is at least 5 minutes old |
|
||||
| 3 | HIGH<sup>2</sup> | The MEDIUM requirement, and the membership is at least 10 minutes old |
|
||||
| 4 | VERY_HIGH<sup>3</sup> | The account has a verified phone number |
|
||||
| 3 | HIGH<sup>2</sup> <sup>3</sup> | The MEDIUM requirement, and the membership is at least 10 minutes old |
|
||||
|
||||
<sup>1</sup> A guild with `DISCOVERABLE` is evaluated at an effective minimum of LOW even when the stored value is NONE, and [Modify guild](#modify-guild) rejects lowering a discoverable guild below LOW with 400 `INVALID_FORM_BODY` and the field code `DISCOVERABLE_GUILD_VERIFICATION_LEVEL_TOO_LOW`
|
||||
|
||||
<sup>2</sup> The membership age requirement is skipped when the join timestamp cannot be read, so the level behaves as MEDIUM
|
||||
|
||||
<sup>3</sup> A verified phone number is the whole requirement at this level. A deployment whose [instance features](/http-api/instance/#instance-features-object) report `phone_verification_enabled` as false evaluates a stored VERY_HIGH as HIGH. [Modify guild](#modify-guild) then rejects a change to VERY_HIGH with 400 `INVALID_FORM_BODY` and the field code `VALUE_MUST_BE_INTEGER_IN_RANGE`, and a [guild template](#guild-creation-template-object) value is clamped to HIGH
|
||||
<sup>3</sup> A request value of 4 is stored as HIGH, and a [guild template](#guild-creation-template-object) value above HIGH is clamped to HIGH
|
||||
|
||||
The guild owner, a bot, and any member holding at least one role bypass the check at every level.
|
||||
|
||||
@@ -531,6 +530,7 @@ The creation response has no roles, channels, emojis, stickers, or member counts
|
||||
| 200 | [guild object](#guild-object) | Guild was created |
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | Body, image, template, bot or unclaimed credential, configured guild limit, or single community policy rejects creation |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | Email address is unverified, or the name is blocked |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
|
||||
<sup>1</sup> The [error code](/http-api/errors/) is `SINGLE_COMMUNITY_CANNOT_CREATE_GUILDS` while the single community policy is active, `BOTS_CANNOT_CREATE_GUILDS` for a bot credential, `UNCLAIMED_ACCOUNT_CANNOT_CREATE_GUILDS` for an unclaimed account, `MAX_GUILDS` at the configured guild limit, `GUILD_TEMPLATE_INVALID` for a rejected template, and `INVALID_FORM_BODY` otherwise
|
||||
|
||||
@@ -674,7 +674,7 @@ Every field is optional. An omitted field preserves its current value, and a fie
|
||||
|
||||
<sup>5</sup> The channel must exist in this guild and be a voice channel, and is otherwise rejected with `AFK_CHANNEL_MUST_BE_IN_GUILD` or `AFK_CHANNEL_MUST_BE_VOICE`
|
||||
|
||||
<sup>6</sup> A guild with `DISCOVERABLE` cannot be lowered below LOW and is rejected with the field code `DISCOVERABLE_GUILD_VERIFICATION_LEVEL_TOO_LOW`. A change to VERY_HIGH is rejected with the field code `VALUE_MUST_BE_INTEGER_IN_RANGE` when phone verification is unavailable, as [Verification levels](#verification-levels) states
|
||||
<sup>6</sup> A guild with `DISCOVERABLE` cannot be lowered below LOW and is rejected with the field code `DISCOVERABLE_GUILD_VERIFICATION_LEVEL_TOO_LOW`.
|
||||
|
||||
<sup>7</sup> Sending the value the guild already holds needs neither ownership nor sudo mode, and an owner without a configured second factor is rejected with the field code `MUST_ENABLE_2FA_BEFORE_REQUIRING_FOR_MODS`
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ Those operations document their own JSON validation errors.
|
||||
|
||||
[Authentication](/authentication/) defines the accepted `Authorization` schemes and links to the OAuth2 scope registry. Each operation states which credentials it accepts. The [sudo verification object](/http-api/users/mfa/#sudo-verification-object) defines the proof required for sensitive account operations.
|
||||
|
||||
An OAuth2 bearer access token is accepted only where a route opts in, and the resource page says so. Everywhere else a bearer credential is refused with 403 `ACCESS_DENIED`. An account with a suspicious activity flag is refused with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
An OAuth2 bearer access token is accepted only where a route opts in, and the resource page says so. Everywhere else a bearer credential is refused with 403 `ACCESS_DENIED`.
|
||||
|
||||
## Standard request headers
|
||||
|
||||
@@ -274,7 +274,6 @@ Fluxer produces at most one entry for each distinct pair of `path` and `code`, s
|
||||
| [User settings Protobuf](/http-api/users/settings-protobuf/) | Every structured client preference message and enumeration |
|
||||
| [Email and password changes](/http-api/users/email-and-password/) | The ticketed credential replacement flows |
|
||||
| [Multi-factor authentication](/http-api/users/mfa/) | TOTP, backup codes, WebAuthn credentials, sudo verification |
|
||||
| [Phone verification](/http-api/users/phone-verification/) | Outbound and inbound phone verification |
|
||||
| [Relationships](/http-api/users/relationships/) | Friend requests, friendships, blocks, relationship nicknames |
|
||||
| [User notes](/http-api/users/notes/) | Private notes attached to user IDs |
|
||||
| [Private channels](/http-api/users/private-channels/) | Direct message and group DM discovery, creation, preload, pin state |
|
||||
|
||||
@@ -138,7 +138,7 @@ Deployment-wide switches a client reads before it offers a feature, plus whether
|
||||
| self_hosted | boolean | Whether this deployment identifies itself as self-hosted |
|
||||
| presigned_attachment_uploads | boolean | Whether a client can request presigned attachment upload URLs |
|
||||
| emails_enabled<sup>1</sup> | boolean | Whether the deployment sends email |
|
||||
| phone_verification_enabled<sup>5</sup> | boolean | Whether accounts can verify a phone number |
|
||||
| phone_verification_enabled | boolean | Deprecated. Always false |
|
||||
|
||||
<sup>1</sup> The value is true only when email is switched on and the transport is completely configured
|
||||
|
||||
@@ -148,8 +148,6 @@ Deployment-wide switches a client reads before it offers a feature, plus whether
|
||||
|
||||
<sup>4</sup> On a hosted deployment, true while billing is switched on and a Stripe secret key is set. A self-hosted deployment reports true while a Stripe secret key is set and `premium_enabled` is true, even after billing is switched off
|
||||
|
||||
<sup>5</sup> Defaults to true on a hosted deployment and false on a self-hosted one. While it is false, the `VERY_HIGH` [verification level](/http-api/guilds/#verification-levels) is evaluated as `HIGH`
|
||||
|
||||
A deployment that reports `emails_enabled` as false sends no verification, password recovery, or IP authorisation message, and the flows that depend on one are unusable there. [Deployment availability](/http-api/deployment-availability/) states which routes a self-hosted deployment does not serve at all.
|
||||
|
||||
## GIF provider object
|
||||
|
||||
@@ -220,6 +220,7 @@ Accepting an exhausted invite returns 404 `UNKNOWN_INVITE` and makes the code st
|
||||
| 403 | [error response](/http-api/#error-response) | Instant invites are temporarily disabled for the guild and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is banned by account or email address and the request returns `USER_BANNED_FROM_GUILD` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is banned by network address and the request returns `USER_IP_BANNED_FROM_GUILD` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and not already a member or recipient, and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | The named guild no longer exists and the request returns `UNKNOWN_GUILD` |
|
||||
| 404 | [error response](/http-api/#error-response) | The target channel no longer exists and the request returns `UNKNOWN_CHANNEL` |
|
||||
| 404 | [error response](/http-api/#error-response) | The code resolves to no record, the record is exhausted, a guild invite names no guild, or a group direct message invite names no channel or a channel that no longer exists, each returning `UNKNOWN_INVITE` |
|
||||
|
||||
@@ -171,7 +171,6 @@ The account's [collection limit](#collection-limits) bounds how many entries the
|
||||
| --- | --- | --- |
|
||||
| 200 | array[[meme](#meme-object) object] | Collection was returned, possibly as an empty array |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -216,7 +215,6 @@ A URL that resolves to no usable media fails with 400 `MEDIA_METADATA_ERROR`. Sa
|
||||
| 400 | [error response](/http-api/#error-response) | The URL resolves to no usable media with `MEDIA_METADATA_ERROR` |
|
||||
| 400 | [error response](/http-api/#error-response) | The collection is at its limit with `MAX_FAVORITE_MEMES` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
|
||||
<sup>5</sup> Including `MAX_FAVORITE_MEME_TAGS_EXCEEDED` at the `tags` path and `MEDIA_ALREADY_IN_FAVORITE_MEMES` at the `media` path
|
||||
|
||||
@@ -274,7 +272,6 @@ An embed selection uses its image, video, or thumbnail, in that order. Media who
|
||||
| 400 | [error response](/http-api/#error-response) | External media metadata cannot be resolved with `MEDIA_METADATA_ERROR` |
|
||||
| 400 | [error response](/http-api/#error-response) | The collection is at its limit with `MAX_FAVORITE_MEMES` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is not a member of the guild or lacks `VIEW_CHANNEL` and the request returns `MISSING_PERMISSIONS` |
|
||||
| 403 | [error response](/http-api/#error-response) | The channel requires age verification the account has not completed and the request returns `NSFW_CONTENT_AGE_RESTRICTED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Channel is unavailable or the caller is not a recipient of it with `UNKNOWN_CHANNEL` |
|
||||
@@ -314,7 +311,6 @@ Returns one [meme object](#meme-object) owned by the current account.
|
||||
| --- | --- | --- |
|
||||
| 200 | [meme](#meme-object) object | Meme was returned |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 404 | [error response](/http-api/#error-response) | Meme is unknown or belongs to another account with `UNKNOWN_FAVORITE_MEME` |
|
||||
|
||||
### Rate limit
|
||||
@@ -358,7 +354,6 @@ Fluxer checks the tag allowance against the resulting list, so a request omittin
|
||||
| 200 | [meme](#meme-object) object | Meme was updated |
|
||||
| 400 | [error response](/http-api/#error-response) | Path, name, alt text, or tag length fails validation with `INVALID_FORM_BODY`<sup>3</sup> |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 404 | [error response](/http-api/#error-response) | Meme is unknown or belongs to another account with `UNKNOWN_FAVORITE_MEME` |
|
||||
|
||||
<sup>3</sup> Including `MAX_FAVORITE_MEME_TAGS_EXCEEDED` at the `tags` path
|
||||
@@ -395,7 +390,6 @@ Every meme owns its stored bytes, so deletion removes them permanently. The meme
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | Meme was deleted, or no owned meme existed |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -447,7 +441,6 @@ An unresolved URL still returns an entry with its signed proxy URL and an empty
|
||||
| --- | --- | --- |
|
||||
| 200 | response body | One entry was returned for each URL, including URLs that did not resolve |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -1125,7 +1125,6 @@ Signs from 1 through 50 attachment URLs again. Returns one [refreshed attachment
|
||||
| 400 | [error response](/http-api/#error-response) | Body, URL count, or a URL length is invalid and the request returns `INVALID_FORM_BODY` |
|
||||
| 401 | [error response](/http-api/#error-response) | The credential is missing or invalid and the request returns `UNAUTHORIZED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
|
||||
:::caution[A refreshed URL is not an access check]
|
||||
Anyone who can authenticate can sign any attachment URL of the instance whose path they know. A signature limits how long a copied URL works. It does not decide who can read the file.
|
||||
@@ -1278,6 +1277,7 @@ The resolved `max_voice_message_duration` limit defaults to 1200 seconds.
|
||||
| 403 | [error response](/http-api/#error-response) | Is age restricted from the channel and the request returns `NSFW_CONTENT_AGE_RESTRICTED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Message sending is temporarily disabled for the guild and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | A limited caller messages an account that has not written in the direct message and the request returns `NEW_CONVERSATIONS_LIMITED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation), the channel is not its personal notes channel, and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Channel, referenced message, sticker, or guild does not exist or is unavailable, or the supplied nonce was last used in a different channel and the request returns `UNKNOWN_MESSAGE` |
|
||||
|
||||
:::caution[Slowmode denials are 400, not 429]
|
||||
@@ -1368,6 +1368,7 @@ An `id` that names no attachment on the message is skipped, and the edit still s
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is age restricted from the channel and the request returns `NSFW_CONTENT_AGE_RESTRICTED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Message sending is temporarily disabled for the guild and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | A limited author edits a direct message the other account has not written in and the request returns `NEW_CONVERSATIONS_LIMITED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation), the channel is not its personal notes channel, and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Channel or message does not exist |
|
||||
| 429 | [error response](/http-api/#error-response) | The published message edit allowance is used up, returning `PUBLISHED_MESSAGE_EDIT_RATE_LIMITED` |
|
||||
|
||||
@@ -1422,6 +1423,7 @@ Each announcement channel can publish 10 messages in a row, then one every 6 min
|
||||
| 403 | [error response](/http-api/#error-response) | The author is timed out, returning `COMMUNICATION_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The message matches an instance blocklist, returning `CONTENT_BLOCKED` |
|
||||
| 403<sup>1</sup> | [error response](/http-api/#error-response) | Publishing is disabled for the guild, returning `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Channel or message does not exist, or is older than the caller's history cutoff |
|
||||
| 429<sup>2</sup> | [error response](/http-api/#error-response) | The channel publish allowance is used up |
|
||||
| 429 | [error response](/http-api/#error-response) | A concurrent change holds the message, returning `RESOURCE_LOCKED` |
|
||||
@@ -1521,7 +1523,7 @@ Deletes the authenticated identity's read state entry for one channel. Returns 2
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | Read state was absent or deleted |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential type or account state denies the request and it returns `ACCESS_DENIED` or `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential type denies the request and it returns `ACCESS_DENIED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -1709,7 +1711,7 @@ Deletion has no age boundary and no confirmation step, and there is no restore o
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | response body | Every personal note was deleted |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential type or account state denies the request and it returns `ACCESS_DENIED` or `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential type denies the request and it returns `ACCESS_DENIED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Channel does not exist or the caller cannot resolve it |
|
||||
|
||||
### Side effects
|
||||
@@ -1943,7 +1945,7 @@ A channel the caller cannot see is acknowledged like any other whenever it store
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | Pin state was absent or acknowledged |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential type or account state denies the request and it returns `ACCESS_DENIED` or `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential type denies the request and it returns `ACCESS_DENIED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -2102,6 +2104,7 @@ Adds the authenticated identity's reaction. Returns 204 with an empty body. Emit
|
||||
| 403 | [error response](/http-api/#error-response) | The caller has an unverified email and the request returns `REACTION_EMAIL_VERIFICATION_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is under a new conversation limit in a direct message the recipient has not written in, and the request returns `NEW_CONVERSATIONS_LIMITED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Reactions are temporarily disabled for the guild and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Channel or message does not exist, or the message is outside the caller's message history cutoff |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -22,7 +22,7 @@ An authorisation code expires 10 minutes after it is issued, an access token aft
|
||||
|
||||
## Route authentication
|
||||
|
||||
Every route on this page that authenticates a user is user-only unless the operation says otherwise. Fluxer rejects a bot token and an OAuth2 bearer with 403 `ACCESS_DENIED`, and accepts an account with an outstanding required action. The token, introspection, and revocation routes authenticate the client application instead, so their [rate limit](/topics/rate-limits/) is keyed by client IP address.
|
||||
Every route on this page that authenticates a user is user-only unless the operation says otherwise. Fluxer rejects a bot token and an OAuth2 bearer with 403 `ACCESS_DENIED`. The token, introspection, and revocation routes authenticate the client application instead, so their [rate limit](/topics/rate-limits/) is keyed by client IP address.
|
||||
|
||||
## OAuth2 string normalisation
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ When any entry is manual or has a positive mention count, entries for the same c
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | response body | Acknowledgements were processed and the resulting entries returned |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED`, or the account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -168,7 +168,7 @@ Every entry advances its channel watermark monotonically and sets the channel's
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | Acknowledgements were processed |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED`, or the account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
|
||||
@@ -236,7 +236,6 @@ A reporter without `READ_MESSAGE_HISTORY` can report only a message at or after
|
||||
| 400 | [error response](/http-api/#error-response) | The account is unclaimed and the request returns `UNCLAIMED_ACCOUNT_CANNOT_SUBMIT_REPORTS`, or the reporter is the message author and the request returns `CANNOT_REPORT_OWN_MESSAGE` |
|
||||
| 403 | [error response](/http-api/#error-response) | A body string is blocked and the request returns `CONTENT_BLOCKED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The credential is a bot token or OAuth2 bearer and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Its email is unverified and the request returns `REPORT_EMAIL_VERIFICATION_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is report banned and the request returns `REPORT_BANNED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The reporter is not a guild member or lacks `VIEW_CHANNEL` and the request returns `MISSING_PERMISSIONS` |
|
||||
@@ -293,7 +292,6 @@ Creates and returns a [report object](#report-object) for another user, optional
|
||||
| 400 | [error response](/http-api/#error-response) | The account is unclaimed and the request returns `UNCLAIMED_ACCOUNT_CANNOT_SUBMIT_REPORTS`, or the reporter is the reported user and the request returns `CANNOT_REPORT_YOURSELF` |
|
||||
| 403 | [error response](/http-api/#error-response) | A body string is blocked and the request returns `CONTENT_BLOCKED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The credential is a bot token or OAuth2 bearer and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Its email is unverified and the request returns `REPORT_EMAIL_VERIFICATION_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is report banned and the request returns `REPORT_BANNED` |
|
||||
| 404 | [error response](/http-api/#error-response) | The reported user does not exist and the request returns `UNKNOWN_USER` |
|
||||
@@ -348,7 +346,6 @@ This route reads the bare code and accepts no invite URL. A code that resolves t
|
||||
| 400 | [error response](/http-api/#error-response) | The account is unclaimed and the request returns `UNCLAIMED_ACCOUNT_CANNOT_SUBMIT_REPORTS`, or the reporter owns the guild and the request returns `CANNOT_REPORT_OWN_GUILD` |
|
||||
| 403 | [error response](/http-api/#error-response) | A body string is blocked and the request returns `CONTENT_BLOCKED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The credential is a bot token or OAuth2 bearer and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Its email is unverified and the request returns `REPORT_EMAIL_VERIFICATION_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is report banned and the request returns `REPORT_BANNED` |
|
||||
| 403 | [error response](/http-api/#error-response) | Access to the guild cannot be established and the request returns `CANNOT_REPORT_GUILD` |
|
||||
|
||||
@@ -277,7 +277,6 @@ Content moderation scans every string in the body whose field name does not end
|
||||
| 200 | [message search result](#message-search-result-object) object \| [search indexing](#search-indexing-object) object | Search completed, or an index it needed is not queryable yet |
|
||||
| 400 | [error response](/http-api/#error-response) | A bot supplied a scope other than `current`, the `current` scope has no context, or a requested channel does not belong to the context guild, each returning `INVALID_FORM_BODY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller presents an OAuth2 bearer credential, or the guild of the context channel cannot be resolved while a stored guild record still exists, each returning `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | A body string is blocked by content moderation and the request returns `CONTENT_BLOCKED` |
|
||||
| 403 | [error response](/http-api/#error-response) | No search backend is configured for the instance and the request returns `FEATURE_TEMPORARILY_DISABLED` |
|
||||
| 403 | [error response](/http-api/#error-response) | A requested channel is outside the resolved scope, the caller is not a member of the context guild, or the caller cannot view the context channel, each returning `MISSING_PERMISSIONS` |
|
||||
|
||||
@@ -86,7 +86,6 @@ The object has the identifier and nothing else.
|
||||
| 201 | response body | Document was stored |
|
||||
| 400 | [error response](/http-api/#error-response) | The encoded document exceeds the size ceiling and the request returns `FILE_SIZE_TOO_LARGE` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | The document is blocked by content moderation and the request returns `CONTENT_BLOCKED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -97,7 +97,7 @@ It resolves precisely the URL in the request body and returns the complete resol
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | array[[embed](/http-api/messages/#embed-object) object] | Resolution completed, possibly with no embed |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED`, or the account has an outstanding required action and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller is a bot or presents a bearer credential and the request returns `ACCESS_DENIED` |
|
||||
| 502 | [error response](/http-api/#error-response) | The resolution service reports a failure, or answers with a payload the API cannot read, and the request returns `BAD_GATEWAY` |
|
||||
| 503 | [error response](/http-api/#error-response) | No resolution service is answering, or the service rejects the request at its concurrency limit, and the request returns `SERVICE_UNAVAILABLE` |
|
||||
| 504 | [error response](/http-api/#error-response) | The resolution service does not answer within its 12-second deadline and the request returns `GATEWAY_TIMEOUT` |
|
||||
|
||||
@@ -108,26 +108,26 @@ The private representation of the current account, returned by [Get current user
|
||||
| traits<sup>3</sup> <sup>4</sup> | array[string] | The account traits, in sorted order |
|
||||
| email<sup>5</sup> | ?string | The account email address, or null when none exists |
|
||||
| email_bounced?<sup>3</sup> | boolean | Whether the mail provider marked the current email as bounced |
|
||||
| phone<sup>6</sup> | ?string | Always null |
|
||||
| has_verified_phone | boolean | Whether phone verification is complete |
|
||||
| has_verified_phone | boolean | Deprecated. Always false |
|
||||
| bio | ?string | The profile biography, or null when none is set |
|
||||
| pronouns | ?string | The profile pronouns, or null when none are set |
|
||||
| accent_color | ?integer | The profile accent colour as packed 24-bit RGB, or null when none is set |
|
||||
| timezone?<sup>7</sup> | ?string | The IANA timezone identifier stored for the account, or null when none is set |
|
||||
| timezone_privacy_flags?<sup>7</sup> | integer | [Profile field privacy flags](#profile-field-privacy-flags) applied to the profile timezone |
|
||||
| banner<sup>8</sup> | ?string | The image hash of the profile banner, or null when none is set or the entitlement is missing |
|
||||
| timezone?<sup>6</sup> | ?string | The IANA timezone identifier stored for the account, or null when none is set |
|
||||
| timezone_privacy_flags?<sup>6</sup> | integer | [Profile field privacy flags](#profile-field-privacy-flags) applied to the profile timezone |
|
||||
| banner<sup>7</sup> | ?string | The image hash of the profile banner, or null when none is set or the entitlement is missing |
|
||||
| banner_color | ?integer | The packed 24-bit RGB colour derived from the stored banner, or null |
|
||||
| mfa_enabled<sup>3</sup> <sup>9</sup> | boolean | Whether any authenticator is configured |
|
||||
| authenticator_types?<sup>3</sup> <sup>9</sup> | array[integer] | [Authenticator types](#authenticator-types) configured for the account |
|
||||
| mfa_enabled<sup>3</sup> <sup>8</sup> | boolean | Whether any authenticator is configured |
|
||||
| authenticator_types?<sup>3</sup> <sup>8</sup> | array[integer] | [Authenticator types](#authenticator-types) configured for the account |
|
||||
| verified<sup>5</sup> | boolean | Whether the account email is verified |
|
||||
| premium_type<sup>10</sup> | ?integer | [Premium type](#premium-types) |
|
||||
| premium_since<sup>3</sup> <sup>10</sup> | ?ISO8601 timestamp | The time premium was first activated, or null |
|
||||
| premium_until<sup>3</sup> <sup>11</sup> | ?ISO8601 timestamp | The end of current premium access, or null when no premium period is stored |
|
||||
| account_limited? | boolean | Whether the account is [limited](#account-limitation) |
|
||||
| premium_type<sup>9</sup> | ?integer | [Premium type](#premium-types) |
|
||||
| premium_since<sup>3</sup> <sup>9</sup> | ?ISO8601 timestamp | The time premium was first activated, or null |
|
||||
| premium_until<sup>3</sup> <sup>10</sup> | ?ISO8601 timestamp | The end of current premium access, or null when no premium period is stored |
|
||||
| premium_will_cancel<sup>3</sup> | boolean | Whether subscription premium will cancel at the billing boundary |
|
||||
| premium_billing_cycle<sup>3</sup> | ?string | The premium billing cycle, or null |
|
||||
| premium_lifetime_sequence<sup>3</sup> | ?integer | The lifetime premium sequence, or null |
|
||||
| premium_grace_ends_at<sup>12</sup> | ?ISO8601 timestamp | The end of grace access after premium_until passes, or null when no grace is recorded |
|
||||
| premium_discriminator<sup>13</sup> | boolean | Whether the current discriminator was selected under a premium entitlement |
|
||||
| premium_grace_ends_at<sup>11</sup> | ?ISO8601 timestamp | The end of grace access after premium_until passes, or null when no grace is recorded |
|
||||
| premium_discriminator<sup>12</sup> | boolean | Whether the current discriminator was selected under a premium entitlement |
|
||||
| premium_badge_hidden<sup>3</sup> | boolean | Whether the premium badge is hidden from the public profile |
|
||||
| premium_badge_masked<sup>3</sup> | boolean | Whether a lifetime badge is presented as an ordinary subscription badge |
|
||||
| premium_badge_timestamp_hidden<sup>3</sup> | boolean | Whether the premium activation time is hidden from the public profile |
|
||||
@@ -135,15 +135,14 @@ The private representation of the current account, returned by [Get current user
|
||||
| premium_purchase_disabled<sup>3</sup> | boolean | Whether premium purchasing is disabled for the account |
|
||||
| premium_enabled_override<sup>3</sup> | boolean | Whether an administrative override grants premium entitlements |
|
||||
| premium_perks_disabled<sup>3</sup> | boolean | Whether premium entitlements are suspended for the account |
|
||||
| force_inbound_phone_verification?<sup>14</sup> | boolean | The debugging switch that forces the inbound phone verification flow |
|
||||
| password_last_changed_at<sup>3</sup> | ?ISO8601 timestamp | The time of the most recent password change, or null |
|
||||
| last_voice_activity_sharing_change_at<sup>15</sup> | ?ISO8601 timestamp | The time of the most recent bulk voice activity sharing change, or null |
|
||||
| required_actions<sup>3</sup> <sup>16</sup> | array[string] | The ordered [required actions](#required-actions) the account must complete before unrestricted use |
|
||||
| nsfw_allowed<sup>3</sup> <sup>17</sup> | boolean | Whether the account can access age-restricted content |
|
||||
| has_dismissed_premium_onboarding<sup>3</sup> <sup>18</sup> | boolean | Whether premium onboarding was dismissed |
|
||||
| last_voice_activity_sharing_change_at<sup>13</sup> | ?ISO8601 timestamp | The time of the most recent bulk voice activity sharing change, or null |
|
||||
| required_actions<sup>3</sup> | array[string] | Deprecated. Always empty |
|
||||
| nsfw_allowed<sup>3</sup> <sup>14</sup> | boolean | Whether the account can access age-restricted content |
|
||||
| has_dismissed_premium_onboarding<sup>3</sup> <sup>15</sup> | boolean | Whether premium onboarding was dismissed |
|
||||
| has_ever_purchased<sup>3</sup> | boolean | Whether the account has completed a purchase |
|
||||
| has_unread_gift_inventory<sup>3</sup> <sup>19</sup> | boolean | Whether the gift inventory has unread items |
|
||||
| unread_gift_inventory_count<sup>3</sup> <sup>19</sup> | integer | The number of unread gift inventory items |
|
||||
| has_unread_gift_inventory<sup>3</sup> <sup>16</sup> | boolean | Whether the gift inventory has unread items |
|
||||
| unread_gift_inventory_count<sup>3</sup> <sup>16</sup> | integer | The number of unread gift inventory items |
|
||||
| pending_bulk_message_deletion<sup>3</sup> | ?[pending bulk message deletion](#pending-bulk-message-deletion-object) object | The message deletion the account scheduled for itself, or null when none is pending |
|
||||
| age_verified_adult? | boolean | Whether adult age verification is complete, omitted when false |
|
||||
| terms_agreed_at | ?ISO8601 timestamp | The time of the most recent terms acceptance, or null |
|
||||
@@ -159,46 +158,32 @@ The private representation of the current account, returned by [Get current user
|
||||
|
||||
<sup>5</sup> For an OAuth2 bearer credential without the `email` [scope](/http-api/oauth2/#oauth2-scopes), `email` is `null`
|
||||
|
||||
<sup>6</sup> Always null
|
||||
<sup>6</sup> This server version writes the pair for every account
|
||||
|
||||
<sup>7</sup> This server version writes the pair for every account
|
||||
<sup>7</sup> The banner hash is not returned at all while the account lacks the animated banner entitlement, which every profile banner requires
|
||||
|
||||
<sup>8</sup> The banner hash is not returned at all while the account lacks the animated banner entitlement, which every profile banner requires
|
||||
<sup>8</sup> `mfa_enabled` is true exactly when at least one authenticator is configured. An account with no authenticator reports `authenticator_types` as an empty array, and only a bearer read drops the field
|
||||
|
||||
<sup>9</sup> `mfa_enabled` is true exactly when at least one authenticator is configured. An account with no authenticator reports `authenticator_types` as an empty array, and only a bearer read drops the field
|
||||
<sup>9</sup> The value is forced to `0` and `premium_since` is forced to `null` while premium entitlements are not active
|
||||
|
||||
<sup>10</sup> The value is forced to `0` and `premium_since` is forced to `null` while premium entitlements are not active
|
||||
<sup>10</sup> `premium_until` is the later of the subscription end and any stacked gift extension. Fluxer reports it even when premium entitlements are no longer active
|
||||
|
||||
<sup>11</sup> `premium_until` is the later of the subscription end and any stacked gift extension. Fluxer reports it even when premium entitlements are no longer active
|
||||
<sup>11</sup> After `premium_until` passes, premium entitlements stay active until this time. While the value is null, they stay active for 3 days after `premium_until`
|
||||
|
||||
<sup>12</sup> After `premium_until` passes, premium entitlements stay active until this time. While the value is null, they stay active for 3 days after `premium_until`
|
||||
<sup>12</sup> The flag is set when the account changes its username or discriminator while holding non-lifetime premium, and after that premium access ends, Fluxer replaces the discriminator with a newly generated one the next time the account starts a Gateway session
|
||||
|
||||
<sup>13</sup> The flag is set when the account changes its username or discriminator while holding non-lifetime premium, and after that premium access ends, Fluxer replaces the discriminator with a newly generated one the next time the account starts a Gateway session
|
||||
<sup>13</sup> The value drives the 24-hour cooldown on changing the default voice activity sharing state
|
||||
|
||||
<sup>14</sup> The field is declared but never populated, so it is absent from every response
|
||||
<sup>14</sup> A bot account is always permitted, and a user account is permitted only when its recorded date of birth places it at 18 years or older
|
||||
|
||||
<sup>15</sup> The value drives the 24-hour cooldown on changing the default voice activity sharing state
|
||||
<sup>15</sup> The value is false while premium entitlements are not active, even when a dismissal was recorded earlier
|
||||
|
||||
<sup>16</sup> The array is empty when the account has no stored suspicious activity flag, when it has no email address, or when its email address is on an exempt domain
|
||||
|
||||
<sup>17</sup> A bot account is always permitted, and a user account is permitted only when its recorded date of birth places it at 18 years or older
|
||||
|
||||
<sup>18</sup> The value is false while premium entitlements are not active, even when a dismissal was recorded earlier
|
||||
|
||||
<sup>19</sup> Reports whether unread gifts exist and how many. An account with no gifts reports `false` and `0`
|
||||
<sup>16</sup> Reports whether unread gifts exist and how many. An account with no gifts reports `false` and `0`
|
||||
|
||||
:::note[A bearer read returns zero values]
|
||||
`acls`, `traits`, and `required_actions` become empty arrays. `email_bounced` and `authenticator_types` are dropped. Every field marked <sup>3</sup> above that is a boolean becomes `false`, every timestamp or nullable field becomes `null`, and `unread_gift_inventory_count` becomes `0`.
|
||||
:::
|
||||
|
||||
:::caution[An outstanding required action blocks ordinary routes]
|
||||
While `required_actions` is non-empty, every route that requires a signed-in account, except the recovery routes below, returns 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`. The error body's `data` member has `suspicious_activity_flags`, with one bit set for each entry in `required_actions`.
|
||||
:::
|
||||
|
||||
Recovery routes stay open to a restricted account. They are [Get current user](/http-api/users/current-user/#get-current-user), [Modify current user](/http-api/users/current-user/#modify-current-user), [Get current user settings](/http-api/users/settings/#get-current-user-settings), every [email change](/http-api/users/email-and-password/) and [phone verification](/http-api/users/phone-verification/) route, [Resend email verification](/http-api/authentication/#resend-email-verification), session listing and termination, and the OAuth2 routes.
|
||||
|
||||
A requirement disappears from the list as soon as it is met. An Admin operation applies no suspicious activity gate.
|
||||
|
||||
## Pending bulk message deletion object
|
||||
|
||||
A scheduled deletion of every message the account has sent. The field is this object only while the deletion requested through [Request bulk message deletion](/http-api/users/content/#request-bulk-message-deletion) is still waiting to run.
|
||||
@@ -232,23 +217,21 @@ The value `1` is unassigned. Any stored authenticator value outside this registr
|
||||
| 1 | SUBSCRIPTION | Active premium subscription |
|
||||
| 2 | LIFETIME | Lifetime premium subscription |
|
||||
|
||||
## Required actions
|
||||
## Account limitation
|
||||
|
||||
Each entry is an exact string in the `required_actions` array of the [user object](#user-object), ordered by the registry order below. Fluxer removes a requirement the account already satisfies, and removes a requirement implied by a stricter retained one.
|
||||
An account is limited while its [user object](#user-object) reports `account_limited` as true. A limited account can still read, join voice, change its own settings, and manage its sessions. The operations below return 403 [`ACCOUNT_LIMITED`](/http-api/errors/), and the error message tells the user to check their email.
|
||||
|
||||
| Value | Description |
|
||||
| --- | --- |
|
||||
| REQUIRE_VERIFIED_EMAIL | The account needs a verified email address |
|
||||
| REQUIRE_REVERIFIED_EMAIL | The account needs to verify its email address again |
|
||||
| REQUIRE_VERIFIED_PHONE | The account needs a verified phone number |
|
||||
| REQUIRE_REVERIFIED_PHONE | The account needs to verify its phone number again |
|
||||
| REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE | The account needs either a verified email address or a verified phone number |
|
||||
| REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE | The account needs to verify its email address again or hold a verified phone number |
|
||||
| REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE | The account needs a verified email address or needs to verify its phone number again |
|
||||
| REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE | The account needs to verify either its email address or its phone number again |
|
||||
| REQUIRE_INBOUND_PHONE_VERIFICATION<sup>1</sup> | The account needs to complete phone verification by sending an SMS to the instance's inbound number |
|
||||
|
||||
<sup>1</sup> The entry appears only while a phone requirement is also outstanding. A stored inbound requirement with no other phone requirement adds `REQUIRE_VERIFIED_PHONE` to the array
|
||||
- [Create message](/http-api/messages/#create-message) and [Modify message](/http-api/messages/#modify-message) in any channel except the account's personal notes
|
||||
- [Crosspost message](/http-api/messages/#crosspost-message)
|
||||
- [Add own reaction](/http-api/messages/#add-own-reaction)
|
||||
- [Create webhook](/http-api/webhooks/#create-webhook)
|
||||
- [Accept invite](/http-api/invites/#accept-invite) and [Join discovery guild](/http-api/discovery/#join-discovery-guild), unless the account is already a member
|
||||
- [Create guild](/http-api/guilds/#create-guild)
|
||||
- Creating a group direct message through [Create private channel](/http-api/users/private-channels/#create-private-channel), and [Add group direct message recipient](/http-api/channels/#add-group-direct-message-recipient)
|
||||
- Changing a profile field through [Modify current user](/http-api/users/current-user/#modify-current-user) or [Modify current guild member](/http-api/guild-members/#modify-current-guild-member)
|
||||
- Setting a custom status through [Modify current user settings](/http-api/users/settings/#modify-current-user-settings) or a [presence update](/gateway/commands/#presence-update)
|
||||
- [Initiate connection](/http-api/connections/#initiate-connection), [Verify and create connection](/http-api/connections/#verify-and-create-connection), and [Start Bluesky authorisation](/http-api/connections/#start-bluesky-authorisation)
|
||||
- [Upload entrance sound](/http-api/entrance-sounds/#upload-entrance-sound) and [Set entrance sound selection](/http-api/entrance-sounds/#set-entrance-sound-selection)
|
||||
|
||||
## Profile field privacy flags
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
Recent mentions and saved messages are private lists that only the account owning them can read. Further routes delete the messages the caller authored, one filtered and immediate, the other unfiltered and delayed by a day. Data harvests live on [Data harvests](/http-api/users/data-harvest/).
|
||||
|
||||
These routes are user-only. Fluxer rejects a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`, and an account that has an outstanding required action with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
These routes are user-only. Fluxer rejects a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`.
|
||||
|
||||
Every route except [Delete current user's messages](#delete-current-users-messages) reaches the main Gateway, and a Gateway failure returns 502 `BAD_GATEWAY`, 503 `SERVICE_UNAVAILABLE`, or 504 `GATEWAY_TIMEOUT`. [Save message](#save-message) resolves the channel before it writes, so a failure there stores nothing. The request still returns 204 when a [Saved Message Create](/gateway/events/#saved-message-create), [Saved Message Delete](/gateway/events/#saved-message-delete), or [Recent Mention Delete](/gateway/events/#recent-mention-delete) Dispatch fails to publish after the write.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
The current user is the account that presented the credential, represented as a [user object](/http-api/users/#user-object). The routes here read that account, change it, and run its lifecycle operations.
|
||||
|
||||
Every route except [Get current user](#get-current-user) is user-only. Those routes reject a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`. They also reject an account that has an outstanding [required action](/http-api/users/#required-actions) with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
Every route except [Get current user](#get-current-user) is user-only. Those routes reject a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`.
|
||||
|
||||
## Sudo verification
|
||||
|
||||
@@ -34,8 +34,6 @@ Returns the current [user](/http-api/users/#user-object) object.
|
||||
- An OAuth2 bearer credential must hold the `identify` [scope](/http-api/oauth2/#oauth2-scopes), and one without it is rejected with 403 [`MISSING_OAUTH_SCOPE`](/http-api/errors/), whose body has `required_scope`.
|
||||
- A bearer receives the [user object](/http-api/users/#user-object) with the zero values that page lists for a bearer read, and `email` only when it also holds the `email` scope.
|
||||
|
||||
An account with an outstanding [required action](/http-api/users/#required-actions) can call this route, so a client can read and poll the actions it has to complete.
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
@@ -122,12 +120,12 @@ An omitted field leaves its stored value untouched, and an explicit `null` clear
|
||||
|
||||
To skip the body proof fields, send the sudo token in the `X-Fluxer-Sudo-Mode-JWT` request header.
|
||||
|
||||
A body that has no `email_token` and nothing beyond `mfa_method`, `mfa_code`, `webauthn_response`, and `webauthn_challenge` returns the current account unchanged. That check runs before Fluxer checks required actions, so it succeeds even while an action is outstanding. Supplying `password` alone is an update with nothing to change, and it still emits [User Update](/gateway/events/#user-update).
|
||||
|
||||
Fluxer rejects an account with an outstanding required action. The one exemption is a body that has `email_token` and nothing beyond the sudo verification fields. Any other defined field removes the exemption.
|
||||
A body that has no `email_token` and nothing beyond `mfa_method`, `mfa_code`, `webauthn_response`, and `webauthn_challenge` returns the current account unchanged. Supplying `password` alone is an update with nothing to change, and it still emits [User Update](/gateway/events/#user-update).
|
||||
|
||||
A claimed account needs a verified email before changing the username, discriminator, display name, avatar, banner, biography, pronouns, accent colour, timezone, timezone privacy, or any premium badge field. A request without one returns 403 [`PROFILE_EMAIL_VERIFICATION_REQUIRED`](/http-api/errors/).
|
||||
|
||||
A [limited account](/http-api/users/#account-limitation) cannot change any of those fields. A request that sets one returns 403 [`ACCOUNT_LIMITED`](/http-api/errors/).
|
||||
|
||||
An account is unclaimed while it holds no password credential, is not a bot, and does not have the SSO trait. Such an account may set only `new_password`, `has_dismissed_premium_onboarding`, and `has_unread_gift_inventory`. Any other field is rejected with [`UNCLAIMED_ACCOUNTS_CAN_ONLY_SET_EMAIL_VIA_TOKEN`](/http-api/errors/) on the path of the first offending field. The allow-list covers the profile payload only, so `email_token` and the sudo fields stay available as the way such an account claims itself.
|
||||
|
||||
### Image validation
|
||||
@@ -168,6 +166,7 @@ Supplying `new_password` on a claimed account deletes every other authentication
|
||||
| 200 | [user](/http-api/users/#user-object) object | Account was returned after applying every permitted field |
|
||||
| 400 | [error response](/http-api/#error-response) | Body, image, tag, password, entitlement, secondary control, or sudo proof is invalid |
|
||||
| 403 | [error response](/http-api/#error-response) | Email verification, sudo verification, or a staff-only field is required, or content is blocked |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is limited and the body changes a profile field, and the request returns `ACCOUNT_LIMITED` |
|
||||
|
||||
### Side effects
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A data harvest is a ZIP archive of the current account's data. Fluxer compiles it in the background, reports progress while it runs, and issues a temporary download URL once the archive is written.
|
||||
|
||||
Every route except [Download data harvest archive](#download-data-harvest-archive) is user-only. Fluxer rejects a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`, and an account that has suspicious activity flags with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`. No route here requires [sudo mode](/http-api/users/mfa/#sudo-mode). [Delete current user's messages](/http-api/users/content/#delete-current-users-messages) takes the same filter shape, destroys the messages, and requires it.
|
||||
Every route except [Download data harvest archive](#download-data-harvest-archive) is user-only. Fluxer rejects a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`. No route here requires [sudo mode](/http-api/users/mfa/#sudo-mode). [Delete current user's messages](/http-api/users/content/#delete-current-users-messages) takes the same filter shape, destroys the messages, and requires it.
|
||||
|
||||
:::caution[A completed archive has a seven day download deadline]
|
||||
A completed harvest records a deadline exactly seven days after the archive was written. After it, [Get data harvest download URL](#get-data-harvest-download-url) returns 400 `HARVEST_EXPIRED` and [Download data harvest archive](#download-data-harvest-archive) returns 404, while the record keeps reporting `completed`. Nothing extends or restores it.
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
Changing the email address or the password on an account takes several steps. Fluxer opens a ticket, emails a code, and every later step sends that ticket with the codes and proofs collected so far. An account whose address the mail provider rejected uses [bounced email recovery](#bounced-email-recovery) instead.
|
||||
|
||||
Every operation needs a non-bot user session. The email change routes accept a session with suspicious account state. Every password change route rejects that state with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
Every operation needs a non-bot user session.
|
||||
|
||||
## Ticket and code contract
|
||||
|
||||
@@ -41,7 +41,7 @@ Every ticket, code, address, proof, token, and password failure named on this pa
|
||||
- An email token failure uses the path `email_token`.
|
||||
- A password failure uses the path `password` or `new_password`.
|
||||
|
||||
The account-level codes `ACCESS_DENIED`, `ACCOUNT_SUSPICIOUS_ACTIVITY`, `SUDO_MODE_REQUIRED`, and `RATE_LIMITED` arrive as the top-level `code`.
|
||||
The account-level codes `ACCESS_DENIED`, `SUDO_MODE_REQUIRED`, and `RATE_LIMITED` arrive as the top-level `code`.
|
||||
|
||||
## Email send controls
|
||||
|
||||
@@ -399,7 +399,7 @@ The ticket moves to the completed state and a 30-minute email token is issued fo
|
||||
|
||||
Consumes an email token and writes its address to the account. Returns the updated [user](/http-api/users/#user-object) object on success. Emits a [User Update](/gateway/events/#user-update) Gateway event.
|
||||
|
||||
A claimed account proves [sudo mode](/http-api/users/mfa/#sudo-mode) before the route reads the token. An unclaimed account is exempt and applies the change with its session alone. The route accepts suspicious account state.
|
||||
A claimed account proves [sudo mode](/http-api/users/mfa/#sudo-mode) before the route reads the token. An unclaimed account is exempt and applies the change with its session alone.
|
||||
|
||||
A token that belongs to another account fails with `INVALID_EMAIL_TOKEN`, and one past its 30-minute lifetime fails with `EMAIL_TOKEN_EXPIRED`. An expired token is deleted when rejected. Fluxer checks the address against existing account ownership again here. An address another account has taken since fails with `EMAIL_ALREADY_IN_USE` without consuming the token.
|
||||
|
||||
@@ -449,7 +449,7 @@ This operation replaces the email address only. Nothing on the path revokes a cr
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer replaces the account email, marks it verified, and removes every email-clearable suspicious activity flag, which can empty `required_actions` and restore ordinary access. The bounced marker is not cleared, so an account that has it recovers through [bounced email recovery](#bounced-email-recovery).
|
||||
Fluxer replaces the account email and marks it verified. The bounced marker is not cleared, so an account that has it recovers through [bounced email recovery](#bounced-email-recovery).
|
||||
|
||||
For an ordinary change from an existing address, Fluxer sends a revert email to the original address so its holder can use [Revert an email change](/http-api/authentication/#revert-an-email-change). Fluxer deletes the email token, records the contact change in the account's contact change log, and emits [User Update](/gateway/events/#user-update). No public member field changes, so no [Guild Member Update](/gateway/events/#guild-member-update) follows.
|
||||
|
||||
@@ -459,7 +459,7 @@ For an ordinary change from an existing address, Fluxer sends a revert email to
|
||||
|
||||
## Bounced email recovery
|
||||
|
||||
An account whose stored address the mail provider rejected has the bounced marker, reported as `email_bounced` on the [user object](/http-api/users/#user-object). The bounce also clears the verified email state and adds a required action. The operations below bind a replacement address and finish without the sudo-gated apply step.
|
||||
An account whose stored address the mail provider rejected has the bounced marker, reported as `email_bounced` on the [user object](/http-api/users/#user-object). The bounce also clears the verified email state. The operations below bind a replacement address and finish without the sudo-gated apply step.
|
||||
|
||||
Every one of them requires the bounced marker. An account without it is rejected with 403 `ACCESS_DENIED`, and an account with no address at all is rejected with `MUST_HAVE_EMAIL_TO_CHANGE_IT`.
|
||||
|
||||
@@ -473,7 +473,7 @@ The codes and the 30-second cooldown in this flow follow [Ticket and code contra
|
||||
|
||||
Starts the recovery flow. Returns a [new email request](#new-email-request-object) object on success.
|
||||
|
||||
This operation creates a ticket and binds the replacement address in the same call. The route accepts suspicious account state. Fluxer checks the address exactly as [Request new email](#request-new-email) does: the address must have a domain that publishes mail exchange or address records, belong to no other account, and differ from the bounced address.
|
||||
This operation creates a ticket and binds the replacement address in the same call. Fluxer checks the address exactly as [Request new email](#request-new-email) does: the address must have a domain that publishes mail exchange or address records, belong to no other account, and differ from the bounced address.
|
||||
|
||||
The send counts against the new address control.
|
||||
|
||||
@@ -557,7 +557,7 @@ No sudo verification and no email token step applies.
|
||||
|
||||
### Side effects
|
||||
|
||||
The account gets the verified replacement address, `email_bounced` becomes false, and satisfied email requirements are removed from `required_actions`. The ticket is completed and cannot be reused.
|
||||
The account gets the verified replacement address and `email_bounced` becomes false. The ticket is completed and cannot be reused.
|
||||
|
||||
No revert email is sent.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Multi-factor authentication asks for a second proof of identity after the accoun
|
||||
|
||||
Registering a WebAuthn credential does not make passkeys a second factor. A registered credential answers a [sudo mode](#sudo-mode) challenge and a passwordless login on its own, and only [Set WebAuthn two-factor authentication](#set-webauthn-two-factor-authentication) adds the WebAuthn [authenticator type](/http-api/users/#authenticator-types) that makes a passkey a required second factor at password login.
|
||||
|
||||
Every route here requires a non-bot user session. Fluxer rejects an account in suspicious activity state with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`, including on the sudo routes.
|
||||
Every route here requires a non-bot user session.
|
||||
|
||||
:::caution[A challenge is one-use and context-bound]
|
||||
Fluxer consumes a backup code, a WebAuthn registration challenge, and a WebAuthn sudo challenge on first use. A challenge is bound to the user it was issued to and to the operation it was issued for. The operations are registration, sudo, MFA login, discoverable login, the passkey bridge, and the passkey update. It expires after five minutes, so a registration challenge cannot be redeemed as a sudo assertion.
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A note is short private text the caller stores about another account.
|
||||
|
||||
Every route here requires a user session. A bot or OAuth2 bearer credential is refused with 403 `ACCESS_DENIED`. An account with an outstanding required action is refused with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
Every route here requires a user session. A bot or OAuth2 bearer credential is refused with 403 `ACCESS_DENIED`.
|
||||
|
||||
:::note[A note is private to its author]
|
||||
The target account never receives the note in an HTTP response or [Gateway Dispatch](/gateway/events/). A note has no relationship requirement, so the caller can hold one for an account that is neither a friend nor blocked.
|
||||
|
||||
@@ -1,320 +0,0 @@
|
||||
---
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
title: Phone verification
|
||||
description: Proving control of a phone number over outbound SMS or an inbound challenge, and setting a deferred requirement aside.
|
||||
---
|
||||
|
||||
import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
Phone verification proves that the current account controls a phone number. The account either receives a one-time code over outbound SMS, or texts an issued challenge code to a Fluxer number. Success sets the account's verified phone state, which the phone [required actions](/http-api/users/#required-actions) and the `VERY_HIGH` guild [verification level](/http-api/guilds/#verification-levels) check.
|
||||
|
||||
Every route here needs a non-bot user session, and each one admits a session with suspicious account state.
|
||||
|
||||
## Eligibility
|
||||
|
||||
[Send phone verification](#send-phone-verification) and [Verify phone code](#verify-phone-code) admit only an account that satisfies at least one of these conditions.
|
||||
|
||||
- The account already holds a verified phone.
|
||||
- A TOTP [authenticator](/http-api/users/#authenticator-types) is enrolled on the account.
|
||||
- The stored suspicious activity bitfield is non-zero.
|
||||
- The account belongs to at least one guild whose [verification level](/http-api/guilds/#verification-levels) is `VERY_HIGH`, and the [instance features](/http-api/instance/#instance-features-object) report `phone_verification_enabled` as true.
|
||||
|
||||
An account satisfying none of them is refused with 403 `PHONE_ADD_NOT_ELIGIBLE`.
|
||||
|
||||
Both operations run the check before they examine the submitted number. A refused account receives no message and consumes no challenge. [Start inbound phone challenge](#start-inbound-phone-challenge) skips the check entirely and issues a challenge for any non-bot session.
|
||||
|
||||
:::note[Visible required actions do not determine eligibility]
|
||||
An account can qualify even when its [required actions](/http-api/users/#required-actions) array is empty.
|
||||
:::
|
||||
|
||||
## Deferred phone requirement
|
||||
|
||||
An account can hold a phone requirement together with a marker that defers it. A deferred requirement is absent from the [required actions](/http-api/users/#required-actions) array and restricts nothing. An account whose deferred requirement was later made due owes `REQUIRE_VERIFIED_PHONE` or `REQUIRE_REVERIFIED_PHONE`, and every ordinary user route refuses it with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
|
||||
[Get phone gate escape preview](#get-phone-gate-escape-preview) and [Run phone gate escape](#run-phone-gate-escape) set a due requirement aside again without verifying a number and without leaving any guild. Both admit an account the rest of the API refuses. The escape is available only while all of these hold.
|
||||
|
||||
- The account holds no verified phone.
|
||||
- The account owes `REQUIRE_VERIFIED_PHONE` or `REQUIRE_REVERIFIED_PHONE`.
|
||||
- The account does not owe `REQUIRE_INBOUND_PHONE_VERIFICATION`.
|
||||
- The requirement was deferred before it became due, and it has not been deferred again.
|
||||
- The account has at least one visible required action.
|
||||
|
||||
## Number representation
|
||||
|
||||
The `phone` field is a number already in canonical E.164 form. Fluxer strips control and formatting characters, trims the value, and then matches it against `^\+[1-9]\d{1,14}$`. A value that fails returns 400 `INVALID_FORM_BODY` with one [validation error](/http-api/#validation-error-object) entry whose `path` is `phone` and whose `code` is `PHONE_NUMBER_INVALID_FORMAT`.
|
||||
|
||||
:::note[Only the verified state is exposed]
|
||||
A successful verification sets a boolean on the account. The `phone` field of the [user object](/http-api/users/#user-object) is always null, and no route returns the submitted number.
|
||||
:::
|
||||
|
||||
:::danger[Numbers and codes are confidential]
|
||||
Keep phone numbers, verification codes, and challenge codes out of logs, analytics, URLs, and messages to unintended recipients.
|
||||
:::
|
||||
|
||||
## Phone verification channel values
|
||||
|
||||
| Value | Description |
|
||||
| --- | --- |
|
||||
| sms<sup>1</sup> | Fluxer asks the SMS provider to deliver a one-time code to the submitted number |
|
||||
| inbound_challenge<sup>2</sup> | The user texts the issued challenge code to the returned Fluxer number |
|
||||
|
||||
<sup>1</sup> Requesting this value is equivalent to omitting the field
|
||||
|
||||
<sup>2</sup> Requesting this value always yields an inbound challenge, and neither the lookup provider nor the outbound SMS provider is contacted
|
||||
|
||||
## Number checks
|
||||
|
||||
Outbound SMS requires the number to pass a line check with the instance's phone provider. A number the provider cannot reach, or one on a line type that cannot receive a code, is refused with 400 `INVALID_PHONE_NUMBER`. Some numbers that pass are routed to an inbound challenge instead. A number the check cannot be run for is refused.
|
||||
|
||||
## Inbound challenge reason values
|
||||
|
||||
| Value | Description |
|
||||
| --- | --- |
|
||||
| verification_required | The instance requires inbound verification for this attempt |
|
||||
|
||||
## SMS delivery object
|
||||
|
||||
Confirmation that the SMS provider accepted a one-time code for delivery. [Send phone verification](#send-phone-verification) returns it on an outbound result. The client then sends the same number and the delivered code to [Verify phone code](#verify-phone-code).
|
||||
|
||||
### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| channel | string | The delivery channel, always the literal `sms` |
|
||||
|
||||
## Inbound challenge object
|
||||
|
||||
The instructions for texting a code to Fluxer, returned by [Send phone verification](#send-phone-verification) when policy routed the attempt inbound. `channel` tells the response shapes apart.
|
||||
|
||||
### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| channel | string | The delivery channel, always the literal `inbound_challenge` |
|
||||
| challenge_code<sup>1</sup> | string | The code the user texts to `our_number` |
|
||||
| our_number | string | The Fluxer E.164 number that receives the code |
|
||||
| expires_at<sup>2</sup> | ISO8601 timestamp | The moment the challenge stops being redeemable |
|
||||
| reason | string | The [inbound challenge reason](#inbound-challenge-reason-values), always `verification_required` |
|
||||
|
||||
<sup>1</sup> Six decimal digits
|
||||
|
||||
<sup>2</sup> 15 minutes after the challenge was issued
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"channel": "inbound_challenge",
|
||||
"challenge_code": "418207",
|
||||
"our_number": "+15550000000",
|
||||
"expires_at": "2026-03-04T18:15:00.000Z",
|
||||
"reason": "verification_required"
|
||||
}
|
||||
```
|
||||
|
||||
## Direct inbound challenge object
|
||||
|
||||
The code and the Fluxer number for a caller that asked for an inbound challenge directly. [Start inbound phone challenge](#start-inbound-phone-challenge) returns it.
|
||||
|
||||
### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| challenge_code<sup>1</sup> | string | The code the user texts to `our_number` |
|
||||
| our_number | string | The Fluxer E.164 number that receives the code |
|
||||
| expires_at<sup>2</sup> | ISO8601 timestamp | The moment the challenge stops being redeemable |
|
||||
|
||||
<sup>1</sup> Six decimal digits
|
||||
|
||||
<sup>2</sup> 15 minutes after the challenge was issued
|
||||
|
||||
## Phone verification result object
|
||||
|
||||
Returned by [Verify phone code](#verify-phone-code) on a successful verification.
|
||||
|
||||
### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| verified | boolean | Always the literal `true` |
|
||||
|
||||
## Phone gate escape guild object
|
||||
|
||||
One guild in a [phone gate escape preview](#phone-gate-escape-preview-object).
|
||||
|
||||
### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| id | snowflake | The ID of the guild |
|
||||
| name | string | The name the guild is listed under |
|
||||
|
||||
## Phone gate escape preview object
|
||||
|
||||
What [Run phone gate escape](#run-phone-gate-escape) would do for the current account.
|
||||
|
||||
### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| available | boolean | Whether the escape is open to this account right now |
|
||||
| guilds | array[[phone gate escape guild](#phone-gate-escape-guild-object) object] | Always empty, the escape leaves no guild |
|
||||
| owned_guilds | array[[phone gate escape guild](#phone-gate-escape-guild-object) object] | Always empty, the escape leaves no guild |
|
||||
|
||||
## Send phone verification
|
||||
|
||||
<RouteHeader method="POST" path="/v1/users/@me/phone/send-verification" />
|
||||
|
||||
Starts verification of the submitted number. Requires an account satisfying [eligibility](#eligibility). Returns an [SMS delivery](#sms-delivery-object) object or an [inbound challenge](#inbound-challenge-object) object on success.
|
||||
|
||||
The instance can route the attempt to an inbound challenge instead of outbound SMS, and that overrides a requested `sms`. Requesting `inbound_challenge` always yields a challenge.
|
||||
|
||||
The number then passes the [number checks](#number-checks). A number that has reached its verification limit is refused with 400 `PHONE_ALREADY_USED`. Sends are limited for each account and for each number, and a denial returns 429 `PHONE_RATE_LIMIT_EXCEEDED`. The phone verification service can also ask for a solved [CAPTCHA](/topics/captcha/) challenge. The route then answers 400 `CAPTCHA_REQUIRED` with a challenge, and a retry with the solved challenge in `X-Captcha-Token` continues the send. When the instance has the check turned off, or the account is exempt from it, that request returns 429 `PHONE_RATE_LIMIT_EXCEEDED` instead. Every other refusal returns 400 `SMS_VERIFICATION_UNAVAILABLE`, as does an instance with no phone verification service.
|
||||
|
||||
### Request headers
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| X-Captcha-Token?<sup>1</sup> | string | The solved ALTCHA challenge, see [CAPTCHA handling](/topics/captcha/) |
|
||||
|
||||
<sup>1</sup> Read only when the phone verification service asks for a CAPTCHA. A missing token then returns 400 `CAPTCHA_REQUIRED` and a rejected token returns 400 `INVALID_CAPTCHA`, each with a new challenge
|
||||
|
||||
### JSON body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| phone | string | The number in canonical E.164 form, matching `^\+[1-9]\d{1,14}$` |
|
||||
| channel?<sup>1</sup> | string | The preferred [verification channel](#phone-verification-channel-values) |
|
||||
|
||||
<sup>1</sup> Omitting the field requests outbound SMS. Requesting `inbound_challenge` always yields a challenge
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [SMS delivery](#sms-delivery-object) object \| [inbound challenge](#inbound-challenge-object) object | Verification was started on the channel named by the response |
|
||||
| 400 | [error response](/http-api/#error-response) | The number is refused with `INVALID_PHONE_NUMBER`, has reached its verification limit and returns `PHONE_ALREADY_USED`, the attempt asks for a CAPTCHA with `CAPTCHA_REQUIRED` or `INVALID_CAPTCHA`, or the send could not start and returns `SMS_VERIFICATION_UNAVAILABLE` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is not eligible and the request returns `PHONE_ADD_NOT_ELIGIBLE` |
|
||||
| 429<sup>1</sup> | [rate limit response](/topics/rate-limits/#rate-limit-response-object) | A send limit or a provider throttle denies the request, which returns `PHONE_RATE_LIMIT_EXCEEDED` |
|
||||
|
||||
<sup>1</sup> A phone denial uses the rate limit envelope with `code` set to `PHONE_RATE_LIMIT_EXCEEDED`. An ordinary route denial returns `RATE_LIMITED`
|
||||
|
||||
On the 429, `X-RateLimit-Scope` reports `shared` when the per-number control or a number-scoped provider throttle produced the denial.
|
||||
|
||||
### Side effects
|
||||
|
||||
On an outbound result, Fluxer asks the configured SMS provider to deliver a one-time code. On an inbound result, it creates a one-use challenge for the current account.
|
||||
|
||||
### Rate limit
|
||||
|
||||
5 requests per minute for each authenticated user, on the `phone:send_verification` bucket.
|
||||
|
||||
## Start inbound phone challenge
|
||||
|
||||
<RouteHeader method="POST" path="/v1/users/@me/phone/inbound-challenge" />
|
||||
|
||||
Issues an inbound challenge without submitting or examining a phone number. Returns a [direct inbound challenge](#direct-inbound-challenge-object) object on success.
|
||||
|
||||
The [eligibility](#eligibility) check does not apply here. The challenge code is six decimal digits, lives for 15 minutes, and can be redeemed once. Requesting a further challenge does not revoke an earlier one, so several codes issued to the same account can be live at the same time until each expires or is redeemed.
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [direct inbound challenge](#direct-inbound-challenge-object) object | A challenge was issued |
|
||||
| 400 | [error response](/http-api/#error-response) | The instance cannot issue an inbound challenge and the request returns `SMS_VERIFICATION_UNAVAILABLE` |
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer creates a one-use challenge for the current account. The challenge completes out of band when the provider delivers the matching inbound message, and the HTTP response has no part of it. On completion, Fluxer sets the verified phone state, clears the phone requirements from the stored suspicious activity bitfield, and dispatches [User Update](/gateway/events/#user-update). A completion from a number that has reached its verification limit does not verify the account.
|
||||
|
||||
### Rate limit
|
||||
|
||||
5 requests per minute for each authenticated user, on the `phone:send_verification` bucket, shared with [Send phone verification](#send-phone-verification).
|
||||
|
||||
## Verify phone code
|
||||
|
||||
<RouteHeader method="POST" path="/v1/users/@me/phone/verify" />
|
||||
|
||||
Verifies an outbound SMS code. Requires an account satisfying [eligibility](#eligibility). Returns a [phone verification result](#phone-verification-result-object) object on success. Emits a [User Update](/gateway/events/#user-update) Gateway event.
|
||||
|
||||
The number passes the [number checks](#number-checks) again before the code is checked, so a number that has become ineligible since the code was sent is refused.
|
||||
|
||||
The phone provider checks the number and the code together, so the provider decides the code lifetime and the attempt allowance. A rejected code returns 400 `INVALID_PHONE_VERIFICATION_CODE`. A throttle returns 429 `PHONE_RATE_LIMIT_EXCEEDED`. An unavailable provider returns 400 `SMS_VERIFICATION_UNAVAILABLE`. A number that has reached its verification limit fails with 400 `PHONE_ALREADY_USED`. A deleted account, or one that can no longer be read, fails with 400 `PHONE_VERIFICATION_REQUIRED`.
|
||||
|
||||
### JSON body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| phone | string | The number in canonical E.164 form, matching `^\+[1-9]\d{1,14}$` |
|
||||
| code<sup>1</sup> | string | The code the outbound SMS delivered (1-32 characters) |
|
||||
|
||||
<sup>1</sup> Stripped of control and formatting characters and trimmed before the length check, and the normalised value is what the provider checks
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [phone verification result](#phone-verification-result-object) object | The number is now verified for the account |
|
||||
| 400 | [error response](/http-api/#error-response) | The number returns `INVALID_PHONE_NUMBER`, the code returns `INVALID_PHONE_VERIFICATION_CODE`, the provider returns `SMS_VERIFICATION_UNAVAILABLE`, the number returns `PHONE_ALREADY_USED`, or the account returns `PHONE_VERIFICATION_REQUIRED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is not eligible and the request returns `PHONE_ADD_NOT_ELIGIBLE` |
|
||||
| 429<sup>1</sup> | [rate limit response](/topics/rate-limits/#rate-limit-response-object) | A provider throttle denies the request, which returns `PHONE_RATE_LIMIT_EXCEEDED` |
|
||||
|
||||
<sup>1</sup> A phone denial uses the rate limit envelope with `code` set to `PHONE_RATE_LIMIT_EXCEEDED`. An ordinary route denial returns `RATE_LIMITED`
|
||||
|
||||
On the 429, `X-RateLimit-Scope` reports `shared` when a number-scoped provider throttle produced the denial.
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer marks the account as holding a verified phone. It then clears every phone requirement from the stored suspicious activity bitfield: `REQUIRE_VERIFIED_PHONE`, `REQUIRE_REVERIFIED_PHONE`, the combined email-or-phone requirements, `REQUIRE_INBOUND_PHONE_VERIFICATION`, and the markers of a deferred phone requirement. Email-only requirements remain, so an account that also owes email verification stays restricted.
|
||||
|
||||
Fluxer dispatches [User Update](/gateway/events/#user-update) before the HTTP response returns.
|
||||
|
||||
### Rate limit
|
||||
|
||||
10 requests per minute for each authenticated user, on the `phone:verify_code` bucket.
|
||||
|
||||
## Get phone gate escape preview
|
||||
|
||||
<RouteHeader method="GET" path="/v1/users/@me/required-actions/phone-gate-escape" />
|
||||
|
||||
Returns a [phone gate escape preview](#phone-gate-escape-preview-object) object for the current account. For an account outside the state [Deferred phone requirement](#deferred-phone-requirement) describes, `available` is false and both arrays are empty.
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [phone gate escape preview](#phone-gate-escape-preview-object) object | The preview was returned |
|
||||
|
||||
### Rate limit
|
||||
|
||||
20 requests per minute for each authenticated user, on the `user:phone_gate_escape:preview` bucket.
|
||||
|
||||
## Run phone gate escape
|
||||
|
||||
<RouteHeader method="POST" path="/v1/users/@me/required-actions/phone-gate-escape" />
|
||||
|
||||
Defers the phone requirement again without leaving any guild. Returns the [user](/http-api/users/#user-object) object.
|
||||
|
||||
### Limitations
|
||||
|
||||
- The account is in the state [Deferred phone requirement](#deferred-phone-requirement) defines, and one outside it is rejected with 400 [`PHONE_GATE_ESCAPE_UNAVAILABLE`](/http-api/errors/).
|
||||
|
||||
### JSON body
|
||||
|
||||
The body is an empty object. Fluxer reads an absent or empty body as an empty object and discards any supplied property. A body that is not valid JSON returns 400 `INVALID_FORM_BODY` with `INVALID_FORMAT` at the `body` path. Valid JSON that is not an object is rejected with 400.
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [user](/http-api/users/#user-object) object | The requirement was deferred again |
|
||||
| 400 | [error response](/http-api/#error-response) | The escape is unavailable and the request returns `PHONE_GATE_ESCAPE_UNAVAILABLE` |
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer defers the requirement again and dispatches [User Update](/gateway/events/#user-update). Guild memberships are unchanged.
|
||||
|
||||
### Rate limit
|
||||
|
||||
5 requests per hour for each authenticated user, on the `user:phone_gate_escape:execute` bucket.
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A private channel is a conversation outside any guild. It is either a direct message between two accounts or a group DM with its own participant set. Its fields come from the [channel object](/http-api/channels/#channel-object).
|
||||
|
||||
The routes here take a user session or a bot token. An OAuth2 bearer credential receives 403 `ACCESS_DENIED`, and an account with an outstanding required action receives 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
The routes here take a user session or a bot token. An OAuth2 bearer credential receives 403 `ACCESS_DENIED`.
|
||||
|
||||
:::note[Open state is per participant]
|
||||
A direct message channel exists once, and each participant holds its own open state. Closing one side leaves the channel and its messages in place, so a later [Create private channel](#create-private-channel) request for the same pair returns that stored channel.
|
||||
@@ -146,6 +146,7 @@ Fluxer holds a bot caller to no friendship. A recipient that has blocked the bot
|
||||
| 200 | [channel](/http-api/channels/#channel-object) object | The direct message was opened or the group DM was created |
|
||||
| 400 | [error response](/http-api/#error-response) | CAPTCHA, account eligibility, the recipient set, relationship policy, a group DM limit, or instance policy rejects creation |
|
||||
| 403 | [error response](/http-api/#error-response) | An ordinary caller's email address is unverified and the request returns `DIRECT_MESSAGE_EMAIL_VERIFICATION_REQUIRED`, or a limited caller opens a new conversation and the request returns `NEW_CONVERSATIONS_LIMITED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and creates a group direct message, and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | The caller or the direct message recipient does not resolve |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
Fluxer delivers a notification to a registered device as [RFC 8291](https://datatracker.ietf.org/doc/html/rfc8291) Web Push. The client generates a P-256 key pair and an auth secret, registers the public half of the pair, and decrypts each delivery with the private half.
|
||||
|
||||
Every route here requires a user session. A bot or OAuth2 bearer credential is refused with 403 `ACCESS_DENIED`. An account with an outstanding required action is refused with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
Every route here requires a user session. A bot or OAuth2 bearer credential is refused with 403 `ACCESS_DENIED`.
|
||||
|
||||
## Registration formats
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A relationship describes a friendship, block, or pending friend request from the caller's perspective. Private notes belong to [User notes](/http-api/users/notes/).
|
||||
|
||||
The routes here are user-only. A bot or OAuth2 bearer credential receives 403 `ACCESS_DENIED`, and an account with an outstanding required action receives 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
The routes here are user-only. A bot or OAuth2 bearer credential receives 403 `ACCESS_DENIED`.
|
||||
|
||||
:::note[Relationship records are directional]
|
||||
A friendship is a FRIEND record on both accounts. A pending request is `OUTGOING_REQUEST` on the sender and `INCOMING_REQUEST` on the recipient. A block exists only on the account that created it, so the block adds no record to the blocked account and sends it no [Relationship Add](/gateway/events/#relationship-add).
|
||||
|
||||
@@ -523,7 +523,6 @@ The `nagbars` field stores dismissed account-wide notices as bools and dismissed
|
||||
| desktop_download | bool | Whether the desktop download notice is dismissed |
|
||||
| guild_membership_cta | bool | Whether the guild membership call to action notice is dismissed |
|
||||
| visionary_mfa<sup>1</sup> | bool | Whether the lifetime premium MFA notice is dismissed |
|
||||
| legacy_phone_unlink | bool | Whether the legacy phone unlink notice is dismissed |
|
||||
| pending_bulk_deletion | map[string, bool] | Dismissal state keyed by pending bulk deletion identifier |
|
||||
| invites_disabled<sup>2</sup> | map[string, bool] | Dismissal state keyed by guild ID for disabled-invite notices |
|
||||
| guild_mfa_requirement<sup>2</sup> | map[string, bool] | Dismissal state keyed by guild ID for MFA requirement notices |
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
User settings include account-wide [preferences](/http-api/users/#user-settings-object) and [notification settings](#user-guild-settings-object) for each guild and for private channels.
|
||||
|
||||
Every route here is user-only and rejects a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`. Every route except [Get current user settings](#get-current-user-settings) also rejects an account with an outstanding required action, with 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`.
|
||||
Every route here is user-only and rejects a bot or OAuth2 bearer credential with 403 `ACCESS_DENIED`.
|
||||
|
||||
## Presence status values
|
||||
|
||||
@@ -341,7 +341,7 @@ A new account starts with these values. Every field not listed starts empty, so
|
||||
|
||||
Returns the [user settings](/http-api/users/#user-settings-object) object.
|
||||
|
||||
An account with an outstanding required action can still call this route. An account with no stored settings record returns 404 `UNKNOWN_USER`.
|
||||
An account with no stored settings record returns 404 `UNKNOWN_USER`.
|
||||
|
||||
### Response
|
||||
|
||||
@@ -375,6 +375,7 @@ The body uses the [user settings update object](#user-settings-update-object).
|
||||
| 200 | [user settings](/http-api/users/#user-settings-object) object | Settings were applied and returned |
|
||||
| 400 | [error response](/http-api/#error-response) | A field value, the custom status emoji, the trusted domain combination, an age-restricted filter, or the synced preferences snapshot is invalid |
|
||||
| 403 | [error response](/http-api/#error-response) | Custom status text is blocked and the request returns `CONTENT_BLOCKED` |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is [limited](/http-api/users/#account-limitation) and the body sets a custom status, and the request returns `ACCOUNT_LIMITED` |
|
||||
| 404 | [error response](/http-api/#error-response) | Settings do not exist and the request returns `UNKNOWN_USER` |
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -783,7 +783,6 @@ Returns an array of [webhook objects](#webhook-object) in a guild. The caller mu
|
||||
| 400 | [error response](/http-api/#error-response) | Path parameter is not a valid snowflake |
|
||||
| 400<sup>2</sup> | [error response](/http-api/#error-response) | The caller holds `MANAGE_WEBHOOKS` with no enrolled authenticator in an elevated-MFA guild |
|
||||
| 403<sup>3</sup> | [error response](/http-api/#error-response) | Credential is a bearer token |
|
||||
| 403<sup>3</sup> | [error response](/http-api/#error-response) | The account has an outstanding required action |
|
||||
| 403<sup>3</sup> | [error response](/http-api/#error-response) | The guild is unavailable |
|
||||
| 403<sup>3</sup> | [error response](/http-api/#error-response) | The caller is not a member of the guild or lacks `MANAGE_WEBHOOKS` |
|
||||
| 404 | [error response](/http-api/#error-response) | Guild does not exist, returning `UNKNOWN_GUILD` |
|
||||
@@ -792,7 +791,7 @@ Returns an array of [webhook objects](#webhook-object) in a guild. The caller mu
|
||||
|
||||
<sup>2</sup> The missing-authenticator code is `TWO_FACTOR_REQUIRED`, returned only after Fluxer confirms the permission itself
|
||||
|
||||
<sup>3</sup> `ACCOUNT_SUSPICIOUS_ACTIVITY` for an outstanding required action, `MISSING_ACCESS` for an unavailable guild, `MISSING_PERMISSIONS` for a membership or permission failure, and `ACCESS_DENIED` otherwise
|
||||
<sup>3</sup> `MISSING_ACCESS` for an unavailable guild, `MISSING_PERMISSIONS` for a membership or permission failure, and `ACCESS_DENIED` otherwise
|
||||
|
||||
A webhook is returned only when the caller also holds both `VIEW_CHANNEL` and `MANAGE_WEBHOOKS` in that webhook's own channel. A webhook with no channel target is never returned.
|
||||
|
||||
@@ -820,7 +819,6 @@ Returns an array of [webhook objects](#webhook-object) in a guild text, voice, o
|
||||
| 400 | [error response](/http-api/#error-response) | Path parameter is not a valid snowflake |
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | The caller holds `MANAGE_WEBHOOKS` with no enrolled authenticator in an elevated-MFA guild |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | Credential is a bearer token |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The account has an outstanding required action |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The guild is unavailable |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The caller is not a member of the channel's guild |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The caller lacks channel access or `MANAGE_WEBHOOKS` |
|
||||
@@ -830,7 +828,7 @@ Returns an array of [webhook objects](#webhook-object) in a guild text, voice, o
|
||||
|
||||
<sup>1</sup> The missing-authenticator code is `TWO_FACTOR_REQUIRED`, returned only after Fluxer confirms the permission itself
|
||||
|
||||
<sup>2</sup> `ACCOUNT_SUSPICIOUS_ACTIVITY` for an outstanding required action, `MISSING_ACCESS` for an unavailable guild, `NSFW_CONTENT_AGE_RESTRICTED` for an unverified account in an age restricted channel, `MISSING_PERMISSIONS` for a membership, channel access, or permission failure, and `ACCESS_DENIED` otherwise
|
||||
<sup>2</sup> `MISSING_ACCESS` for an unavailable guild, `NSFW_CONTENT_AGE_RESTRICTED` for an unverified account in an age restricted channel, `MISSING_PERMISSIONS` for a membership, channel access, or permission failure, and `ACCESS_DENIED` otherwise
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -872,7 +870,7 @@ The decoded bytes must be at most the resolved [avatar_max_size](/http-api/insta
|
||||
| 400<sup>2</sup> | [error response](/http-api/#error-response) | The guild or channel webhook allowance is already reached |
|
||||
| 400 | [error response](/http-api/#error-response) | The caller holds `MANAGE_WEBHOOKS` with no enrolled authenticator in an elevated-MFA guild |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential is a bearer token |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action |
|
||||
| 403 | [error response](/http-api/#error-response) | The account is limited |
|
||||
| 403 | [error response](/http-api/#error-response) | The guild is unavailable |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is not a member of the channel's guild |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller lacks channel access or `MANAGE_WEBHOOKS` |
|
||||
@@ -892,7 +890,7 @@ The decoded bytes must be at most the resolved [avatar_max_size](/http-api/insta
|
||||
| Caller holds the permission but has no enrolled authenticator | 400 `TWO_FACTOR_REQUIRED` |
|
||||
| Schema or image failure | 400 `INVALID_FORM_BODY` |
|
||||
| Name is blocked, or the avatar hash is banned | 403 `CONTENT_BLOCKED` |
|
||||
| Account has an outstanding required action | 403 `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| Account is limited | 403 `ACCOUNT_LIMITED` |
|
||||
| Guild is unavailable | 403 `MISSING_ACCESS` |
|
||||
| Unverified account in an age restricted channel | 403 `NSFW_CONTENT_AGE_RESTRICTED` |
|
||||
| Membership, channel access, or permission failure | 403 `MISSING_PERMISSIONS` |
|
||||
@@ -928,13 +926,12 @@ Returns a [webhook object](#webhook-object). The caller must be a member of the
|
||||
| 400 | [error response](/http-api/#error-response) | Path parameter is not a valid snowflake |
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | The caller holds `MANAGE_WEBHOOKS` with no enrolled authenticator in an elevated-MFA guild |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | Credential is a bearer token |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The account has an outstanding required action |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The caller is not a member of the webhook's guild or lacks `MANAGE_WEBHOOKS` in the webhook's channel |
|
||||
| 404 | [error response](/http-api/#error-response) | Webhook does not exist, returning `UNKNOWN_WEBHOOK`, or its guild does not resolve, returning `UNKNOWN_GUILD` |
|
||||
|
||||
<sup>1</sup> The missing-authenticator code is `TWO_FACTOR_REQUIRED`, returned only after Fluxer confirms the permission itself
|
||||
|
||||
<sup>2</sup> `ACCOUNT_SUSPICIOUS_ACTIVITY` for an outstanding required action, `MISSING_PERMISSIONS` for a membership or permission failure, and `ACCESS_DENIED` otherwise
|
||||
<sup>2</sup> `MISSING_PERMISSIONS` for a membership or permission failure, and `ACCESS_DENIED` otherwise
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -984,7 +981,6 @@ The returned webhook object has the destination channel. Copies a channel follow
|
||||
| 400<sup>3</sup> | [error response](/http-api/#error-response) | A channel follower webhook cannot move into the destination channel |
|
||||
| 400 | [error response](/http-api/#error-response) | The caller holds `MANAGE_WEBHOOKS` with no enrolled authenticator in an elevated-MFA guild |
|
||||
| 403 | [error response](/http-api/#error-response) | Credential is a bearer token |
|
||||
| 403 | [error response](/http-api/#error-response) | The account has an outstanding required action |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller is not a member of the webhook's guild |
|
||||
| 403 | [error response](/http-api/#error-response) | The caller lacks `MANAGE_WEBHOOKS` in the current or destination channel |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The destination channel is age restricted and the account is not age verified |
|
||||
@@ -1010,7 +1006,6 @@ The returned webhook object has the destination channel. Copies a channel follow
|
||||
| Caller holds the permission but has no enrolled authenticator | 400 `TWO_FACTOR_REQUIRED` |
|
||||
| Schema or image failure, or an avatar on a channel follower webhook | 400 `INVALID_FORM_BODY` |
|
||||
| Name is blocked, or the avatar hash is banned | 403 `CONTENT_BLOCKED` |
|
||||
| Account has an outstanding required action | 403 `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| Unverified account in an age restricted destination channel | 403 `NSFW_CONTENT_AGE_RESTRICTED` |
|
||||
| Membership, channel access, or permission failure | 403 `MISSING_PERMISSIONS` |
|
||||
| Any other credential refusal | 403 `ACCESS_DENIED` |
|
||||
@@ -1051,13 +1046,12 @@ No operation restores the webhook or reissues its token. Messages it already cre
|
||||
| 400 | [error response](/http-api/#error-response) | Path parameter is not a valid snowflake |
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | The caller holds `MANAGE_WEBHOOKS` with no enrolled authenticator in an elevated-MFA guild |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | Credential is a bearer token |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The account has an outstanding required action |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | The caller is not a member of the webhook's guild or lacks `MANAGE_WEBHOOKS` in the webhook's channel |
|
||||
| 404 | [error response](/http-api/#error-response) | Webhook does not exist, returning `UNKNOWN_WEBHOOK`, or its guild does not resolve, returning `UNKNOWN_GUILD` |
|
||||
|
||||
<sup>1</sup> The missing-authenticator code is `TWO_FACTOR_REQUIRED`, returned only after Fluxer confirms the permission itself
|
||||
|
||||
<sup>2</sup> `ACCOUNT_SUSPICIOUS_ACTIVITY` for an outstanding required action, `MISSING_PERMISSIONS` for a membership or permission failure, and `ACCESS_DENIED` otherwise
|
||||
<sup>2</sup> `MISSING_PERMISSIONS` for a membership or permission failure, and `ACCESS_DENIED` otherwise
|
||||
|
||||
### Side effects
|
||||
|
||||
|
||||
@@ -994,10 +994,6 @@ Defaults to the inverse of `FLUXER_SELF_HOSTED`. Off by default on a self-hosted
|
||||
|
||||
With feeds off, Fluxer checks none of this data. The first worker start with feeds off removes the URL feed file and every `malware_bazaar` file-SHA ban that no Admin added. File-SHA bans added through the Admin API stay. Turning feeds back on downloads the URLs within six hours and the hashes within twelve. Compose forwards it from `.env`.
|
||||
|
||||
#### `FLUXER_PHONE_VERIFICATION_ENABLED`
|
||||
|
||||
Defaults to the inverse of `FLUXER_SELF_HOSTED`. Phone verification needs an external responder on the `rpc.phone.v1` NATS subjects, and none ships with Fluxer. With it off, the `VERY_HIGH` guild verification level is evaluated as `HIGH`, the guild settings stop offering it, and the API rejects it. Turn it on only when your own responder answers those subjects. Compose forwards it from `.env`.
|
||||
|
||||
#### `FLUXER_BREACHED_PASSWORD_CHECK_ENABLED`
|
||||
|
||||
Defaults to the inverse of `FLUXER_SELF_HOSTED`. Breached password rejection. Sends the first five characters of the password's SHA-1 hash to `api.pwnedpasswords.com`. Off by default on a self-hosted instance. Compose forwards it from `.env`.
|
||||
@@ -1082,7 +1078,7 @@ Default `development`. The runtime mode. `development`, `production`, or `test`.
|
||||
|
||||
#### `FLUXER_SELF_HOSTED`
|
||||
|
||||
Default `false`. The self-host switch. Compose sets `true`. It relaxes the production Postgres SSL requirement, seeds the limit tier, gates registration, donation and discovery controllers, keeps premium billing off until it is set up as [Payments](#payments) describes, and turns blocklist feeds and phone verification off.
|
||||
Default `false`. The self-host switch. Compose sets `true`. It relaxes the production Postgres SSL requirement, seeds the limit tier, gates registration, donation and discovery controllers, keeps premium billing off until it is set up as [Payments](#payments) describes, and turns blocklist feeds off.
|
||||
|
||||
`/_metrics` on `api`, `media-proxy`, `gateway`, and `push`, plus the Gateway's `/_health/ready`, `/_health/drain`, and `/_health/undrain`, are gated to loopback peers, so no proxy reaches them. The probes that work from outside are `/api/_health`, `/gateway/_health`, `/media/_health`, and the edge's own `/_health`.
|
||||
|
||||
|
||||
@@ -51,8 +51,6 @@ The following operations verify a CAPTCHA while the check is on.
|
||||
|
||||
Create private channel is gated only on the group direct message path, where the request body has a `recipients` member. A one-to-one direct message request omits the field and is never gated.
|
||||
|
||||
[Send phone verification](/http-api/users/phone-verification/#send-phone-verification) verifies a CAPTCHA only when the phone verification service asks for one. It then answers and accepts the handshake the same way. When the check is off or the account is exempt, that send is refused with `PHONE_RATE_LIMIT_EXCEEDED` instead.
|
||||
|
||||
## Exemption
|
||||
|
||||
Fluxer skips the check in three cases, and the operation then proceeds with no CAPTCHA header. The authenticated account's email address is on an exempt domain. The authenticated account holds the [`APP_STORE_REVIEWER`](/admin-api/users/#account-flags) flag. The request body has an `email` that belongs to an account holding that flag. Discovery does not report exemptions, so clients must handle a challenge on every gated operation.
|
||||
|
||||
@@ -88,9 +88,7 @@ The `X-RateLimit-Scope` header is the scope that produced a denial.
|
||||
| global | The denial came from the global bucket |
|
||||
| shared<sup>1</sup> | The denial came from an allowance that several accounts can exhaust for each other |
|
||||
|
||||
<sup>1</sup> No route bucket declares a scope of its own, so every route bucket denial reports `user`. Phone verification and the announcement channel allowances are the live sources of `shared`
|
||||
|
||||
Phone verification reports `shared` when the per-number send allowance or a number-scoped provider cooldown produced the denial. Both are keyed by the submitted number, so two accounts sending to one number share the allowance.
|
||||
<sup>1</sup> No route bucket declares a scope of its own, so every route bucket denial reports `user`. The announcement channel allowances are the live sources of `shared`
|
||||
|
||||
[Crosspost message](/http-api/messages/#crosspost-message) reports `shared` for its channel publish allowance, and an edit of a published message reports it for the per-message edit allowance. Every member who publishes or edits draws on the same allowance.
|
||||
|
||||
@@ -158,12 +156,8 @@ The 400 shape has no `retry_after` member, no `X-RateLimit-*` header, and no `Re
|
||||
| [Report message](/http-api/reports/#report-message) | 3 per hour, keyed by the reporter and the channel together | `RATE_LIMITED` |
|
||||
| [Report message](/http-api/reports/#report-message) | 20 per hour, keyed by the reported message, across all reporters | `RATE_LIMITED` |
|
||||
| [Report message](/http-api/reports/#report-message) | 4 per hour, keyed by the reporter and the guild together, for a guild message | `RATE_LIMITED` |
|
||||
| [Send phone verification](/http-api/users/phone-verification/#send-phone-verification) | 3 per 6 hours, keyed by the authenticated account | `PHONE_RATE_LIMIT_EXCEEDED` |
|
||||
| [Send phone verification](/http-api/users/phone-verification/#send-phone-verification) | 3 per 5 days, keyed by the submitted number | `PHONE_RATE_LIMIT_EXCEEDED` |
|
||||
| [Resend IP authorisation](/http-api/authentication/#resend-ip-authorisation) | Nothing in the first 30 seconds after the ticket was issued, keyed by the authorisation ticket | `IP_AUTHORIZATION_RESEND_COOLDOWN` |
|
||||
|
||||
SMS provider throttling can impose an additional cooldown. It returns `PHONE_RATE_LIMIT_EXCEEDED` with the remaining delay.
|
||||
|
||||
The Resend IP authorisation cooldown has no `X-RateLimit-*` header. It has a `Retry-After` header in whole seconds, and the body reports that delay again as a top-level `resend_available_in` and `retry_after`. A second resend on one ticket returns 400 `IP_AUTHORIZATION_RESEND_LIMIT_EXCEEDED`. The allowance never refills, and the ticket expires 15 minutes after it was issued.
|
||||
|
||||
### Announcement channel allowances
|
||||
|
||||
Reference in New Issue
Block a user