mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
docs: reword vague sentences and fix wrong claims (#2760)
This commit is contained in:
1 parent
7d56481aba
commit
69c42cff90
91 files changed
+651
-645
No files matched your search
@@ -10,9 +10,9 @@ 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`. Fluxer accepts an account with an outstanding required action everywhere here.
|
||||
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).
|
||||
|
||||
The sudo-gated routes read `X-Fluxer-Sudo-Mode-JWT`. A valid proof for the authenticated account satisfies [sudo mode](/http-api/users/mfa/#sudo-mode) on its own, and Fluxer echoes it back in the response header. Fluxer issues no token to an account with no authenticator, so that account proves sudo mode with `password` in the body.
|
||||
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 with no authenticator, so that account proves sudo mode with `password` in the body.
|
||||
|
||||
:::caution[Credentials are shown once]
|
||||
A client secret is returned only by [Create application](#create-application) and [Reset client secret](#reset-client-secret), and a [bot token](/authentication/#token-formats) only by [Create application](#create-application) and [Reset bot token](#reset-bot-token).
|
||||
@@ -20,7 +20,7 @@ A client secret is returned only by [Create application](#create-application) an
|
||||
|
||||
## Rate limits
|
||||
|
||||
A bucket name ending in `::client_id` still counts one allowance for the authenticated user across every application that user owns. [List owned applications](#list-owned-applications), [Get current application](#get-current-application), [Get application](#get-application), [Get public application](#get-public-application), and [List OAuth2 authorisations](/http-api/oauth2/#list-oauth2-authorisations) all draw on one `oauth_dev:clients:list` allowance. [Get bot application](#get-bot-application) draws on the same bucket, keyed by the bot account the token names. That allowance is separate from the owner's.
|
||||
A bucket whose name ends in `::client_id` gives the authenticated user one allowance. Requests for every application that user owns count against that one allowance. [List owned applications](#list-owned-applications), [Get current application](#get-current-application), [Get application](#get-application), [Get public application](#get-public-application), and [List OAuth2 authorisations](/http-api/oauth2/#list-oauth2-authorisations) all draw on one `oauth_dev:clients:list` allowance. [Get bot application](#get-bot-application) draws on the same bucket, keyed by the bot account the token names. That allowance is separate from the owner's.
|
||||
|
||||
## Application object
|
||||
|
||||
@@ -42,7 +42,7 @@ The application record as its owner sees it.
|
||||
|
||||
<sup>2</sup> Present only in the response to [Create application](#create-application) and [Reset client secret](#reset-client-secret), which are the only operations that issue one
|
||||
|
||||
<sup>3</sup> Present only when the operation resolved the bot account, which excludes [Reset client secret](#reset-client-secret), an application that owns no bot account, and an application whose bot account record can no longer be read
|
||||
<sup>3</sup> Present in every response except [Reset client secret](#reset-client-secret), and absent for an application that owns no bot account or whose bot account record can no longer be read
|
||||
|
||||
### Example
|
||||
|
||||
@@ -109,7 +109,7 @@ The application as any caller sees it, including a caller that presents no crede
|
||||
| bot<sup>4</sup> | ?[application bot](#application-bot-object) object | The bot account the application owns |
|
||||
| current_user?<sup>5</sup> | ?[partial user](/http-api/users/#partial-user-object) object | The requesting account |
|
||||
|
||||
<sup>1</sup> Null when the application has no bot account or that account has no avatar, and the stored hash is reported unchanged
|
||||
<sup>1</sup> Null when the application has no bot account or that account has no avatar. Otherwise it is the bot account's stored avatar hash, and an animated hash keeps its `a_` prefix
|
||||
|
||||
<sup>2</sup> Contains `bot` when the application owns a bot account, and is otherwise empty
|
||||
|
||||
@@ -121,7 +121,7 @@ The application as any caller sees it, including a caller that presents no crede
|
||||
|
||||
## Current bot application object
|
||||
|
||||
The application as the bot token that application issued sees it.
|
||||
The application that issued the requesting bot token, as returned to that token.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -140,7 +140,7 @@ The application as the bot token that application issued sees it.
|
||||
|
||||
<sup>1</sup> Read from the bot account, so it changes with [Update bot profile](#update-bot-profile), and it is null when the application has no bot account
|
||||
|
||||
<sup>2</sup> No application signing key is available
|
||||
<sup>2</sup> Always 64 `0` characters, because no application signing key is available
|
||||
|
||||
<sup>3</sup> The request fails with 401 `INVALID_TOKEN` when the owning account no longer exists
|
||||
|
||||
@@ -150,7 +150,7 @@ The application as the bot token that application issued sees it.
|
||||
|
||||
## Bot profile object
|
||||
|
||||
The bot account's profile fields after an update. [Update bot profile](#update-bot-profile) is the only operation that returns this shape, and it has no bot token.
|
||||
The bot account's profile fields after an update. [Update bot profile](#update-bot-profile) is the only operation that returns this shape. The object has no `token` field.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -285,7 +285,7 @@ This representation exposes the full registered redirect URI list and the bot pr
|
||||
|
||||
Creates an application together with its bot account. Returns the [application](#application-object) object with the initial client secret and bot token.
|
||||
|
||||
An unclaimed account cannot create an application. An account is unclaimed while it holds no password credential, is not a bot, and does not have the single sign-on trait.
|
||||
An unclaimed account cannot create an application. An account is unclaimed while it holds no password credential, is not a bot, and has not been linked to a single sign-on identity.
|
||||
|
||||
### Request headers
|
||||
|
||||
@@ -294,7 +294,7 @@ An unclaimed account cannot create an application. An account is unclaimed while
|
||||
| X-Captcha-Token?<sup>1</sup> | string | The CAPTCHA proof for the request |
|
||||
| X-Captcha-Type?<sup>2</sup> | string | The CAPTCHA provider to verify against, either `hcaptcha` or `turnstile` |
|
||||
|
||||
<sup>1</sup> A missing proof returns 400 `CAPTCHA_REQUIRED` and a rejected proof returns 400 `INVALID_CAPTCHA`. Fluxer skips verification when CAPTCHA is disabled for the instance, when the account has the CAPTCHA exemption flag, or when the caller's contact has the CAPTCHA exemption capability, as described by [CAPTCHA handling](/topics/captcha/)
|
||||
<sup>1</sup> A missing proof returns 400 `CAPTCHA_REQUIRED` and a rejected proof returns 400 `INVALID_CAPTCHA`. Fluxer skips verification when CAPTCHA is disabled for the instance, when the account has the CAPTCHA exemption flag, or when the instance's account policy grants the caller's email address the CAPTCHA exemption capability, as described by [CAPTCHA handling](/topics/captcha/)
|
||||
|
||||
<sup>2</sup> Fluxer verifies against the instance's configured provider when the header is absent
|
||||
|
||||
@@ -405,7 +405,7 @@ Removing a redirect URI takes effect immediately for new authorisation requests.
|
||||
|
||||
### Side effects
|
||||
|
||||
The submitted fields replace the matching application configuration. Neither credential is rotated, and no Gateway Dispatch is emitted.
|
||||
Each submitted field replaces the stored value of that field. Neither credential is rotated, and no Gateway Dispatch is emitted.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -444,7 +444,7 @@ Updates the bot account an application owns and returns the resulting [bot profi
|
||||
|
||||
<sup>5</sup> Fluxer reads only the [bot flags](#bot-flags) from the supplied bitfield and sets or clears each to match. It ignores every other bit
|
||||
|
||||
Fluxer checks the decoded bytes of `avatar` and `banner` against the instance's avatar byte ceiling, which applies to both fields and defaults to 10 MiB. Each image also passes the format allowlist and the animation rules of the asset policy for the field it sets. Pixel dimensions are never checked.
|
||||
Fluxer checks the decoded bytes of `avatar` and `banner` against the instance's avatar byte ceiling, which applies to both fields and defaults to 10 MiB. An image in a format the field does not accept, an animated image on a field that accepts no animation, and any animated AVIF return the field code `INVALID_IMAGE_FORMAT`. Pixel dimensions are never checked.
|
||||
|
||||
### Response
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ The public single sign-on state. The same object is embedded by the [instance di
|
||||
| display_name | ?string | The configured provider display name, or null when none is set |
|
||||
| redirect_uri | string | The default OAuth2 redirect URI used for the provider callback |
|
||||
|
||||
<sup>1</sup> The value is true only when the operator has enabled SSO and the resolved provider configuration is complete, so a partially configured provider reports false
|
||||
<sup>1</sup> The value is true only when the operator has enabled SSO and the provider has an authorisation URL, a token URL, a client ID, and a JWKS URL or user info URL. Each URL is configured or discovered from the issuer. A provider that lacks any of them reports false
|
||||
|
||||
<sup>2</sup> The value is true only when `enabled` is also true, and every local authentication operation then returns 403 `SSO_REQUIRED`
|
||||
|
||||
@@ -85,7 +85,7 @@ The parameters for sending the user to the identity provider, bound to one new S
|
||||
| state<sup>1</sup> | string | The one-use CSRF state |
|
||||
| redirect_uri | string | The callback URI bound to this state |
|
||||
|
||||
<sup>1</sup> The state is consumed by the first [complete SSO](#complete-sso) attempt that resolves it
|
||||
<sup>1</sup> The first [complete SSO](#complete-sso) request that presents the unexpired state consumes it, whether or not that request then succeeds
|
||||
|
||||
A client MUST return the state unchanged and MUST NOT interpret its contents.
|
||||
|
||||
@@ -264,8 +264,8 @@ The code that identifies one pending desktop handoff.
|
||||
|
||||
<sup>1</sup> The code is 12 characters drawn from the alphabet `ABCDEFGHJKMNPQRSTUVWXYZ23456789`, rendered as two groups of six separated by a hyphen
|
||||
|
||||
:::caution[The code alone cancels a handoff]
|
||||
Anyone who learns the code reads the pending device metadata and cancels the handoff. The token needs `poll_secret` as well. A client MUST show the code only to the person performing the handoff.
|
||||
:::caution[The code alone reads the device metadata]
|
||||
Anyone who learns the code reads the pending device metadata. Reading the token and cancelling the handoff both require `poll_secret`. A client MUST show the code only to the person performing the handoff.
|
||||
:::
|
||||
|
||||
## Handoff information object
|
||||
@@ -407,9 +407,9 @@ Approval mode registration instead returns 403 `REGISTRATION_PENDING_APPROVAL` a
|
||||
|
||||
<RouteHeader method="POST" path="/v1/auth/register" unauthenticated />
|
||||
|
||||
Creates an ordinary account. Returns an [authentication token response](#authentication-token-response) when the instance admits the account immediately and a [registration pending approval response](#registration-pending-approval-response-object) when it does not. Emits a [Guild Member Add](/gateway/events/#guild-member-add) Gateway event when an invite or instance community admission takes effect.
|
||||
Creates an ordinary account. Returns an [authentication token response](#authentication-token-response) when the instance admits the account immediately and a [registration pending approval response](#registration-pending-approval-response-object) when it does not. Emits a [Guild Member Add](/gateway/events/#guild-member-add) Gateway event for each guild the new account joins through an invite or through the instance's single community guild.
|
||||
|
||||
Registration verifies [CAPTCHA](/topics/captcha/) when CAPTCHA is enabled. It permits 3 attempts per hour for each client IP address and 15 per hour for each client subnet, which is the IPv4 /24 or IPv6 /48 network. A supplied email address permits 3 attempts per 15 minutes of its own. Those allowances are separate from the route bucket, and only a deployment that relaxes registration rate limits disables them.
|
||||
Registration verifies [CAPTCHA](/topics/captcha/) when CAPTCHA is enabled. It permits 3 attempts per hour for each client IP address and 15 per hour for each client subnet, which is the IPv4 /24 or IPv6 /48 network. A supplied email address permits 3 attempts per 15 minutes of its own. Those allowances are separate from the route bucket, and only a deployment with `dev.relax_registration_rate_limits` set to true disables them.
|
||||
|
||||
### Request headers
|
||||
|
||||
@@ -478,7 +478,7 @@ There is no retry key. A repeated request with the same values creates a second
|
||||
|
||||
The operation applies the instance's registration, email-domain, breached-password, and regional policies before creating the account, and its risk policy can set suspicious activity flags on the created account. It records the accepted terms and privacy policy, authorises the registering client IP address, and sends an email verification message when the instance sends email. An instance that sends no email marks the address verified at creation instead.
|
||||
|
||||
Registration can accept a supplied or instance-configured invite, and the account can join the instance community. Each join emits [Guild Member Add](/gateway/events/#guild-member-add) to the affected guild's sessions. Registration policy can suppress invite admission.
|
||||
Registration accepts the `invite_code` from the body, or the instance's configured auto-join invite when the body has none. On an instance with single-community mode enabled, the account also joins the community guild. Each join emits [Guild Member Add](/gateway/events/#guild-member-add) to the affected guild's sessions. Registration policy can suppress invite admission.
|
||||
|
||||
Approval mode registration creates no guild membership and no authentication session, and the account cannot sign in until an administrator approves it. Every other successful registration creates one session and returns its token.
|
||||
|
||||
@@ -533,7 +533,7 @@ Account policy can return 403 `REGISTRATION_PENDING_APPROVAL`, 403 `REGISTRATION
|
||||
On an account the user had disabled, a correct password clears the disabled state. It also cancels a self-scheduled deletion if erasure has not started. Both happen before any second factor is requested, without a separate confirmation step. Once erasure starts, login returns `ACCOUNT_SUSPENDED_PERMANENTLY`.
|
||||
:::
|
||||
|
||||
The login clears an expired temporary suspension the same way, but returns the 403 of a live temporary or permanent administrator suspension without clearing it.
|
||||
A correct password also clears an expired temporary suspension before any second factor is requested. A live temporary or permanent administrator suspension stays in place, and the login returns its 403.
|
||||
|
||||
### Response
|
||||
|
||||
@@ -860,7 +860,7 @@ Password recovery verifies CAPTCHA when CAPTCHA is enabled. Fluxer consumes both
|
||||
<sup>1</sup> An address whose domain has no usable DNS records returns the field code `INVALID_EMAIL_ADDRESS`, while an address that passes DNS validation but belongs to no account returns the ordinary success response
|
||||
|
||||
:::note[Account existence is not disclosed]
|
||||
An address that resolves to no account produces the same 204 response as an address that resolves to an ordinary account. Only DNS validation failure, the route bucket, the extra allowances, and an address belonging to a bot account can produce a different outcome.
|
||||
An address that resolves to no account produces the same 204 response as an address that resolves to an ordinary account. Only a DNS validation failure, the route bucket, the client IP address and email address allowances, and an address belonging to a bot account can produce a different outcome.
|
||||
:::
|
||||
|
||||
### Response
|
||||
@@ -943,7 +943,7 @@ An unknown or already consumed token, and a token bound to a deleted account, re
|
||||
Passwords are checked against a breached-password corpus, as they are during [registration](#register-an-account) and [email reversion](#revert-an-email-change).
|
||||
|
||||
:::caution[Resetting a password revokes every existing session]
|
||||
A successful reset terminates every authentication session on the account, including the one any other device is holding. It then issues one fresh session. An account with a second factor instead receives an MFA ticket, and the session follows the factor.
|
||||
A successful reset terminates every authentication session on the account, including the one any other device is holding. It then issues one fresh session. An account with a second factor receives an MFA ticket, and completing MFA with that ticket creates the session.
|
||||
:::
|
||||
|
||||
### Response
|
||||
@@ -971,7 +971,7 @@ An account with no second factor then receives one new session and its token. An
|
||||
|
||||
<RouteHeader method="POST" path="/v1/auth/email-revert" unauthenticated />
|
||||
|
||||
Consumes the token delivered to the previous email address, restores that address, replaces the password, and rebuilds the account's credential state. Returns an [authentication token response](#authentication-token-response). Emits a [User Update](/gateway/events/#user-update) Gateway event.
|
||||
Consumes the token delivered to the previous email address, restores that address, replaces the password, terminates every authentication session, and clears every second factor. The requesting IP address becomes the only authorised IP address. Returns an [authentication token response](#authentication-token-response). Emits a [User Update](/gateway/events/#user-update) Gateway event.
|
||||
|
||||
The token is valid for 24 hours after the address change that issued it.
|
||||
|
||||
@@ -1005,7 +1005,7 @@ Fluxer checks the replacement password against the same breached-password corpus
|
||||
|
||||
### Side effects
|
||||
|
||||
The previous email address becomes verified and the replacement password takes effect. All other sessions, second factors and authorised IP addresses are removed. Only the requesting IP address remains authorised.
|
||||
The previous email address becomes verified and the replacement password becomes the account password. All other sessions, second factors and authorised IP addresses are removed. Only the requesting IP address remains authorised.
|
||||
|
||||
The account receives [User Update](/gateway/events/#user-update). Existing Gateway sessions end as described under [shared behaviour](#shared-behaviour), and the response returns one new authentication session.
|
||||
|
||||
@@ -1115,7 +1115,7 @@ An unknown, expired, already consumed, or account-mismatched token returns the f
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | The IP address was authorised and the login result was published |
|
||||
| 204 | empty | The IP address was authorised and the new session token is readable through poll IP authorisation |
|
||||
| 400 | [error response](/http-api/#error-response) | The body or authorisation token is invalid |
|
||||
| 403 | [error response](/http-api/#error-response) | SSO enforcement rejects the operation or the token resolves to a bot account |
|
||||
| 404 | [error response](/http-api/#error-response) | The token resolves to an account that no longer exists |
|
||||
@@ -1147,7 +1147,7 @@ Sends the authorisation message for an outstanding IP authorisation ticket again
|
||||
|
||||
<sup>1</sup> The resend reuses the authorisation token already bound to the ticket, so a message delivered by an earlier send remains valid
|
||||
|
||||
An unknown or expired ticket returns the field code `INVALID_OR_EXPIRED_AUTHORIZATION_TICKET`. A resend before the delay elapses returns 429 `IP_AUTHORIZATION_RESEND_COOLDOWN` with a `Retry-After` header and a top-level `resend_available_in` in seconds. A second resend returns 400 `IP_AUTHORIZATION_RESEND_LIMIT_EXCEEDED`.
|
||||
An unknown or expired ticket returns the field code `INVALID_OR_EXPIRED_AUTHORIZATION_TICKET`. A resend less than 30 seconds after the ticket was issued returns 429 `IP_AUTHORIZATION_RESEND_COOLDOWN` with a `Retry-After` header and a top-level `resend_available_in` in seconds. A second resend returns 400 `IP_AUTHORIZATION_RESEND_LIMIT_EXCEEDED`.
|
||||
|
||||
:::caution[A lost response still spends the resend]
|
||||
There is no retry key. A client that loses the response cannot tell whether the message was delivered, and a retry returns 400 `IP_AUTHORIZATION_RESEND_LIMIT_EXCEEDED` once the first request marked the resend used.
|
||||
@@ -1414,7 +1414,7 @@ Reports the state of a handoff and delivers the new session token once, to a cal
|
||||
| --- | --- | --- |
|
||||
| code | string | The handoff code, using the same normalisation contract as [get desktop handoff information](#get-desktop-handoff-information) |
|
||||
|
||||
### Request body
|
||||
### JSON body
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
@@ -1457,18 +1457,24 @@ Discards a handoff and everything stored against its code. Authentication is not
|
||||
| --- | --- | --- |
|
||||
| code | string | The handoff code, using the same normalisation contract as [get desktop handoff information](#get-desktop-handoff-information) |
|
||||
|
||||
An unknown or already expired code is an idempotent success.
|
||||
### JSON body
|
||||
|
||||
:::caution[Anyone holding the code can cancel]
|
||||
The route checks no credential and the code is its only input, so a party that learns the code can cancel a handoff the initiating device is still waiting on. The initiating device observes this as the `expired` status.
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| poll_secret | string | The secret returned by [initiate desktop handoff](#initiate-desktop-handoff) |
|
||||
|
||||
An unknown or already expired code has no stored secret, so it returns 400 `INVALID_HANDOFF_CODE`.
|
||||
|
||||
:::caution[Cancelling requires the poll secret]
|
||||
The route requires the `poll_secret` that [initiate desktop handoff](#initiate-desktop-handoff) returned, sent in the JSON body. A wrong secret returns 400 `INVALID_HANDOFF_CODE`, so a party that knows only the code cannot cancel the handoff. After a cancellation the initiating device reads the `expired` status.
|
||||
:::
|
||||
|
||||
### Response
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | The handoff was discarded, or no handoff existed for that code |
|
||||
| 400 | [error response](/http-api/#error-response) | The code is malformed, returning `INVALID_HANDOFF_CODE` |
|
||||
| 204 | empty | The handoff was discarded |
|
||||
| 400 | [error response](/http-api/#error-response) | The code is malformed, or the secret does not match, returning `INVALID_HANDOFF_CODE` |
|
||||
| 429 | [rate limit response](/topics/rate-limits/#rate-limit-response-object) | Route or global bucket denies the request |
|
||||
| 500 | [error response](/http-api/#error-response) | Unexpected internal failure occurs |
|
||||
| 503 | [error response](/http-api/#error-response) | The instance is at its in-flight request ceiling |
|
||||
|
||||
@@ -6,12 +6,12 @@ description: Checkout, card preapproval, gift purchase, age verification, refund
|
||||
|
||||
import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
These routes take payment for premium, verify an account holder's age, and refund the most recent purchase. Each finishes on the payment provider's own pages, so a route here creates the provider session and returns its URL for the browser to open. [Receive Stripe webhook](#receive-stripe-webhook) takes the signed events the provider sends back. The [Premium resource](/http-api/premium/) owns entitlement state, mirrored billing data and subscription self-service.
|
||||
These routes take payment for premium, verify an account holder's age, and refund the most recent purchase. Checkout, card preapproval and age verification each finish on the payment provider's own pages, so those routes create the provider session and return its URL for the browser to open. [Receive Stripe webhook](#receive-stripe-webhook) takes the signed events the provider sends back. The [Premium resource](/http-api/premium/) owns entitlement state, mirrored billing data and subscription self-service.
|
||||
|
||||
Every route here is hosted-only, as [deployment availability](/http-api/deployment-availability/) describes. All of them are user-only except [receive Stripe webhook](#receive-stripe-webhook) and [continue localised card preapproval](#continue-localised-card-preapproval).
|
||||
|
||||
:::note[Every response is a Fluxer object]
|
||||
The responses are Fluxer redirect, eligibility, refund and acknowledgement objects. The only payment provider values in them are the identifiers on a [refund](#refund-object).
|
||||
The responses are Fluxer redirect, eligibility, refund and acknowledgement objects. They copy only a few payment provider values, such as the identifiers on a [refund](#refund-object) and the `invoice_id` of a [refund eligibility](#refund-eligibility-object) object.
|
||||
:::
|
||||
|
||||
## Redirect URL object
|
||||
@@ -36,7 +36,7 @@ One absolute URL that completes a billing operation in a browser. [Create subscr
|
||||
|
||||
## Localised card preapproval result object
|
||||
|
||||
The state of one card preapproval flow. A localised recurring price is offered only to a card issued in the matching country. Fluxer creates the paid session only after a separate setup mode session has proven the card country. `status` says which of the variants the object is, and each variant defines its own members.
|
||||
The state of one card preapproval flow. A localised recurring price is offered only to a card issued in the matching country. Fluxer creates the paid session only after a separate setup mode session, which takes no payment, has shown that the card was issued in the matching country. `status` says which of the variants the object is, and each variant defines its own members.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -67,7 +67,7 @@ The state of one card preapproval flow. A localised recurring price is offered o
|
||||
|
||||
| Value | Description |
|
||||
| --- | --- |
|
||||
| pending | The setup session has not completed, or a concurrent continuation is already resolving the same token |
|
||||
| pending | Setup has not completed, or another [continue localised card preapproval](#continue-localised-card-preapproval) call for this token is still running |
|
||||
| ready | The card was approved and the paid checkout URL is available |
|
||||
| rejected | The card was refused and the paid checkout cannot continue |
|
||||
| expired | The token is empty, unknown, or has passed its one-day lifetime |
|
||||
@@ -85,7 +85,7 @@ The state of one card preapproval flow. A localised recurring price is offered o
|
||||
|
||||
## Refund eligibility object
|
||||
|
||||
Whether the account's most recent purchase can still be refunded without operator involvement. When no refundable purchase was resolved, `eligible` is false, every invoice member is null, and `cooldown_expires_at` still reports an active cooldown unless the reason is `feature_unavailable`. A client reads `eligible` and `reason` before rendering any amount or timestamp.
|
||||
Whether the account's most recent purchase can still be refunded without operator involvement. When no refundable purchase was resolved, `eligible` and `cancels_subscription` are false. `invoice_id`, `invoice_amount_paid_cents`, `currency`, `paid_at` and `refund_window_expires_at` are null. `cooldown_expires_at` still reports an active cooldown unless the reason is `feature_unavailable`. A client reads `eligible` and `reason` before rendering any amount or timestamp.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -158,7 +158,7 @@ The refund that [refund latest purchase](#refund-latest-purchase) created agains
|
||||
| subscription_id<sup>3</sup> | ?string | Payment provider subscription that was cancelled along with the refund |
|
||||
| status<sup>4</sup> | ?string | Provider status of the refund |
|
||||
|
||||
<sup>1</sup> Zero until the provider confirms the refund succeeded. Once confirmed it is the amount the provider accepted, which is one minor unit short of `invoice_amount_paid_cents` in the Pix case
|
||||
<sup>1</sup> Zero until the provider confirms the refund succeeded. Once confirmed it is the amount the provider accepted, which is one minor unit short of `invoice_amount_paid_cents` when the charge was paid with Pix and the invoice amount is greater than one minor unit
|
||||
|
||||
<sup>2</sup> Reported as `usd` when the resolved invoice has no currency of its own
|
||||
|
||||
@@ -214,7 +214,7 @@ Creates a recurring premium checkout session and returns a [redirect URL](#redir
|
||||
- An unverified email address returns 403 `PURCHASE_EMAIL_VERIFICATION_REQUIRED`.
|
||||
- The purchase-disabled premium flag returns 403 `PREMIUM_PURCHASE_BLOCKED` with the reason `purchase_disabled`.
|
||||
- A lifetime Visionary account cannot buy a recurring subscription and returns 403 `PREMIUM_PURCHASE_BLOCKED` with the reason `lifetime`.
|
||||
- An account whose payment provider customer already holds a subscription in the `active`, `trialing`, `past_due`, `unpaid`, `incomplete` or `paused` state returns the same code with the reason `existing_subscription`, unless the conversion below applies.
|
||||
- An account whose payment provider customer already holds a subscription in the `active`, `trialing`, `past_due`, `unpaid`, `incomplete` or `paused` state returns the same code with the reason `existing_subscription`, unless Fluxer converts the purchase into a scheduled billing cycle change, as described below.
|
||||
|
||||
`reason` is a top-level member of the error response, and an `existing_subscription` refusal also has the blocking status in the top-level `subscription_status` member.
|
||||
|
||||
@@ -279,7 +279,7 @@ Creates a setup mode session that captures and verifies a card before a localise
|
||||
### Limitations
|
||||
|
||||
- The claimed account, verified email and purchase flag requirements of [create subscription checkout](#create-subscription-checkout) apply unchanged, with the same codes.
|
||||
- An unresolvable country, meaning a request Fluxer cannot geolocate that also omits `country_code`, and a resolved price that is not a recurring price in a currency other than USD and EUR each return 400 `STRIPE_INVALID_PRODUCT_CONFIGURATION`.
|
||||
- A request that Fluxer cannot geolocate and that omits `country_code` returns 400 `STRIPE_INVALID_PRODUCT_CONFIGURATION`. A resolved price that is not a recurring price in a currency other than USD and EUR also returns 400 `STRIPE_INVALID_PRODUCT_CONFIGURATION`.
|
||||
- The submitted price is always recurring, so the lifetime block and the existing subscription block both apply and return 403 `PREMIUM_PURCHASE_BLOCKED` with the reason `lifetime` or `existing_subscription`.
|
||||
|
||||
### JSON body
|
||||
@@ -323,7 +323,7 @@ The flow stays pending until [receive Stripe webhook](#receive-stripe-webhook) p
|
||||
|
||||
Reports the current state of a preapproval flow and creates the paid checkout session once the card is approved, returning a [localised card preapproval result](#localised-card-preapproval-result-object) object.
|
||||
|
||||
The continuation token is the credential, and no credential supplied on the request selects the flow.
|
||||
The continuation token is the credential. Fluxer selects the flow from the token alone, and a credential supplied on the request selects no flow.
|
||||
|
||||
An empty token, an unknown token, and a token whose one-day flow has expired are all reported as `expired`, so the operation never discloses whether a flow exists.
|
||||
|
||||
@@ -333,10 +333,10 @@ An empty token, an unknown token, and a token whose one-day flow has expired are
|
||||
- A recorded price that no longer belongs to the recorded country catalogue returns 400 `STRIPE_INVALID_PRODUCT_CONFIGURATION`.
|
||||
- The claimed account, verified email, purchase flag, lifetime and existing subscription refusals all apply to the account that opened the flow.
|
||||
|
||||
Creating the paid session repeats the complete preparation that [create subscription checkout](#create-subscription-checkout) does, against the account, price and country recorded on the flow.
|
||||
To create the paid session, Fluxer runs every check of [create subscription checkout](#create-subscription-checkout) and creates a checkout session the same way, using the account, price and country recorded on the flow.
|
||||
|
||||
:::caution[Approval is resolved once]
|
||||
One continuation at a time can resolve an approved flow. While one call creates the paid session, another call for the same flow is reported as `pending`.
|
||||
Only one call at a time creates the paid session for an approved flow. While one call creates the paid session, another call for the same flow is reported as `pending`.
|
||||
:::
|
||||
|
||||
### JSON body
|
||||
@@ -360,7 +360,7 @@ One continuation at a time can resolve an approved flow. While one call creates
|
||||
|
||||
A `pending`, `rejected` or `expired` result changes nothing. For an approved flow, Fluxer attempts to make the approved card the customer's default invoice payment method, then creates the paid checkout even when that update is unavailable.
|
||||
|
||||
It applies the checkout preparation described by [create subscription checkout](#create-subscription-checkout) with the approved price and country. The same token then always resolves to the same paid checkout URL. Entitlement changes only after the matching signed event reaches [receive Stripe webhook](#receive-stripe-webhook).
|
||||
Fluxer runs the checks of [create subscription checkout](#create-subscription-checkout) and creates the checkout session with the approved price and country. The same token then always resolves to the same paid checkout URL. Entitlement changes only after the matching signed event reaches [receive Stripe webhook](#receive-stripe-webhook).
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -456,7 +456,7 @@ A deployment with no configured payment provider answers with `eligible` false a
|
||||
A failed payment-history lookup or an account with no payment history reports no refundable purchase.
|
||||
|
||||
:::note[The same object appears inside premium state]
|
||||
[Get premium state](/http-api/premium/#get-premium-state) also returns `billing.refund_eligibility`, which can lag behind this endpoint.
|
||||
[Get premium state](/http-api/premium/#get-premium-state) also returns `billing.refund_eligibility`, which Fluxer computes from the invoices it has stored. This endpoint reads the invoices from the payment provider, so the premium state value can be older.
|
||||
:::
|
||||
|
||||
### Response
|
||||
@@ -499,7 +499,7 @@ Once the provider confirms the refund succeeded, the subscription that produced
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [refund](#refund-object) object | The refund was created, or the idempotency key resolved an earlier identical refund |
|
||||
| 200 | [refund](#refund-object) object | The refund was created, or the request repeats an earlier refund of this invoice and returns that refund |
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | The account has no refundable purchase, or the payment provider cannot refund it |
|
||||
| 403 | [error response](/http-api/#error-response) | The purchase is outside the refund window, or the refund cooldown is active |
|
||||
| 404 | [error response](/http-api/#error-response) | The authenticated account record no longer exists |
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A call is the live voice session of a direct message or a group direct message. [Channels](/http-api/channels/) defines the channel itself, its recipient set, and the region list a caller can select.
|
||||
|
||||
Joining and leaving a call is a main Gateway operation. [Voice State Update](/gateway/commands/#voice-state-update) requests the placement, and [Call Create](/gateway/events/#call-create), [Call Update](/gateway/events/#call-update), and [Call Delete](/gateway/events/#call-delete) publish the resulting call state.
|
||||
Joining and leaving a call is a main Gateway operation. A client joins or leaves a call by sending [Voice State Update](/gateway/commands/#voice-state-update), and [Call Create](/gateway/events/#call-create), [Call Update](/gateway/events/#call-update), and [Call Delete](/gateway/events/#call-delete) publish the resulting call state.
|
||||
|
||||
Every route on this page is user-only. Bot and OAuth2 credentials are rejected.
|
||||
|
||||
@@ -68,7 +68,7 @@ Returns the [call eligibility object](#call-eligibility-object) for a direct mes
|
||||
|
||||
<sup>1</sup> A guild channel ID is rejected with 400 `INVALID_CHANNEL_TYPE_FOR_CALL`
|
||||
|
||||
A direct message reports `ringable` as false when the other recipient's incoming call policy excludes the caller. That policy is the `incoming_call_flags` bitfield of the recipient's [user settings](/http-api/users/settings/). Fluxer reads the nobody flag, the friends-only flag, an existing friendship, a mutual friend, a mutual guild, and finally the everyone flag, in that order. The silent-everyone flag reports `silent` as true for a caller admitted by the mutual friend, mutual guild, or everyone branch.
|
||||
A direct message reports `ringable` as false when the other recipient's incoming call policy excludes the caller. That policy is the `incoming_call_flags` bitfield of the recipient's [user settings](/http-api/users/settings/). Fluxer checks the policy in this order. The nobody flag rejects every caller. The friends-only flag admits only a friend. Under any other policy a friend is always admitted. A caller who shares a friend with the recipient is admitted when the friends-of-friends flag is set. A caller who shares a guild with the recipient is admitted when the guild-members flag is set. The everyone flag admits every remaining caller. Any other caller is rejected. The silent-everyone flag reports `silent` as true for a caller admitted by the mutual friend, mutual guild, or everyone branch.
|
||||
|
||||
For a recipient who has never stored settings, `ringable` is true and `silent` is false. A direct message that has lost its other recipient reports the same pair. In a group direct message `ringable` is true unless the caller is already connected to its call, and `silent` is always false.
|
||||
|
||||
@@ -157,7 +157,7 @@ Starts a direct message or group direct message call, or adds ringing recipients
|
||||
|
||||
- The caller must satisfy the [access rules](#access-rules).
|
||||
- Every explicitly named recipient must be a current recipient other than the caller.
|
||||
- A direct message also requires the caller to satisfy the direct message send policy against the other recipient.
|
||||
- A direct message also requires that the caller is allowed to send the other recipient a direct message.
|
||||
|
||||
### Path parameters
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ An absent field is one this channel type does not own. A field present with `nul
|
||||
A category owns no `parent_id`. A client that reads the absent key as `null` sees a channel whose parent was cleared.
|
||||
|
||||
:::caution[Age and warning fields report this channel alone]
|
||||
The API enforces the resolved state, so a client must apply the inherited category state before it shows an age restriction or a content warning.
|
||||
The API enforces the value resolved through this channel, then its parent category, then the guild, as described below. A client must resolve the value in the same order before it shows an age restriction or a content warning.
|
||||
:::
|
||||
|
||||
An age restriction and a content warning both resolve through this channel first, then the parent category, and finally the guild. A category has no parent category and resolves through itself and then the guild.
|
||||
@@ -221,7 +221,7 @@ Returns the [channel object](#channel-object) visible to the authenticated user.
|
||||
- A private channel requires current recipient access.
|
||||
- The personal notes channel requires ownership.
|
||||
|
||||
Fluxer enforces age verification only for a guild text, voice, or link channel. A guild category is never gated on it, even when it has the override its children inherit.
|
||||
Fluxer enforces age verification only for a guild text, voice, or link channel. Fluxer never requires age verification for a guild category, even when the category has the override its children inherit.
|
||||
|
||||
### Path parameters
|
||||
|
||||
@@ -453,9 +453,9 @@ Fluxer trims a stored nickname, and a value that is null or empty after trimming
|
||||
|
||||
A guild channel change emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to its guild. Replacing a category's permission overwrites also replaces them on each child whose overwrites still exactly match the category's previous values, with one Dispatch per changed child. A child whose overwrites had differed is left unchanged.
|
||||
|
||||
Changing `rate_limit_per_user` clears the channel's current slowmode state so the new interval applies immediately. Changing `rtc_region` on a guild voice channel moves its active voice connections to the selected region.
|
||||
Changing `rate_limit_per_user` clears the remaining slowmode delay of every user in the channel, so each user's next message is checked against the new interval. Changing `rtc_region` on a guild voice channel moves its active voice connections to the selected region.
|
||||
|
||||
A request that changes at least one field creates a channel update audit log entry with no reason. Supplying `permission_overwrites` also creates the overwrite audit log entries the change implies.
|
||||
A request that changes at least one field creates a channel update audit log entry with no reason. Supplying `permission_overwrites` also creates one overwrite create, update, or delete audit log entry for each overwrite the array adds, changes, or removes.
|
||||
|
||||
A group direct message change emits [Channel Update](/gateway/events/#channel-update) to every current recipient. A name or successful icon change creates a system message delivered with [Message Create](/gateway/events/#message-create), and replacing the icon permanently deletes the previous one. Ownership and nickname changes create no system message and no audit entry.
|
||||
|
||||
@@ -542,7 +542,7 @@ An account that stores neither a password nor a second factor is verified withou
|
||||
|
||||
Deleting a guild category first clears the `parent_id` of every child channel and emits [Channel Update](/gateway/events/#channel-update) for each one. The category itself is then deleted like any other guild channel.
|
||||
|
||||
Deleting a guild channel permanently removes its messages, attachments, invites and webhooks. Guild subscribers receive [Channel Delete](/gateway/events/#channel-delete), and the deletion appears in the audit log. A system, rules or AFK channel reference is cleared with [Guild Update](/gateway/events/#guild-update).
|
||||
Deleting a guild channel permanently removes its messages, attachments, invites and webhooks. Guild subscribers receive [Channel Delete](/gateway/events/#channel-delete), and the deletion appears in the audit log. When the deleted channel is the guild's system, rules or AFK channel, Fluxer clears that guild setting and emits [Guild Update](/gateway/events/#guild-update).
|
||||
|
||||
Closing a direct message marks the channel closed for the caller alone and emits [Channel Delete](/gateway/events/#channel-delete) to that caller. The channel, its messages, and the other recipient's view are untouched.
|
||||
|
||||
@@ -600,7 +600,7 @@ Fluxer evaluates the target's admission policy in this order.
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | Recipient was added, or was already a recipient |
|
||||
| 400 | [error response](/http-api/#error-response) | CAPTCHA proof is missing or rejected, the caller and target are not friends and the request returns `NOT_FRIENDS_WITH_USER` |
|
||||
| 400 | [error response](/http-api/#error-response) | CAPTCHA proof is missing or rejected, or the caller and target are not friends and the request returns `NOT_FRIENDS_WITH_USER` |
|
||||
| 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` |
|
||||
@@ -735,7 +735,7 @@ A larger decimal string returns 400 `INVALID_FORM_BODY` with the code `INTEGER_O
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to the guild, including when the submitted overwrite exactly matches the stored one. When the channel is a category, every child whose overwrites exactly matched the category's previous values receives the same replacement and its own Channel Update.
|
||||
Fluxer emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to the guild, including when the submitted overwrite exactly matches the stored one. When the channel is a category, every child whose overwrites exactly matched the category's previous values receives a copy of the category's new overwrites and its own Channel Update.
|
||||
|
||||
The operation records an overwrite create or update audit entry with no reason. An unchanged overwrite records no audit entry.
|
||||
|
||||
@@ -774,7 +774,7 @@ The operation is idempotent, does not require `VIEW_CHANNEL`, and never returns
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to the guild, including when the identifier names no existing overwrite. Removing an overwrite that exists also records an overwrite delete audit entry with the previous overwrite state and no reason. An identifier that names no existing overwrite records no audit entry. When the target channel is a category, propagation follows the same exact-match rule as overwrite replacement.
|
||||
Fluxer emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to the guild, including when the identifier names no existing overwrite. Removing an overwrite that exists also records an overwrite delete audit entry with the previous overwrite state and no reason. An identifier that names no existing overwrite records no audit entry. When the target channel is a category, each child whose overwrites exactly matched the category's previous overwrites receives a copy of the category's new overwrites and its own Channel Update.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -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 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). Accounts with an outstanding [required action](/http-api/users/#required-actions) receive 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`. Connection IDs are not snowflakes.
|
||||
|
||||
[Bluesky client metadata](#get-bluesky-client-metadata) and [JWKS](#get-bluesky-jwks) are public and contain no account data.
|
||||
|
||||
@@ -37,7 +37,7 @@ The pair of [type](#connection-types) and `id` identifies a connection. Both val
|
||||
|
||||
<sup>2</sup> For a `domain` connection the name is the submitted domain. For a `bsky` connection it is the Bluesky handle, refreshed by completing the [authorisation flow](#start-bluesky-authorisation)
|
||||
|
||||
<sup>3</sup> Starts true. A failed ownership recheck sets it to false
|
||||
<sup>3</sup> Fluxer sets it to true when the proof succeeds and again each time a Bluesky authorisation completes. No later check sets it to false
|
||||
|
||||
<sup>4</sup> The stored value is the integer the client supplied and is not masked against the defined bits
|
||||
|
||||
@@ -116,7 +116,7 @@ Returned by [Start Bluesky authorisation](#start-bluesky-authorisation).
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| authorize_url | string | URL to open for authorisation. Its origin depends on the account |
|
||||
| authorize_url | string | URL to open for authorisation. Its origin depends on the submitted `handle` |
|
||||
|
||||
## List connections
|
||||
|
||||
@@ -317,7 +317,7 @@ Assigns the display order of the listed connections from their position in the a
|
||||
|
||||
Each named connection receives its zero-based array index as `sort_order`. The complete list is sent to the caller's sessions in [User Connections Update](/gateway/events/#user-connections-update), even if the order did not change.
|
||||
|
||||
The reorder is atomic. A rejected write changes no order and emits no event. A later notification failure can return an error after the order has been saved.
|
||||
The reorder is atomic. A request that returns 409 `CONFLICT` changes no order and emits no event. When Fluxer saves the order and then fails to send [User Connections Update](/gateway/events/#user-connections-update), the request returns an error and the new order stays saved.
|
||||
|
||||
A partial array can leave two connections sharing a `sort_order`, which [List connections](#list-connections) resolves by stored order.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Deployment availability
|
||||
description: The hosted-only routes and the instance flags that report deployment kind.
|
||||
---
|
||||
|
||||
A small set of routes exists only on the deployment Fluxer hosts. An operator runs the same release, and most of the HTTP API is identical on both.
|
||||
The routes listed under [Hosted-only routes](#hosted-only-routes) exist only on the deployment Fluxer hosts. A self-hosted deployment runs the same release, and most of the HTTP API is identical on both.
|
||||
|
||||
A self-hosted deployment does not register those routes. A request to one returns 404 `NOT_FOUND` with no feature-specific code, so a caller cannot tell an unavailable route from an unrecognised path.
|
||||
|
||||
@@ -14,13 +14,13 @@ Credentials, permissions, premium state and OAuth2 scopes do not change route av
|
||||
|
||||
Every deployment reports its kind in `self_hosted` on the [instance features object](/http-api/instance/#instance-features-object). The unauthenticated [instance discovery document](/http-api/instance/#get-instance-discovery) publishes it before a client holds any credential. `self_hosted` alone decides whether the API registers the routes below.
|
||||
|
||||
`stripe_enabled` on the same object reports the payment provider toggle alone. A hosted deployment that reports it false still serves every route in the table below. A deployment reporting `stripe_enabled` true with no provider secret key configured behaves exactly like one reporting it false.
|
||||
`stripe_enabled` on the same object reports only the `integrations.stripe.enabled` configuration value. A hosted deployment that reports it false still serves every route in the table below. A deployment reporting `stripe_enabled` true with no provider secret key configured behaves exactly like one reporting it false.
|
||||
|
||||
:::caution[Read `self_hosted` for the deployment kind]
|
||||
Neither flag promises that a provider-dependent operation succeeds.
|
||||
:::
|
||||
|
||||
Without a provider client, the answer depends on the operation. An operation that has to reach the provider fails with 400 `STRIPE_PAYMENT_NOT_AVAILABLE`, and [Receive Stripe webhook](/http-api/billing/#receive-stripe-webhook) fails with 400 `STRIPE_WEBHOOK_NOT_AVAILABLE`. These read operations report the absence in a 200 body instead:
|
||||
When `stripe_enabled` is false or no provider secret key is configured, the answer depends on the operation. An operation that has to reach the provider fails with 400 `STRIPE_PAYMENT_NOT_AVAILABLE`, and [Receive Stripe webhook](/http-api/billing/#receive-stripe-webhook) fails with 400 `STRIPE_WEBHOOK_NOT_AVAILABLE`. These read operations report the absence in a 200 body instead:
|
||||
|
||||
- [Get refund eligibility](/http-api/billing/#get-refund-eligibility) reports `eligible` false with the reason `feature_unavailable`.
|
||||
- [Get current subscription price](/http-api/premium/#get-current-subscription-price) reports null.
|
||||
@@ -68,4 +68,4 @@ A route every deployment registers can still produce a different answer on a sel
|
||||
|
||||
Premium state is the clearest case. Every deployment registers [Get premium state](/http-api/premium/#get-premium-state) and [Set premium perks disabled](/http-api/premium/#set-premium-perks-disabled). A self-hosted instance still reports premium state and still records the perks-disabled flag. The response repeats the deployment kind in `self_hosted` on the [effective premium state object](/http-api/premium/#effective-premium-state-object). That flag alone does not make `is_premium` true. A self-hosted deployment grants premium to every account only while its instance [premium mode](/admin-api/instance/#premium-modes) is `everyone`. That mode also overrides the perks-disabled flag, so `is_premium` stays true while `premium_perks_disabled` is true.
|
||||
|
||||
The other instance feature flags published by [instance discovery](/http-api/instance/#instance-features-object) work the same way. `voice_enabled`, `presigned_attachment_uploads`, and `emails_enabled` each switch off a capability that the surrounding routes still expose, so a client reads the flag.
|
||||
The other instance feature flags published by [instance discovery](/http-api/instance/#instance-features-object) work the same way. `voice_enabled`, `presigned_attachment_uploads`, and `emails_enabled` each report whether a capability is switched on. The routes for that capability stay registered when the flag is false, so a client reads the flag before it uses them.
|
||||
@@ -82,7 +82,7 @@ One page of matching listings, together with the total and the per-category coun
|
||||
| total<sup>1</sup> | integer | The total number of guilds matching the query |
|
||||
| category_counts<sup>2</sup> | array[[discovery category count](#discovery-category-count-object) object] | The match count for each category under the current filters |
|
||||
|
||||
<sup>1</sup> The count describes the complete match set, so it bounds paging through `offset`
|
||||
<sup>1</sup> The count covers every match on every page. A client pages with `offset` until `offset` reaches `total`
|
||||
|
||||
<sup>2</sup> Computed with the `category` filter removed and every other filter applied, so the counts describe what selecting a different category would return. A category with no match is omitted, and the array is ordered by ascending category
|
||||
|
||||
@@ -358,7 +358,7 @@ A pending, rejected, removed, or absent application fails with 400 `DISCOVERY_NO
|
||||
| 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` |
|
||||
|
||||
<sup>1</sup> An account whose phone requirement was deferred is re-evaluated against the target guild at join time, so an account that satisfies the standing check can still be refused here
|
||||
<sup>1</sup> An account without a verified phone number can have a phone requirement that Fluxer holds back until the account joins a guild. At join time Fluxer checks that requirement against the target guild. An account that passes the account-wide check can therefore still receive `ACCOUNT_SUSPICIOUS_ACTIVITY` here
|
||||
|
||||
:::note[A guild that never existed returns 400, not 404]
|
||||
A guild with no approved listing returns `DISCOVERY_NOT_DISCOVERABLE`.
|
||||
@@ -368,9 +368,9 @@ A guild with no approved listing returns `DISCOVERY_NOT_DISCOVERABLE`.
|
||||
|
||||
An account that is already a member receives the same 204 response with no Dispatch. Otherwise the operation creates the membership, records discovery as its join source, and adds the guild to the caller's settings and folder layout.
|
||||
|
||||
The joining account's sessions receive [Guild Create](/gateway/events/#guild-create). [Guild Member Add](/gateway/events/#guild-member-add) is dispatched guild-wide and reaches whichever sessions [event filtering](/gateway/event-filtering/) selects. A bot session always receives it, and a passive user session in a guild with more than 250 members does not receive it until [Lazy Request](/gateway/commands/#lazy-request) marks that guild active. The joining account receives [User Settings Update](/gateway/events/#user-settings-update) when the join changes its restricted guild set or its folder layout, and [User Guild Settings Update](/gateway/events/#user-guild-settings-update) when its account default hides muted channels.
|
||||
The joining account's sessions receive [Guild Create](/gateway/events/#guild-create). [Guild Member Add](/gateway/events/#guild-member-add) is dispatched guild-wide and reaches whichever sessions [event filtering](/gateway/event-filtering/) selects. A bot session always receives it, and a passive user session in a guild with more than 250 members does not receive it until [Lazy Request](/gateway/commands/#lazy-request) marks that guild active. The joining account receives [User Settings Update](/gateway/events/#user-settings-update) when the join adds the guild to `restricted_guilds` in its [user settings](/http-api/users/settings/) or changes its folder layout, and [User Guild Settings Update](/gateway/events/#user-guild-settings-update) when its account default hides muted channels.
|
||||
|
||||
Unless join notifications are suppressed or no system channel is configured, the join emits a [USER_JOIN](/http-api/messages/#message-types) system message through [Message Create](/gateway/events/#message-create). No invite use is consumed.
|
||||
Unless the guild sets the `SUPPRESS_JOIN_NOTIFICATIONS` [system channel flag](/http-api/guilds/#system-channel-flags) or has no system channel, the join emits a [USER_JOIN](/http-api/messages/#message-types) system message through [Message Create](/gateway/events/#message-create). No invite use is consumed.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -483,7 +483,7 @@ Every field is optional, and an omitted field preserves the stored value.
|
||||
|
||||
### Side effects
|
||||
|
||||
Editing preserves the listing's status and review details. Approved changes appear in [Search discovery guilds](#search-discovery-guilds), while pending listings remain absent. No guild feature changes.
|
||||
Editing preserves the listing's status and review details. Changes to an approved listing appear in [Search discovery guilds](#search-discovery-guilds). A pending listing stays absent from search. No guild feature changes.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A donation is a one-off or recurring payment, taken on an externally hosted checkout page. Fluxer tracks it by email address, and it grants no [premium](/http-api/premium/). [Billing](/http-api/billing/) defines the payment provider webhook that completes one.
|
||||
|
||||
None of the routes here takes a credential, and all are hosted-only, as [deployment availability](/http-api/deployment-availability/) describes. A valid credential presented anyway keys the rate limit to the account.
|
||||
None of the routes here takes a credential, and all are hosted-only, as [deployment availability](/http-api/deployment-availability/) describes. When a request presents a valid credential anyway, Fluxer counts the request against that account's rate limit.
|
||||
|
||||
Fluxer uses a submitted address exactly as written and matches a donor by exact equality, so `[email protected]` and `[email protected]` address two different donors.
|
||||
|
||||
:::note[Donation management requires a completed donation]
|
||||
Donation management becomes available after payment is confirmed.
|
||||
Fluxer stores a donor only when the payment provider confirms a donation checkout. [Request donation management link](#request-donation-management-link) sends a link only to an address stored as a donor.
|
||||
:::
|
||||
|
||||
## Donation currencies
|
||||
@@ -96,7 +96,7 @@ An address that resolves to no donor receives no email.
|
||||
|
||||
### Side effects
|
||||
|
||||
An eligible donor receives an email. The 204 response does not guarantee delivery.
|
||||
An address that resolves to a donor receives an email with the management link. The 204 response does not guarantee delivery.
|
||||
|
||||
Fluxer creates a single-use token of 64 lowercase hexadecimal characters, valid for 15 minutes, only when the address resolves to a donor. Issuing a new link deletes every earlier token for the same address, and a replaced link returns 400 `DONATION_MAGIC_LINK_INVALID`.
|
||||
|
||||
@@ -176,7 +176,7 @@ A recurring donation for an address with an active recurring donation returns th
|
||||
|
||||
Checkout opens on the provider's pages for the submitted amount, currency and interval. Completion returns the donor to the public donation success page, while cancellation returns to the public donation page.
|
||||
|
||||
Payment confirmation sends an email and enables [Request donation management link](#request-donation-management-link).
|
||||
When the payment provider confirms the payment, Fluxer stores the address as a donor and sends a donation confirmation email to it. From then on, [Request donation management link](#request-donation-management-link) sends a link to that address.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ The download resource serves the desktop application builds a deployment has sto
|
||||
|
||||
Fluxer registers the routes under `/dl`, and the desktop routes under `/dl/desktop`. A client builds the URL against `api_client` from the [instance endpoints object](/http-api/instance/#instance-endpoints-object).
|
||||
|
||||
The prefix mounts at the root and at `/v1`, so `/v1/dl/desktop/...` resolves for the routes that name their own segments.
|
||||
Fluxer mounts the prefix at the root and at `/v1`. Every desktop route below also answers under `/v1/dl/desktop/...`.
|
||||
|
||||
:::caution[The catch-all has no `/v1` form]
|
||||
[Download stored object](#download-stored-object) requires the `/dl` prefix. Its `/v1/dl/...` equivalent returns 404.
|
||||
@@ -173,19 +173,19 @@ A release feed filename is any of these:
|
||||
|
||||
The deployment settings below answer a download with 302.
|
||||
|
||||
A hosted deployment can redirect downloads from selected countries to GitHub release assets. Responses under this setting have `Cache-Control: private, no-store`.
|
||||
A hosted deployment can list countries whose downloads of a file under `desktop/` Fluxer can redirect to GitHub release assets. Once the list is set, every response for such a file has `Cache-Control: private, no-store`, including a response Fluxer serves from storage.
|
||||
|
||||
A deployment that issues presigned download URLs answers a `GET` with 302 to a storage URL valid for 900 seconds. That redirect has `Cache-Control: no-store` and `Accept-Ranges: bytes`. The setting is off by default.
|
||||
|
||||
:::caution[A 302 has no bytes and no range]
|
||||
The client follows `Location` to read the file. Fluxer produces the redirect before it reads any `Range` header, so range handling on a redirected download belongs to the target.
|
||||
The client follows `Location` to read the file. Fluxer produces the redirect before it reads any `Range` header, so the server at `Location` answers any `Range` header on a redirected download.
|
||||
:::
|
||||
|
||||
## Test builds
|
||||
|
||||
Every route accepts the `test` query parameter. `1` or `true`, matched without regard to case, selects test releases. Any other value is false.
|
||||
|
||||
On the desktop routes the flag replaces the prefix outright. On [Download stored object](#download-stored-object) it rewrites a key beginning `desktop/` and leaves any other key unchanged, and a path that already names `desktop-test/` resolves there with no flag at all.
|
||||
On the desktop routes the flag makes Fluxer resolve every file under `desktop-test/`. On [Download stored object](#download-stored-object) it rewrites a key beginning `desktop/` and leaves any other key unchanged, and a path that already names `desktop-test/` resolves there with no flag at all.
|
||||
|
||||
A `url` and a `checksum_url` built for a request that sent the flag repeat `?test=1`, so a client following either one stays on the test prefix.
|
||||
|
||||
@@ -312,7 +312,7 @@ Streams the newest file at the coordinate for one package format.
|
||||
| 404 | `Not Found` | No file resolved for the format at the coordinate |
|
||||
| 416 | empty | The requested range is unsatisfiable |
|
||||
|
||||
The stored file keeps its own name, so the resolved filename has the release version even though the request named `latest`. A client that follows this route on every check reads a different file once the coordinate publishes a new release.
|
||||
The stored file keeps its own name, so the resolved filename has the release version even though the request named `latest`. A client that follows this route on every check reads a different file once a newer release is stored at the coordinate.
|
||||
|
||||
### Response headers
|
||||
|
||||
@@ -493,7 +493,7 @@ A path outside `desktop/` or `desktop-test/` returns 404.
|
||||
|
||||
Fluxer also rejects the key when it is empty, when normalisation leaves it beginning `..` or `/`, or when any segment is `.`, `..`, or contains a NUL character. Each of those returns the same 404, so a caller cannot tell a traversal attempt from a missing object.
|
||||
|
||||
Both `desktop/stable/linux-x64/manifest.json` and `desktop/stable/linux/x64/manifest.json` path forms are supported.
|
||||
A request can name `desktop/stable/linux-x64/manifest.json` or `desktop/stable/linux/x64/manifest.json`. For the hyphen form, Fluxer returns that object when storage holds it, and otherwise returns the object at the slash form.
|
||||
|
||||
### Response headers
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ Every bound below is a fixed constant of the instance, and none is resolved from
|
||||
|
||||
<sup>2</sup> Only [Upload entrance sound](#upload-entrance-sound) applies the lower bound
|
||||
|
||||
The [instance discovery](/http-api/instance/#limit-keys) document publishes a `feature_voice_entrance_sounds` limit key. That key gates the feature in the client. No route on this page checks it, so a library can be read, written, and played back regardless of its resolved value.
|
||||
The [instance discovery](/http-api/instance/#limit-keys) document publishes a `feature_voice_entrance_sounds` limit key. The Fluxer client reads that key to decide whether it opens the clip upload dialog and whether it calls [Play entrance sound](#play-entrance-sound) after it connects to a voice channel. No route on this page checks it, so a library can be read, written, and played back regardless of its resolved value.
|
||||
|
||||
## Supported containers
|
||||
|
||||
@@ -316,7 +316,7 @@ Fluxer writes or removes the scope's selection. One clip can be selected in any
|
||||
|
||||
<RouteHeader method="POST" path="/v1/voice/channels/{channel_id}/entrance-sound" />
|
||||
|
||||
Fans the caller's chosen clip out to everyone else connected to a voice channel and returns 204 with an empty body. The caller must already hold a voice state in that channel and must own the clip. Emits an [ENTRANCE_SOUND_PLAY](/gateway/events/#entrance-sound-play) Gateway event.
|
||||
Tells every other account connected to a voice channel to play the caller's chosen clip and returns 204 with an empty body. The caller must already hold a voice state in that channel and must own the clip. Emits an [ENTRANCE_SOUND_PLAY](/gateway/events/#entrance-sound-play) Gateway event.
|
||||
|
||||
Recipients fetch and play the clip locally from the URL in the event.
|
||||
|
||||
@@ -336,7 +336,7 @@ Recipients fetch and play the clip locally from the URL in the event.
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | Fan-out was attempted for every other connected account |
|
||||
| 204 | empty | Fluxer attempted a Dispatch to every other connected account |
|
||||
| 400 | [error response](/http-api/#error-response) | Caller holds no voice state in the channel, including when the channel does not exist, and the request returns `ENTRANCE_SOUND_INVALID_SCOPE` at the `channel_id` path, or the account owns no such clip and the request returns `ENTRANCE_SOUND_NOT_FOUND` at the `sound_id` path |
|
||||
|
||||
:::caution[Connection is the only authorisation]
|
||||
|
||||
@@ -68,7 +68,7 @@ A validation failure whose elements have enumerated codes answers 400 with its e
|
||||
|
||||
### Default schema failure codes
|
||||
|
||||
A boundary schema constraint can name its own [validation code](#validation-error-code-registry). When it names none, Fluxer maps the failure to one of the codes below by the kind of constraint that failed.
|
||||
A constraint in a route's request schema can name its own [validation code](#validation-error-code-registry). When it names none, Fluxer maps the failure to one of the codes below by the kind of constraint that failed.
|
||||
|
||||
| Constraint | Code | Description |
|
||||
| --- | --- | --- |
|
||||
@@ -110,7 +110,7 @@ Fluxer answers an unrecognised failure with 500 `INTERNAL_SERVER_ERROR` and a ge
|
||||
|
||||
## Client errors as an abuse signal
|
||||
|
||||
Repeated invalid requests or credentials can trigger a temporary IP ban. A `4xx` answer to a request with no authenticated user adds to that signal, weighted by status. A 429 weighs 3, a 401 weighs 0.75, a 403 weighs 0.5, and every other 4xx weighs 0.25. One request adds at most one signal, and a request from a private or exempt address adds none. Stop using a rejected credential and respect rate-limit responses instead of retrying unchanged requests.
|
||||
Repeated invalid requests or credentials can trigger a temporary IP ban. A `4xx` answer to a request with no authenticated user adds to that signal, weighted by status. A 429 weighs 3, a 401 weighs 0.75, a 403 weighs 0.5, and every other 4xx weighs 0.25. One request adds at most one signal, and a request from a private or exempt address adds none. Stop using a rejected credential. Change a rejected request before sending it again, and after a 429 wait `retry_after` before the next attempt.
|
||||
|
||||
:::caution[An automatic ban answers every request for 24 hours]
|
||||
A temporary ban lasts 24 hours by default. Requests from the banned address return 403 `GLOBAL_IP_TEMPORARILY_BANNED`. Use `expires_at` from the response when available.
|
||||
|
||||
@@ -6,7 +6,7 @@ description: The experiment assignments envelope, the revalidation and polling c
|
||||
|
||||
import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
An experiment is one instance-wide rollout the operator configures and Fluxer resolves against one account. The single route on this page resolves every experiment the server defines and returns them in one envelope, together with the polling cadence they share. `voice_noise_suppression` is the only experiment defined today, and [Voice](/voice/) defines the placement protocol its assignment applies to.
|
||||
An experiment is an instance-wide rollout that an operator configures. For each account, Fluxer works out from that configuration whether the account is in the rollout and which settings the account receives. The single route on this page resolves every experiment the server defines and returns them in one envelope, together with the polling cadence they share. `voice_noise_suppression` is the only experiment defined today, and [Voice](/voice/) defines the placement protocol its assignment applies to.
|
||||
|
||||
Every assignment is advice. A client that ignores one behaves as it does with the rollout off, and no route and no Gateway event reports what a client actually ran.
|
||||
|
||||
@@ -19,10 +19,10 @@ One resolution of every defined experiment against one account. Every field is p
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| poll_interval_seconds | integer | Seconds to wait before revalidating, from 60 through 86400 |
|
||||
| poll_jitter_percent | integer | How far to spread the wait around the interval, from 0 through 50 |
|
||||
| poll_jitter_percent | integer | The largest random offset added to or subtracted from the wait, as a percentage of the wait, from 0 through 50 |
|
||||
| assignments | [assignment map](#assignment-map-object) object | One entry for each experiment the server defines |
|
||||
|
||||
The polling fields apply to every experiment and account on the instance.
|
||||
Every account on the instance receives the same `poll_interval_seconds` and `poll_jitter_percent`. One request refreshes every experiment.
|
||||
|
||||
## Assignment map object
|
||||
|
||||
@@ -36,7 +36,7 @@ One entry per experiment. The envelope reports this object even when it is empty
|
||||
|
||||
Ignore unknown experiments and treat a missing experiment as off.
|
||||
|
||||
This server version writes `voice_noise_suppression` on every response, including while the rollout is disabled. The disabled value is the first [resolution outcome](#resolution-outcomes) below, which reports `enabled` false and the stored `config_version`, so a client can tell an operator write from a no-op without a second request.
|
||||
This server version writes `voice_noise_suppression` on every response, including while the rollout is disabled. The disabled value is the first [resolution outcome](#resolution-outcomes) below, which reports `enabled` false and the stored `config_version`, so a client that compares `config_version` with the value from its previous response can see that an operator saved the noise suppression configuration, even while the rollout stays disabled, and needs no second request for it.
|
||||
|
||||
## Noise suppression backends
|
||||
|
||||
@@ -91,7 +91,7 @@ A client branches on `user_targeted` rather than on `enabled_backends`, because
|
||||
|
||||
## Noise suppression guild override object
|
||||
|
||||
One backend replacement scoped to one guild. A guild named here replaces `backend` while the caller is connected to a voice channel of that guild.
|
||||
One backend replacement scoped to one guild. While the caller is connected to a voice channel of that guild, the override's `backend` replaces the assignment's `backend`.
|
||||
|
||||
### Structure
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Every field is always present.
|
||||
| shards | integer | Recommended shard count, always `1` |
|
||||
| session_start_limit | [session start limit](#session-start-limit-object) object | Fixed session start values |
|
||||
|
||||
Fluxer appends no query string, so a client appends the [connection parameters](/gateway/overview/#connection-parameters) itself. A client MUST NOT upgrade a published `ws` value, because a deliberately plain HTTP deployment advertises one.
|
||||
Fluxer appends no query string, so a client appends the [connection parameters](/gateway/overview/#connection-parameters) itself. A client MUST NOT rewrite a published `ws` value to `wss`, because a deployment that deliberately serves plain HTTP advertises a `ws` value.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -56,7 +56,7 @@ These are fixed compatibility values, not live usage counters.
|
||||
|
||||
<sup>1</sup> A bot does not need to pace Identify requests against this value
|
||||
|
||||
The limits the Gateway enforces are in [Session lifecycle](/gateway/limits-and-rate-limits/#session-lifecycle). Fluxer budgets Identify per source address and caps a user account at a fixed number of concurrent sessions. Neither bound is reported here.
|
||||
The limits the Gateway enforces are in [Session lifecycle](/gateway/limits-and-rate-limits/#session-lifecycle). Fluxer rate limits Identify per source address and limits a user account to a fixed number of concurrent sessions. Neither bound is reported here.
|
||||
|
||||
:::caution[A remaining session start does not guarantee admission]
|
||||
These values reserve no capacity. [Session admission](/gateway/limits-and-rate-limits/#session-lifecycle) can still hold or reject a connection.
|
||||
@@ -76,7 +76,7 @@ The route checks the shape of the credential only. A well-formed value naming no
|
||||
|
||||
Validate a token with [Identify](/gateway/commands/#identify) or [Get bot application](/http-api/applications/#get-bot-application) instead.
|
||||
|
||||
A 200 has the informational [rate limit headers](/topics/rate-limits/#rate-limit-headers) only for the `Bot ` prefix. Fluxer keys the bare form and the `Bearer ` form on the client IP address.
|
||||
A 200 has the informational [rate limit headers](/topics/rate-limits/#rate-limit-headers) only for the `Bot ` prefix. For the bare form and the `Bearer ` form, Fluxer keys the rate limit bucket on the client IP address.
|
||||
|
||||
### Response
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Every route requires a session credential. A bot token and an OAuth2 bearer cred
|
||||
|
||||
## Provider availability
|
||||
|
||||
`klipy` is the only provider name a deployment binds. [Get instance discovery](/http-api/instance/#get-instance-discovery) publishes it as the [GIF provider](/http-api/instance/#gif-provider-object) object. An instance that has bound no provider key refuses every route on this page with 403 `FEATURE_TEMPORARILY_DISABLED`. That same key drives `gif_enabled` in [service availability](/http-api/instance/#service-availability-object), a flag an operator can also set by hand, so a client that reads `gif_enabled` as true can still receive the 403.
|
||||
`klipy` is the only provider name a deployment binds. [Get instance discovery](/http-api/instance/#get-instance-discovery) publishes it as the [GIF provider](/http-api/instance/#gif-provider-object) object. An instance that has bound no provider key refuses every route on this page with 403 `FEATURE_TEMPORARILY_DISABLED`. `gif_enabled` in [service availability](/http-api/instance/#service-availability-object) reports whether that key is bound, unless an operator has set the flag by hand. A client that reads `gif_enabled` as true can therefore still receive the 403.
|
||||
|
||||
A provider outage, a request past its deadline, and an unreadable provider payload all return 503 `SERVICE_UNAVAILABLE`. The deadline is 12 seconds everywhere except [Register a GIF share](#register-a-gif-share), which uses 3 seconds. An operator can change both.
|
||||
|
||||
@@ -36,7 +36,7 @@ Every response under `/gifs`, `/tenor`, and `/klipy` has these headers, includin
|
||||
|
||||
## Result freshness
|
||||
|
||||
Results may be cached and can lag behind the provider. Repeating a request does not force a refresh.
|
||||
Fluxer caches results, so a result can lag behind the provider. Repeating a request does not force a refresh.
|
||||
|
||||
## GIF object
|
||||
|
||||
@@ -62,7 +62,7 @@ A GIF object is one media item the active provider owns. Every URL in it resolve
|
||||
|
||||
<sup>2</sup> Copied from the `webm` entry of `media` when the provider returned one, and from the first entry it returned otherwise
|
||||
|
||||
<sup>3</sup> Empty when no format the provider returned could be proxied. No key is guaranteed, so a client walks a priority list
|
||||
<sup>3</sup> Empty when no format the provider returned could be proxied. No key is guaranteed, so a client checks the format names it prefers in order and uses the first one present
|
||||
|
||||
<sup>4</sup> The active provider emits none, so the field is absent from every GIF these routes return. [Resolve GIF URLs](/http-api/memes/#resolve-gif-urls) is the operation that produces one
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ A gift records no recipient, so Fluxer binds the code to whichever eligible acco
|
||||
|
||||
## Gift object
|
||||
|
||||
A gift records a duration. Fluxer computes the entitlement window at redemption time from the redeemer's existing state.
|
||||
A gift records a duration. Fluxer computes the entitlement window at redemption time. For a positive quantity, the entitlement anchor is the latest of the current time, the redeemer's current premium end and their existing gift extension end.
|
||||
|
||||
Both creation paths record a creator. A completed gift checkout records the purchaser. An Admin API gift records the system account with ID `0`, and no field names the administrator that requested it. No operation unredeems a code.
|
||||
|
||||
@@ -134,7 +134,7 @@ One redemption can be in flight for a code across the whole deployment, and a se
|
||||
| X-Captcha-Token?<sup>1</sup> | string | The proof issued by the CAPTCHA provider |
|
||||
| X-Captcha-Type?<sup>2</sup> | string | The CAPTCHA provider, either `hcaptcha` or `turnstile` |
|
||||
|
||||
<sup>1</sup> A missing proof returns 400 `CAPTCHA_REQUIRED` and a rejected proof returns 400 `INVALID_CAPTCHA`. Verification is skipped when CAPTCHA is disabled, when the account has the exemption flag, or when the caller's contact has the exemption capability, as described by [CAPTCHA handling](/topics/captcha/)
|
||||
<sup>1</sup> A missing proof returns 400 `CAPTCHA_REQUIRED` and a rejected proof returns 400 `INVALID_CAPTCHA`. Verification is skipped when CAPTCHA is disabled, when the account has the exemption flag, or when the account's email address has the `captcha_exempt` account policy capability, as described by [CAPTCHA handling](/topics/captcha/)
|
||||
|
||||
<sup>2</sup> Any other value, including an omitted header, falls back to the instance's configured provider
|
||||
|
||||
@@ -143,7 +143,7 @@ One redemption can be in flight for a code across the whole deployment, and a se
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 204 | empty | The gift was redeemed and the entitlement was applied |
|
||||
| 400 | [error response](/http-api/#error-response) | CAPTCHA failed, the code is redeemed or a redemption is in flight, the account is unclaimed or holds lifetime entitlement, the payment provider rejected the subscription work, or the Visionary guild join for a lifetime gift failed |
|
||||
| 400 | [error response](/http-api/#error-response) | CAPTCHA failed, the code is redeemed or a redemption is in flight, the account is unclaimed or holds lifetime entitlement, the payment provider rejected the change to the active subscription, or the Visionary guild join for a lifetime gift failed |
|
||||
| 403 | [error response](/http-api/#error-response) | The email address is unverified, or purchases are disabled for the account |
|
||||
| 404 | [error response](/http-api/#error-response) | No gift exists for the code, the gift was revoked, the authenticated account record no longer exists, or the configured Visionary guild does not exist |
|
||||
|
||||
@@ -151,9 +151,9 @@ One redemption can be in flight for a code across the whole deployment, and a se
|
||||
|
||||
The gift becomes redeemed, so [Get gift](#get-gift) reports `redeemed` as true and [List current user gifts](/http-api/users/gifts/#list-current-user-gifts) shows the redemption time and the redeemer to the buyer.
|
||||
|
||||
A positive quantity extends premium from the latest of the current time, the current premium end and the existing gift extension end. An eligible active subscription also has its trial or billing period extended without proration. A provider failure can return 400 `STRIPE_ERROR` and leave the code unredeemed.
|
||||
A positive quantity extends premium from the latest of the current time, the current premium end and the existing gift extension end. When the account has a subscription with the payment provider and its subscription premium has not ended, Fluxer also extends that subscription's trial or billing period without proration. A provider failure can return 400 `STRIPE_ERROR` and leave the code unredeemed.
|
||||
|
||||
A quantity of `0` grants lifetime Visionary premium and immediately cancels an active subscription without proration or a final invoice. It can also assign a Visionary sequence and join the [Visionary guild](/http-api/premium/#rejoin-visionary-guild).
|
||||
A quantity of `0` grants lifetime Visionary premium and immediately cancels an active subscription without proration or a final invoice. When the gift record has a Visionary sequence number, the redeemer receives that number as their lifetime Visionary sequence and joins the [Visionary guild](/http-api/premium/#rejoin-visionary-guild).
|
||||
|
||||
A failed Visionary guild join leaves the gift unredeemed. Guild limits return 400 `MAX_GUILDS` or `MAX_GUILD_MEMBERS`. Any subscription cancellation already completed is not reversed.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ An audit log entry records one change made to a guild and the account that made
|
||||
|
||||
## Audit log reason
|
||||
|
||||
An audit-capable route accepts the `X-Audit-Log-Reason` request header, listed with the other [standard request headers](/http-api/#standard-request-headers). Fluxer reads the value verbatim and never percent-decodes it, so a caller that percent-encodes the reason stores and reads back the percent-encoded form.
|
||||
A route marked Audit reason accepts the `X-Audit-Log-Reason` request header, listed with the other [standard request headers](/http-api/#standard-request-headers). Fluxer reads the value verbatim and never percent-decodes it, so a caller that percent-encodes the reason stores and reads back the percent-encoded form.
|
||||
|
||||
Fluxer trims the value. A value that is blank before trimming, empty after it, or longer than 512 characters after it counts as no reason at all. None of those values fails the request, so a reason that misses the bound is dropped and the operation still succeeds.
|
||||
|
||||
@@ -197,7 +197,7 @@ Fluxer serialises a permission mask or a snowflake as a decimal string. A bounde
|
||||
:::
|
||||
|
||||
:::caution[A change list holds only the differing fields]
|
||||
An update records one change object per differing field, so an unchanged field is absent. An identity field never appears in an update change list, though it does appear in a creation or deletion list, where only one side of the comparison exists.
|
||||
An update records one change object per differing field, so an unchanged field is absent. The field that holds the entity's own ID, such as `channel_id` or `role_id`, never appears in an update change list, though it does appear in a creation or deletion list, where only one side of the comparison exists.
|
||||
:::
|
||||
|
||||
### Change fields
|
||||
|
||||
@@ -44,7 +44,7 @@ The initial overwrite collection supplied when a channel is created. The stored
|
||||
|
||||
A larger decimal string returns 400 `INVALID_FORM_BODY` with the code `INTEGER_OUT_OF_INT64_RANGE`. A JSON number outside the safe integer range returns `INVALID_INTEGER_FORMAT`, and so does a string that is not all digits.
|
||||
|
||||
An undefined bit never fails the authority comparison described by [Create guild channel](#create-guild-channel). It is stored as supplied and echoed back by every later read of the channel.
|
||||
A bit that names no defined permission never fails the authority comparison described by [Create guild channel](#create-guild-channel). It is stored as supplied and echoed back by every later read of the channel.
|
||||
|
||||
A bit set in both masks resolves to an allow during [permission computation](/http-api/permissions/#permission-computation).
|
||||
|
||||
@@ -73,7 +73,7 @@ One entry of the bulk hierarchy update. Entries are applied in array order, each
|
||||
| preceding_sibling_id?<sup>3</sup> | ?snowflake | Sibling that sits directly before this channel, or null to place it first |
|
||||
| lock_permissions?<sup>4</sup> | boolean | Whether to copy the destination category's overwrites onto the moved channel (default false) |
|
||||
|
||||
<sup>1</sup> Read only when `preceding_sibling_id` is omitted. Sending `preceding_sibling_id` at all, including as null, makes it inert
|
||||
<sup>1</sup> Read only when `preceding_sibling_id` is omitted. When `preceding_sibling_id` is sent, including as null, Fluxer ignores `position`
|
||||
|
||||
<sup>2</sup> An omitted field keeps the channel's current parent. A category given any parent is refused with `CATEGORIES_CANNOT_HAVE_PARENTS`
|
||||
|
||||
@@ -191,7 +191,7 @@ Creates a guild channel and returns its [channel object](/http-api/channels/#cha
|
||||
|
||||
A value longer than 10000 characters is rejected with `STRING_LENGTH_INVALID` before normalisation runs. A parent that does not exist in this guild returns `INVALID_PARENT_CHANNEL`, and one that is not a category returns `PARENT_MUST_BE_CATEGORY`.
|
||||
|
||||
The guild holds at most the instance-configured `max_guild_channels` [limit](/http-api/instance/#limit-keys), defaulting to 500, and a parent category holds at most `max_channels_per_category`, defaulting to 50. Reaching the guild limit returns 400 `MAX_GUILD_CHANNELS` and reaching the category limit returns 400 `MAX_CATEGORY_CHANNELS`. Each message has the resolved limit. The new channel is created with no RTC region.
|
||||
The guild holds at most the instance-configured `max_guild_channels` [limit](/http-api/instance/#limit-keys), defaulting to 500, and a parent category holds at most `max_channels_per_category`, defaulting to 50. Reaching the guild limit returns 400 `MAX_GUILD_CHANNELS` and reaching the category limit returns 400 `MAX_CATEGORY_CHANNELS`. Each error message states the resolved limit. The new channel is created with no RTC region.
|
||||
|
||||
The new channel takes a position derived from its siblings. A category, and a channel created with no parent, is placed after the highest position in the guild. A voice channel created inside a category is placed after the last voice sibling, or after the last text or link sibling when the category holds no voice channel. A text or link channel is placed after the last text or link sibling, and a channel with no such sibling is placed directly after the category itself. The rest of the guild is not renumbered, so two channels can hold the same stored position until the next [hierarchy update](#modify-guild-channel-positions) renumbers them.
|
||||
|
||||
@@ -239,7 +239,7 @@ Applies a guild channel hierarchy update and returns 204 with an empty body. Req
|
||||
|
||||
Concurrent hierarchy updates to the same guild can return 423 [`GENERAL_ERROR`](/http-api/errors/) with a `Retry-After` header of two seconds.
|
||||
|
||||
An unknown channel, parent or sibling rejects the entire request. Other failures can leave earlier entries applied. Moving a category moves its children with it, and neither the category nor its children count as destination siblings.
|
||||
An unknown channel, parent or sibling rejects the entire request. Fluxer applies entries one at a time. When a later entry fails a check after that existence check, including the `lock_permissions` authority check, every earlier entry stays applied. Moving a category moves its children with it, and neither the category nor its children count as destination siblings.
|
||||
|
||||
After a move, positions run consecutively from 1 across the guild. Each category is followed by its children, with text and link channels before voice channels.
|
||||
|
||||
@@ -271,7 +271,7 @@ Channels at the guild root are exempt from this restriction.
|
||||
| 400<sup>1</sup><sup>2</sup> | [error response](/http-api/#error-response) | An entry is structurally invalid, the destination category is full, or the caller has no enrolled authenticator in an elevated-MFA guild |
|
||||
| 403 | [error response](/http-api/#error-response) | Caller lacks `MANAGE_CHANNELS`, is not a member of the guild, or set `lock_permissions` without sufficient authority in the moved channel, each returning `MISSING_PERMISSIONS` |
|
||||
| 404 | [error response](/http-api/#error-response) | Guild does not exist, returning `UNKNOWN_GUILD` |
|
||||
| 423 | [error response](/http-api/#error-response) | Another hierarchy update owns this guild, returning `GENERAL_ERROR` |
|
||||
| 423 | [error response](/http-api/#error-response) | Another hierarchy update to this guild is in progress, returning `GENERAL_ERROR` |
|
||||
|
||||
<sup>1</sup> A structural failure returns `INVALID_FORM_BODY` and names the field with `CHANNEL_NOT_FOUND`, `INVALID_CHANNEL_ID`, `INVALID_PARENT_CHANNEL`, `PARENT_MUST_BE_CATEGORY`, `CATEGORIES_CANNOT_HAVE_PARENTS`, `PRECEDING_CHANNEL_MUST_SHARE_PARENT`, `CANNOT_POSITION_CHANNEL_RELATIVE_TO_ITSELF`, or `VOICE_CHANNELS_CANNOT_BE_ABOVE_TEXT_CHANNELS`. A full destination category returns the top-level `MAX_CATEGORY_CHANNELS`
|
||||
|
||||
@@ -283,7 +283,7 @@ Each entry that changes the order emits [Channel Update Bulk](/gateway/events/#c
|
||||
|
||||
When `lock_permissions` copies the destination category's overwrites, re-read the moved channel to obtain them. The copy has no Gateway event or audit entry.
|
||||
|
||||
The caller must hold [MANAGE_ROLES](/http-api/permissions/) in the moved channel and each deny bit the copy removes or allow bit it adds. Insufficient authority returns 403 `MISSING_PERMISSIONS`, but the channel move remains applied.
|
||||
The copy requires [MANAGE_ROLES](/http-api/permissions/) in the moved channel. The caller must also hold every allow bit the copy adds and every deny bit the copy removes. Insufficient authority returns 403 `MISSING_PERMISSIONS`, but the channel move remains applied.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ Each successful item consumes one guild emoji slot and records an [`EMOJI_CREATE
|
||||
|
||||
Copies an existing emoji into the target guild and returns the new [guild emoji object](#guild-emoji-object) without `user`. Requires membership of the target guild and [CREATE_EXPRESSIONS](/http-api/permissions/) there. Emits a [Guild Emojis Update](/gateway/events/#guild-emojis-update) Gateway event in the target guild.
|
||||
|
||||
The name, animation state and image are copied unchanged. Membership of the source guild is not required, but it must have opted in with [CLONE_EMOJI_ENABLED](/http-api/guilds/#guild-features).
|
||||
The name, animation state and image are copied unchanged. Membership of the source guild is not required. The source guild must have [CLONE_EMOJI_ENABLED](/http-api/guilds/#guild-features).
|
||||
|
||||
[Get emoji metadata](/http-api/expressions/#get-emoji-metadata) reports whether a source permits cloning.
|
||||
|
||||
@@ -309,7 +309,7 @@ An emoji that does not belong to the guild in the path returns 404 `UNKNOWN_EMOJ
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [guild emoji](#guild-emoji-object) object | Name was processed |
|
||||
| 200 | [guild emoji](#guild-emoji-object) object | Name was set, including a name equal to the current one |
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | Path parameter or name is invalid |
|
||||
| 403<sup>2</sup> | [error response](/http-api/#error-response) | Name is blocked, the guild is unavailable, or the caller is neither the uploader with `CREATE_EXPRESSIONS` nor a member holding `MANAGE_EXPRESSIONS` |
|
||||
| 404 | [error response](/http-api/#error-response) | Emoji does not exist in that guild and the request returns `UNKNOWN_EMOJI` |
|
||||
|
||||
@@ -85,7 +85,7 @@ The result has no `avatar`, `banner`, `accent_color`, `mute`, `deaf`, `communica
|
||||
|
||||
## Guild member search supplemental object
|
||||
|
||||
A guild member search supplemental object has the join source the [guild member object](/http-api/guild-members/#guild-member-object) never exposes. Only a caller that can already manage the guild reads it.
|
||||
A guild member search supplemental object has the join source the [guild member object](/http-api/guild-members/#guild-member-object) never exposes. Only a caller holding [MANAGE_GUILD](/http-api/permissions/) receives its values.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -210,7 +210,7 @@ An instance using Meilisearch returns an empty page beyond 10000 results. Instan
|
||||
|
||||
<sup>1</sup> The [error code](/http-api/errors/) is `MISSING_ACCESS` for an unavailable guild, `CONTENT_BLOCKED` for a blocked body string, and `MISSING_PERMISSIONS` otherwise
|
||||
|
||||
When results are being prepared, the response is 200 with an empty page and `indexing` set to true. Retry later. Search unavailability can also produce an empty page with `indexing` false, rather than `FEATURE_TEMPORARILY_DISABLED`.
|
||||
When Fluxer is building the guild member index, the response is 200 with an empty page and `indexing` set to true. Retry later. This route has no `FEATURE_TEMPORARILY_DISABLED` response. When the search backend is unavailable, the route returns 200 with an empty page and `indexing` false.
|
||||
|
||||
:::note[`indexing` false does not mean zero matches]
|
||||
An empty page with `indexing` false can mean no matches or that search is unavailable.
|
||||
@@ -218,7 +218,7 @@ An empty page with `indexing` false can mean no matches or that search is unavai
|
||||
|
||||
### Side effects
|
||||
|
||||
The first search can begin preparing the guild's results. It does not change memberships.
|
||||
A search in a guild whose member index needs building queues an indexing job and returns `indexing` true. It does not change memberships.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A guild member is an account that has joined a guild. The membership has a nickname, avatar, role set, and moderation state that apply in that guild alone. Indexed member queries live on [Guild member search](/http-api/guild-member-search/), and bans on [Guild moderation](/http-api/guild-moderation/).
|
||||
|
||||
An unknown guild returns 404 `UNKNOWN_GUILD`, a non-member receives 403 `MISSING_PERMISSIONS`, and a guild marked unavailable returns 403 `MISSING_ACCESS`. [Transfer guild ownership](#transfer-guild-ownership) can also return 403 `ACCESS_DENIED` when the guild cannot be accessed.
|
||||
An unknown guild returns 404 `UNKNOWN_GUILD`, a non-member receives 403 `MISSING_PERMISSIONS`, and a guild marked unavailable returns 403 `MISSING_ACCESS`. [Transfer guild ownership](#transfer-guild-ownership) also returns 403 `ACCESS_DENIED` when the guild exists in storage but the Gateway reports it as not found.
|
||||
|
||||
The modify operations return the resulting membership, [Transfer guild ownership](#transfer-guild-ownership) returns the updated guild, and every other mutation returns 204 with an empty body.
|
||||
|
||||
@@ -49,7 +49,7 @@ Every field except `user` describes state that belongs to the membership.
|
||||
|
||||
<sup>6</sup> Omitted while the stored value is 0, so absence means no flag is set and, for `mention_flags`, that the account-level preference applies
|
||||
|
||||
Guild avatars and banners can be hidden after the account loses its premium entitlement.
|
||||
Fluxer marks a membership premium sanitised after its account loses premium, when the membership has a guild avatar, banner, bio, or accent colour. A premium sanitised membership reports `avatar` and `banner` as null.
|
||||
|
||||
A client compares `communication_disabled_until` against the current time, because a non-null value can name a moment that has already passed. A [communication timeout](/http-api/permissions/#communication-timeout) does not reduce the member's computed permission mask.
|
||||
|
||||
@@ -76,7 +76,7 @@ A client compares `communication_disabled_until` against the current time, becau
|
||||
|
||||
## Guild member profile flags
|
||||
|
||||
A guild profile asset has these states. A membership with no flag and no stored hash inherits the account-level asset. A stored hash sets a guild-specific asset. The flag below blocks inheritance and renders the default.
|
||||
A guild profile asset has these states. A membership with no flag and no stored hash inherits the account-level asset. A stored hash sets a guild-specific asset. Each flag below blocks inheritance, so the client shows the default asset.
|
||||
|
||||
| Value | Name | Description |
|
||||
| --- | --- | --- |
|
||||
@@ -127,7 +127,7 @@ The request body shared by [Modify current guild member](#modify-current-guild-m
|
||||
|
||||
A `roles` entry that does not resolve to an existing role of the guild is dropped from the replacement, so a request naming only unknown roles clears the member's role set.
|
||||
|
||||
An `avatar` or `banner` base64 payload longer than 13981016 characters returns `BASE64_LENGTH_INVALID`, and a malformed one returns `INVALID_BASE64_FORMAT`. Fluxer then checks the decoded bytes against the instance-configured `avatar_max_size` [limit](/http-api/instance/#limit-keys), whose stock value is 10485760. The same limit applies to both fields. The decoded image must also pass the format allowlist and the animation rules of the asset policy for the field it sets. Pixel dimensions are not enforced. A value that is too large returns `IMAGE_SIZE_EXCEEDS_LIMIT`, and one whose format or animation is not allowed returns `INVALID_IMAGE_FORMAT`.
|
||||
An `avatar` or `banner` base64 payload longer than 13981016 characters returns `BASE64_LENGTH_INVALID`, and a malformed one returns `INVALID_BASE64_FORMAT`. Fluxer then checks the decoded bytes against the instance-configured `avatar_max_size` [limit](/http-api/instance/#limit-keys), whose stock value is 10485760. The same limit applies to both fields. The decoded image must also be in a format the field accepts, and an animated AVIF is rejected. Pixel dimensions are not enforced. A value that is too large returns `IMAGE_SIZE_EXCEEDS_LIMIT`, and one whose format or animation is not allowed returns `INVALID_IMAGE_FORMAT`.
|
||||
|
||||
Null and any `communication_disabled_until` that is not in the future both clear the timeout. A time more than 365.25 days ahead returns `TIMEOUT_CANNOT_EXCEED_365_DAYS`, and a value that passes the schema but is not a real instant returns `INVALID_TIMEOUT_VALUE`. `timeout_reason` has no effect unless `communication_disabled_until` is also supplied.
|
||||
|
||||
@@ -318,7 +318,7 @@ A caller addressing their own user ID here follows the self-targeted rules of [M
|
||||
:::
|
||||
|
||||
:::caution[A blocklisted `bio` or `pronouns` returns 403]
|
||||
The request fails with `CONTENT_BLOCKED` even though the field is never written.
|
||||
A request that supplies a blocked `bio` or `pronouns` for another member fails with `CONTENT_BLOCKED`, even though Fluxer never writes those fields for another member.
|
||||
:::
|
||||
|
||||
### Path parameters
|
||||
@@ -350,7 +350,7 @@ The body is a [guild member update object](#guild-member-update-object).
|
||||
|
||||
### Side effects
|
||||
|
||||
The operation has the same effects as [Modify current guild member](#modify-current-guild-member). Applying a non-empty role set also makes a temporary membership permanent.
|
||||
The operation has the same effects as [Modify current guild member](#modify-current-guild-member). Applying a non-empty role set also makes a temporary membership permanent. A membership is temporary when the member joined through an [invite](/http-api/invites/#invite-object) whose `temporary` field is true.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -45,12 +45,12 @@ A guild ban object names the banned account, the moderator, the reason, and any
|
||||
```
|
||||
|
||||
:::note[Fluxer never returns the ban address or email]
|
||||
A ban can also prevent accounts using the same IP address or email from joining. These values are not exposed in the ban object.
|
||||
A ban also stores the email address and the last active IP address of the banned account. Fluxer rejects an invite join by any account that matches either value. These values are not exposed in the ban object.
|
||||
:::
|
||||
|
||||
An [invite](/http-api/invites/) rejected for an IP match returns 403 `USER_IP_BANNED_FROM_GUILD`. An account or email match returns 403 `USER_BANNED_FROM_GUILD`. Address exemptions can allow a join from a shared network. Ban checks differ for administrator actions, bot installation and automatic guild joins.
|
||||
An [invite](/http-api/invites/) rejected for an IP match returns 403 `USER_IP_BANNED_FROM_GUILD`. An account or email match returns 403 `USER_BANNED_FROM_GUILD`. Fluxer skips the IP match when the joining account's address is on the operator's IP ban exemption list, or when IP lookup classifies that address as carrier-grade NAT or shared access. Adding a member through the Admin API, installing a bot through OAuth2, and the automatic join into the visionaries guild after a Stripe purchase skip every ban check. The automatic join into the single community checks the account and IP matches and skips the email match.
|
||||
|
||||
[Remove guild ban](#remove-guild-ban) releases both blocks. Permanently deleting the banned account deletes every guild ban it holds, and that releases both blocks in every guild at once.
|
||||
[Remove guild ban](#remove-guild-ban) releases the IP address block and the email block. Permanently deleting the banned account deletes every guild ban it holds, which releases both of those blocks in every guild at once.
|
||||
|
||||
## List guild bans
|
||||
|
||||
@@ -95,7 +95,7 @@ Creates a guild ban, or replaces an existing one, and returns 204 with an empty
|
||||
|
||||
- The caller cannot ban themselves, and Fluxer reports a self-target as 404 `UNKNOWN_MEMBER`.
|
||||
- Banning a target who is a member also requires role hierarchy authority over that member. The guild owner holds that authority over everyone, and no other caller holds it over the owner.
|
||||
- A target who is not a member can still be banned, and the ban pre-empts a future join.
|
||||
- A target who is not a member can still be banned, and Fluxer refuses a later join by that account.
|
||||
|
||||
A blocked `reason` returns 403 `CONTENT_BLOCKED`.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Every route names a guild in its path. A guild that has [UNAVAILABLE_FOR_EVERYON
|
||||
|
||||
Names, descriptions, tags and uploaded images must pass the instance's content policy. Blocked content returns 403 `CONTENT_BLOCKED`.
|
||||
|
||||
A guild that does not exist returns 404 `UNKNOWN_GUILD`. A caller who is not a current member of an existing guild returns 403 `MISSING_PERMISSIONS`, so guild existence is distinguishable from guild membership. [Modify guild sticker](#modify-guild-sticker) answers 404 `UNKNOWN_STICKER` for such a guild.
|
||||
A guild that does not exist returns 404 `UNKNOWN_GUILD`. A caller who is not a current member of an existing guild returns 403 `MISSING_PERMISSIONS`, so guild existence is distinguishable from guild membership. [Modify guild sticker](#modify-guild-sticker) returns 404 `UNKNOWN_STICKER` for a guild that does not exist.
|
||||
|
||||
There is no single-sticker read scoped to a guild. [List guild stickers](#list-guild-stickers) returns the whole collection in one response.
|
||||
|
||||
@@ -132,7 +132,7 @@ One rejected item from a bulk create call, named and explained in display text.
|
||||
|
||||
<sup>2</sup> Rendered in the locale of the authenticated account, so its value changes with the caller's locale
|
||||
|
||||
There is no machine-readable code, so a client that needs to branch on the reason retries the item on its own.
|
||||
There is no machine-readable code, so a client that needs to branch on the reason submits that item again through [Create guild sticker](#create-guild-sticker), which returns an error code.
|
||||
|
||||
## List guild stickers
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ A guild is a community with its own channels, roles, members, and configuration.
|
||||
|
||||
[List current user guilds](#list-current-user-guilds) and [Get guild](#get-guild) declare the `guilds` [OAuth2 scope](/http-api/oauth2/#oauth2-scopes), and a bearer credential without that scope receives 403 `MISSING_OAUTH_SCOPE`. Every other route rejects a bearer credential with 403 `ACCESS_DENIED`.
|
||||
|
||||
A guild that does not exist returns 404 `UNKNOWN_GUILD`. A non-member receives 403 `MISSING_PERMISSIONS`. A guild that exists but cannot be accessed can return 403 `ACCESS_DENIED`.
|
||||
A guild that does not exist returns 404 `UNKNOWN_GUILD`. A non-member receives 403 `MISSING_PERMISSIONS`. A guild that exists in storage but that the Gateway reports as not found returns 403 `ACCESS_DENIED`.
|
||||
|
||||
An [unavailable guild](#guild-features) can still appear in [List current user guilds](#list-current-user-guilds). Members can still [leave](#leave-guild) or [delete their own messages](#bulk-delete-current-users-guild-messages).
|
||||
|
||||
@@ -93,7 +93,7 @@ A guild object contains the guild's configuration. The operation that returns it
|
||||
|
||||
<sup>14</sup> Only [Get guild](#get-guild) populates these fields
|
||||
|
||||
<sup>15</sup> Only [List current user guilds](#list-current-user-guilds) populates these fields, and only when `with_counts` is true. A guild whose counts are not currently held reports 0 for both
|
||||
<sup>15</sup> Only [List current user guilds](#list-current-user-guilds) populates these fields, and only when `with_counts` is true. A guild for which the Gateway holds no cached counts reports 0 for both
|
||||
|
||||
:::note[An absent field means the operation omitted it]
|
||||
`roles`, `emojis`, `stickers`, `channels`, and every count field arrive only from the operations named in the footnotes above. A guild returned without `roles` can still hold roles.
|
||||
@@ -251,7 +251,7 @@ A creation template describes the roles and channels that [Create guild](#create
|
||||
|
||||
<sup>1</sup> A duplicate identifier in the same template rejects creation with 400 `GUILD_TEMPLATE_INVALID`
|
||||
|
||||
<sup>2</sup> The value 0 creates a text channel, 2 a voice channel, and 4 a category, and for an import from the other platform 5 creates a text channel and 13 a voice channel. Fluxer skips every other value, so the channel is not created
|
||||
<sup>2</sup> The value 0 creates a text channel, 2 a voice channel, and 4 a category. The value 5, the announcement channel type of another platform, creates a text channel. The value 13, the stage channel type of another platform, creates a voice channel. Fluxer skips every other value, so the channel is not created
|
||||
|
||||
<sup>3</sup> The identifier is applied only when it resolves to a category in the same template, and every other value leaves the channel at the guild root
|
||||
|
||||
@@ -390,7 +390,7 @@ Each value in the guild's `features` array is a capability or availability flag.
|
||||
| INVITE_SPLASH | Guild can use invite splash assets |
|
||||
| INVITES_DISABLED<sup>1</sup> | Guild invite use is disabled |
|
||||
| RAID_DETECTED | Raid detection is active and invites are restricted |
|
||||
| TEXT_CHANNEL_FLEXIBLE_NAMES<sup>1</sup> | Text channel names accept the flexible naming policy |
|
||||
| TEXT_CHANNEL_FLEXIBLE_NAMES<sup>1</sup> | Text channel names keep uppercase letters, spaces, and punctuation |
|
||||
| HIDE_OWNER_CROWN<sup>1</sup> | Guild owner crown is hidden |
|
||||
| MORE_EMOJI<sup>2</sup> | Legacy increased emoji slot allowance |
|
||||
| MORE_STICKERS<sup>2</sup> | Legacy increased sticker slot allowance |
|
||||
@@ -401,11 +401,11 @@ Each value in the guild's `features` array is a capability or availability flag.
|
||||
| DISCOVERABLE | Guild is present in public discovery |
|
||||
| PARTNERED | Guild has partnered status |
|
||||
| VERIFIED | Guild has verified status |
|
||||
| VIP_VOICE | Guild has VIP voice capability |
|
||||
| VIP_VOICE | Guild can use voice regions that are restricted to VIP guilds |
|
||||
| VOICE_E2EE | Guild voice channels support end-to-end encryption |
|
||||
| UNAVAILABLE_FOR_EVERYONE<sup>4</sup> | Guild is unavailable to every account |
|
||||
| UNAVAILABLE_FOR_EVERYONE_BUT_STAFF<sup>4</sup> | Guild is unavailable to every account without the instance staff flag |
|
||||
| UNAVAILABLE_HIDDEN | Guild is hidden while it is forced unavailable |
|
||||
| UNAVAILABLE_HIDDEN | While the guild is unavailable, the Gateway sends its unavailable guild entry with `unavailable_hidden: true` |
|
||||
| VISIONARY | Guild has visionary status |
|
||||
| LARGE_GUILD_OVERRIDE<sup>2</sup> | Guild is marked as a large guild |
|
||||
| VERY_LARGE_GUILD<sup>5</sup> | Guild member capacity is raised |
|
||||
@@ -414,7 +414,7 @@ Each value in the guild's `features` array is a capability or availability flag.
|
||||
|
||||
<sup>2</sup> The feature changes no HTTP API behaviour. An instance can name it in a filter of the ordered [limit configuration](/http-api/instance/#limit-keys), and the stock configuration names none of them
|
||||
|
||||
<sup>3</sup> The expression operations raise the slot ceiling directly, outside the instance limit configuration
|
||||
<sup>3</sup> Emoji and sticker creation use a fixed slot ceiling of 999999 and ignore the instance limit configuration
|
||||
|
||||
<sup>4</sup> Guild and channel routes return 403 `MISSING_ACCESS`. `UNAVAILABLE_FOR_EVERYONE` includes the guild owner. `UNAVAILABLE_FOR_EVERYONE_BUT_STAFF` exempts accounts with the instance staff flag
|
||||
|
||||
@@ -494,7 +494,7 @@ Creates a guild owned by the caller. Requires a user session credential. Returns
|
||||
- An unclaimed account is rejected with 400 `UNCLAIMED_ACCOUNT_CANNOT_CREATE_GUILDS`.
|
||||
- An account without a verified email address is rejected with 403 `GUILD_CREATION_EMAIL_VERIFICATION_REQUIRED`.
|
||||
- A caller already at the configured guild limit is rejected with 400 `MAX_GUILDS`.
|
||||
- While the instance's single community policy is active, every caller is rejected with 400 `SINGLE_COMMUNITY_CANNOT_CREATE_GUILDS`.
|
||||
- While `single_community_enabled` is true in the [instance policy](/admin-api/instance/#instance-policy-object), every caller is rejected with 400 `SINGLE_COMMUNITY_CANNOT_CREATE_GUILDS`.
|
||||
|
||||
### JSON body
|
||||
|
||||
@@ -601,7 +601,7 @@ The response has no `permissions` field. Read [List current user guilds](#list-c
|
||||
| 403<sup>1</sup> | [error response](/http-api/#error-response) | Guild is unavailable, the bearer credential lacks the `guilds` scope, or the caller is not a member |
|
||||
| 404 | [error response](/http-api/#error-response) | Guild does not exist and the request returns `UNKNOWN_GUILD` |
|
||||
|
||||
<sup>1</sup> The [error code](/http-api/errors/) is `MISSING_ACCESS` for an unavailable guild, `MISSING_OAUTH_SCOPE` for a missing scope, `MISSING_PERMISSIONS` for a non-member, and `ACCESS_DENIED` when the guild otherwise cannot be accessed
|
||||
<sup>1</sup> The [error code](/http-api/errors/) is `MISSING_ACCESS` for an unavailable guild, `MISSING_OAUTH_SCOPE` for a missing scope, `MISSING_PERMISSIONS` for a non-member, and `ACCESS_DENIED` when the guild exists in storage but the Gateway reports it as not found
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -715,9 +715,9 @@ Send the current array with the intended changes applied. A feature without the
|
||||
|
||||
Every successful request emits [Guild Update](/gateway/events/#guild-update) to every session that can see the guild, and a request that writes no field still emits it.
|
||||
|
||||
Only a change to an audited field records a [`GUILD_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry with the previous and new values. That entry emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) to sessions that can read the audit log.
|
||||
A request that changes the stored value of at least one body field records a [`GUILD_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry with the previous and new values. That entry emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) to sessions that can read the audit log.
|
||||
|
||||
Removing `TEXT_CHANNEL_FLEXIBLE_NAMES` renames every guild text channel whose stored name does not satisfy the strict naming policy. When at least one channel is renamed, the removal emits one [Channel Update Bulk](/gateway/events/#channel-update-bulk) Dispatch with every channel in the guild.
|
||||
Removing `TEXT_CHANNEL_FLEXIBLE_NAMES` renames every guild text channel whose stored name changes when Fluxer trims it, lowercases it, replaces each whitespace run with a hyphen, and removes disallowed punctuation. When at least one channel is renamed, the removal emits one [Channel Update Bulk](/gateway/events/#channel-update-bulk) Dispatch with every channel in the guild.
|
||||
|
||||
Replacing an image removes the previous asset. A failed replacement leaves the previous image unchanged.
|
||||
|
||||
@@ -733,7 +733,7 @@ Permanently deletes the guild and returns 204 with an empty body. Requires the g
|
||||
|
||||
Fluxer refuses a non-owner with 403 `MISSING_PERMISSIONS`. A bot can never own a guild, so a bot credential never satisfies the requirement.
|
||||
|
||||
A guild protected by an active single community policy cannot be deleted and returns 400 `SINGLE_COMMUNITY_CANNOT_DELETE`.
|
||||
While `single_community_enabled` is true in the [instance policy](/admin-api/instance/#instance-policy-object), the guild named by `single_community_guild_id` cannot be deleted and returns 400 `SINGLE_COMMUNITY_CANNOT_DELETE`.
|
||||
|
||||
### Path parameters
|
||||
|
||||
@@ -772,7 +772,7 @@ Deletion removes the guild, memberships, roles, channels, messages, attachments,
|
||||
|
||||
### Side effects
|
||||
|
||||
Every member receives [Guild Delete](/gateway/events/#guild-delete). Guild settings are removed. Non-bot members also receive [User Settings Update](/gateway/events/#user-settings-update) with the guild removed from their folder layout.
|
||||
Every member receives [Guild Delete](/gateway/events/#guild-delete). Fluxer deletes every member's [user guild settings](/http-api/users/settings/#user-guild-settings-object) for the guild. Non-bot members also receive [User Settings Update](/gateway/events/#user-settings-update) with the guild removed from their folder layout.
|
||||
|
||||
No audit log entry is recorded, because the audit log is destroyed with the guild. An `X-Audit-Log-Reason` header on this request is read and discarded.
|
||||
|
||||
@@ -786,7 +786,7 @@ No audit log entry is recorded, because the audit log is destroyed with the guil
|
||||
|
||||
Removes the authenticated account's membership and returns 204 with an empty body. Requires a current membership. Emits a [Guild Member Remove](/gateway/events/#guild-member-remove) Gateway event to the remaining guild sessions and a [Guild Delete](/gateway/events/#guild-delete) Gateway event to the leaving account's own sessions.
|
||||
|
||||
A caller with no current membership receives 404 `UNKNOWN_MEMBER` whether or not the guild exists. The guild owner cannot leave and receives 400 `INVALID_FORM_BODY` with the field code `CANNOT_LEAVE_GUILD_AS_OWNER`. A guild protected by an active single community policy cannot be left and returns 400 `SINGLE_COMMUNITY_CANNOT_LEAVE`. Setting `delete_messages` requires sudo mode, which a bot credential satisfies implicitly.
|
||||
A caller with no current membership receives 404 `UNKNOWN_MEMBER` whether or not the guild exists. The guild owner cannot leave and receives 400 `INVALID_FORM_BODY` with the field code `CANNOT_LEAVE_GUILD_AS_OWNER`. While `single_community_enabled` is true in the [instance policy](/admin-api/instance/#instance-policy-object), the guild named by `single_community_guild_id` cannot be left and returns 400 `SINGLE_COMMUNITY_CANNOT_LEAVE`. Setting `delete_messages` requires sudo mode, which a bot credential satisfies implicitly.
|
||||
|
||||
### Path parameters
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ An empty string becomes `null` at any depth, and an empty nested object becomes
|
||||
|
||||
This normalisation applies to JSON and form bodies, query strings, path parameters, request headers, and cookies.
|
||||
|
||||
A nested object containing only `null` values also becomes `null`. The root object is preserved. An empty body is treated as `{}` and validated for required fields. Malformed JSON returns 400 `INVALID_FORM_BODY` with a validation error at path `body` and code `INVALID_FORMAT`.
|
||||
A nested object containing only `null` values also becomes `null`. The root object never becomes `null`, even when it is empty or has only `null` values. An empty body is treated as `{}` and validated for required fields. Malformed JSON returns 400 `INVALID_FORM_BODY` with a validation error at path `body` and code `INVALID_FORMAT`.
|
||||
|
||||
:::caution[Message operations preserve empty values]
|
||||
[Create message](/http-api/messages/#create-message), [Modify message](/http-api/messages/#modify-message), and [Execute webhook](/http-api/webhooks/#execute-webhook) do not apply this normalisation.
|
||||
@@ -162,7 +162,7 @@ An operation that sets its own `Cache-Control` keeps that value. A response whos
|
||||
|
||||
## Rate limits
|
||||
|
||||
Every route consumes its own rate limit bucket and is also evaluated against one global bucket unless that bucket is exempt. A denial returns 429 `RATE_LIMITED`. [Rate limits](/topics/rate-limits/) defines the bucket scoping rules, the global allowance, the 429 body, the scope registry, and the complete `X-RateLimit-*` header contract.
|
||||
Every route consumes its own rate limit bucket. A route that is not exempt from the global bucket also counts against one global bucket. A denial returns 429 `RATE_LIMITED`. [Rate limits](/topics/rate-limits/) defines the bucket scoping rules, the global allowance, the 429 body, the scope registry, and the complete `X-RateLimit-*` header contract.
|
||||
|
||||
:::note[These 429 responses have no `X-RateLimit-*` header]
|
||||
A 429 `RESOURCE_LOCKED` response has `Retry-After: 1`, and a 429 `IP_AUTHORIZATION_RESEND_COOLDOWN` response has the remaining cooldown in whole seconds. A client that reads the bucket headers branches on `code`.
|
||||
@@ -180,7 +180,7 @@ The CORS response policy is an allow-list of exactly two origins, the deployment
|
||||
|
||||
The paths below are readable from any origin. `/v1/webhooks/{webhook_id}/{token}` and `/v1/webhooks/{webhook_id}/{token}/messages/{message_id}` have a second cross-origin policy that allows any origin. Four of the methods registered on them refuse the first-party web client outright, and that refusal is defined by [Origin refusal](/http-api/webhooks/#origin-refusal).
|
||||
|
||||
[Get instance discovery](/http-api/instance/#get-instance-discovery) on `/.well-known/fluxer`, [Get OpenAPI document](/http-api/instance/#get-openapi-document) on `/v1/openapi.json`, and [Get client geolocation](/http-api/instance/#get-client-geolocation) on `/v1/ip` set `Access-Control-Allow-Origin: *` in the operation itself. The wildcard stands for any origin outside the allow-list, and for an allowed origin the policy replaces it with that exact origin and sends `Vary: Origin`.
|
||||
[Get instance discovery](/http-api/instance/#get-instance-discovery) on `/.well-known/fluxer`, [Get OpenAPI document](/http-api/instance/#get-openapi-document) on `/v1/openapi.json`, and [Get client geolocation](/http-api/instance/#get-client-geolocation) on `/v1/ip` set `Access-Control-Allow-Origin: *` in the operation itself. A request whose `Origin` is absent or outside the allow-list receives `*`. For an allowed origin, the policy replaces `*` with that exact origin and sends `Vary: Origin`.
|
||||
|
||||
`Access-Control-Expose-Headers` is the value `X-Fluxer-Version, ETag`. Every other Fluxer response header, the rate limit headers and `X-Request-ID` included, is hidden from cross-origin script. `Access-Control-Allow-Headers` is `Content-Type, Authorization, X-Requested-With, Accept-Language, X-Request-ID, If-None-Match`, so a cross-origin client revalidates an [ETag](/http-api/experiments/#get-experiment-assignments) it was served.
|
||||
|
||||
@@ -255,7 +255,7 @@ Each entry identifies one failed input field. A 400 response whose top-level cod
|
||||
}
|
||||
```
|
||||
|
||||
Fluxer produces at most one entry for each distinct pair of `path` and `code`, so a field that fails several equivalent constraints appears once.
|
||||
Fluxer produces at most one entry for each distinct pair of `path` and `code`, so a field that fails several constraints with the same `code` appears once.
|
||||
|
||||
## Resource pages
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Each value is an absolute URL supplied by the operator. A value can be a bare or
|
||||
|
||||
<sup>2</sup> The value is the configured Gateway endpoint and its scheme is `ws` or `wss` as the operator configured it
|
||||
|
||||
<sup>3</sup> A deployment that configures a separate static asset domain derives that value over `https` on the configured domain and without the port the other endpoints have
|
||||
<sup>3</sup> When a deployment configures a separate static asset domain, the value is `https://` followed by that domain, with no port
|
||||
|
||||
Every value is the exact origin the deployment advertises, including its scheme and any explicit port, and a deliberately plain HTTP deployment publishes `http` and `ws` values here.
|
||||
|
||||
@@ -193,10 +193,10 @@ Whether this deployment runs as one community, and whether direct messages exist
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| single_community | boolean | Whether this deployment runs as one community that every account joins |
|
||||
| single_community_guild_id<sup>1</sup> | ?snowflake | The stock community guild ID, or null |
|
||||
| single_community_guild_id<sup>1</sup> | ?snowflake | The ID of the guild every account joins in single-community mode, or null |
|
||||
| direct_messages_disabled | boolean | Whether direct messages and friend requests are disabled for the whole deployment |
|
||||
|
||||
<sup>1</sup> The identifier is published only while `single_community` is true and a stock community has been chosen
|
||||
<sup>1</sup> The identifier is published only while `single_community` is true and the deployment has designated a community guild
|
||||
|
||||
## Service availability object
|
||||
|
||||
@@ -258,10 +258,10 @@ The document lets a client present the correct bounds before it attempts an oper
|
||||
:::
|
||||
|
||||
:::caution[An unauthenticated reader resolves only the unfiltered outcome]
|
||||
The document publishes neither the requesting account's traits nor any guild feature set. A client supplies that context from its own authenticated state.
|
||||
The document publishes neither the requesting account's traits nor any guild feature set. A client supplies the account's traits and the guild's feature names itself, from data it received while authenticated.
|
||||
:::
|
||||
|
||||
Nothing validates a rule's `traits` filter against `traitDefinitions`, so a rule can filter on a name that collection omits. A self-hosted deployment whose premium mode grants every account the stock limits publishes an empty collection and has no rule that filters on `premium`.
|
||||
Nothing validates a rule's `traits` filter against `traitDefinitions`, so a rule can filter on a name that collection omits. A self-hosted deployment whose [premium mode](/admin-api/instance/#premium-modes) is `everyone` publishes an empty collection and has no rule that filters on `premium`.
|
||||
|
||||
## Limit rule object
|
||||
|
||||
@@ -353,7 +353,7 @@ Each key names one limit. A key whose name begins with `feature_` is a feature g
|
||||
| max_webhooks_per_guild | Maximum webhooks per guild, in guild scope |
|
||||
| sticker_max_size | Maximum file size for sticker uploads in bytes, in guild scope |
|
||||
|
||||
<sup>1</sup> Emoji limits are enforced as one shared total against `max_guild_emojis`, and the aliases exist only so an older client reads a plausible value
|
||||
<sup>1</sup> Emoji limits are enforced as one shared total against `max_guild_emojis`, and the aliases exist only for older clients. The default of each alias equals the `max_guild_emojis` default
|
||||
|
||||
<sup>2</sup> Sticker limits are enforced as one shared total against `max_guild_stickers`
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ A guild invite always has `guild`, `channel`, `member_count`, and `presence_coun
|
||||
|
||||
## Invite metadata object
|
||||
|
||||
The metadata object extends the invite with its creation and usage state. Creation and listing operations return it when the caller has management access to the target, and [Get invite](#get-invite) never does.
|
||||
The metadata object extends the invite with its creation and usage state. [Create channel invite](#create-channel-invite), [List channel invites](#list-channel-invites), and [List guild invites](#list-guild-invites) return it. [Get invite](#get-invite) never does.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -154,7 +154,7 @@ An `Authorization` header that cannot be resolved is ignored, and the route retu
|
||||
Codes from [Create channel invite](#create-channel-invite) are case-sensitive. Custom invite codes are case-insensitive.
|
||||
|
||||
:::note[Every admission gate runs at accept time]
|
||||
Resolving an invite proves that the record exists and that its target is describable. [Accept invite](#accept-invite) evaluates bans, disabled invites, exhausted uses, missing presence, and target capacity.
|
||||
A 200 response means the invite code exists and none of the 404 conditions in the response table below applies. [Accept invite](#accept-invite) checks bans, the guild's invites-disabled feature, exhausted uses, the active presence a temporary invite requires, and whether the target is full.
|
||||
:::
|
||||
|
||||
:::note[Lookup does not guarantee admission]
|
||||
@@ -223,15 +223,15 @@ Accepting an exhausted invite returns 404 `UNKNOWN_INVITE` and makes the code st
|
||||
| 403 | [error response](/http-api/#error-response) | A deferred phone verification requirement becomes due and the request returns `ACCOUNT_SUSPICIOUS_ACTIVITY` |
|
||||
| 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, or the invite names no reachable target, each returning `UNKNOWN_INVITE` |
|
||||
| 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` |
|
||||
|
||||
### Side effects
|
||||
|
||||
An account already in the target receives the invite unchanged without consuming a use or emitting a Dispatch. A new admission consumes one use and removes the invite when its limit is reached. A failed admission consumes no use. If the invite's channel has been deleted, the account can still join and consume a use even though the response is 404 `UNKNOWN_CHANNEL`.
|
||||
When the account is already in the target, the route returns 200 with the invite object. It consumes no use and emits no Dispatch. A new admission consumes one use and removes the invite when its limit is reached. A failed admission consumes no use. If the invite's channel has been deleted, the account can still join and consume a use even though the response is 404 `UNKNOWN_CHANNEL`.
|
||||
|
||||
A guild admission creates the membership, records whether the join used a custom invite URL or an instant invite, and adds the guild to the caller's settings and folder layout. A temporary invite marks the membership temporary.
|
||||
|
||||
The joining account receives [Guild Create](/gateway/events/#guild-create). [Guild Member Add](/gateway/events/#guild-member-add) is guild-wide and reaches every session connected to the guild, subject to the [event filtering](/gateway/event-filtering/) gates, which suppress it for a passive session in a large guild. The joining account receives [User Settings Update](/gateway/events/#user-settings-update) when the join changes its stored settings, and [User Guild Settings Update](/gateway/events/#user-guild-settings-update) when its `default_hide_muted_channels` setting is enabled. Unless join notifications are suppressed or no system channel exists, the operation creates a join system message and delivers [Message Create](/gateway/events/#message-create).
|
||||
The joining account receives [Guild Create](/gateway/events/#guild-create). [Guild Member Add](/gateway/events/#guild-member-add) is guild-wide and reaches every session connected to the guild, subject to the [event filtering](/gateway/event-filtering/) gates, which withhold it from a passive user session in a guild with more than 250 members. The joining account receives [User Settings Update](/gateway/events/#user-settings-update) when the join changes its stored settings, and [User Guild Settings Update](/gateway/events/#user-guild-settings-update) when its `default_hide_muted_channels` setting is enabled. Unless the guild's `system_channel_flags` has [SUPPRESS_JOIN_NOTIFICATIONS](/http-api/guilds/#system-channel-flags) or the guild has no system channel, the operation creates a join system message and delivers [Message Create](/gateway/events/#message-create).
|
||||
|
||||
A group direct message admission adds the caller as a recipient and creates a recipient addition system message. The joining account receives [Channel Create](/gateway/events/#channel-create), the recipients the group already held receive [Channel Recipient Add](/gateway/events/#channel-recipient-add), and every recipient receives [Message Create](/gateway/events/#message-create).
|
||||
|
||||
@@ -305,7 +305,7 @@ Creates an invite for a channel, or returns an existing equivalent invite. Retur
|
||||
This operation does not require elevated multi-factor authentication.
|
||||
|
||||
:::note[Guild affiliation determines the invite type]
|
||||
A channel that belongs to a guild creates a guild invite. Any other channel the caller can resolve creates a type `1` invite, including a direct message and the personal notes channel. Only a group direct message admits an account afterwards.
|
||||
A channel that belongs to a guild creates a guild invite. Any other channel the caller can resolve creates a type `1` invite, including a direct message and the personal notes channel. Of these type `1` invites, only one whose channel is a group direct message admits an account through [Accept invite](#accept-invite).
|
||||
:::
|
||||
|
||||
### Path parameters
|
||||
|
||||
@@ -6,13 +6,13 @@ description: Saved favourite media and batch GIF URL resolution.
|
||||
|
||||
import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A meme is an image, video, or audio asset saved as a favourite. Its copy remains available after the source URL or message disappears.
|
||||
A meme is an image, video, or audio asset saved as a favourite. Fluxer stores a copy of the media with the meme. The copy remains available after the source URL or the source message is gone.
|
||||
|
||||
The routes here are user-only and operate on the caller's own collection. Fluxer rejects a bot token with 403 `ACCESS_DENIED`. [Create message](/http-api/messages/#create-message) sends a stored meme through its `favorite_meme_id` field, and the [GIF provider API](/http-api/gifs/) searches the configured provider for a GIF to save.
|
||||
|
||||
## Collection limits
|
||||
|
||||
Fluxer caps a collection at the instance-configured `max_favorite_memes` [limit](/http-api/instance/#limit-keys), with a fallback of 50. Both creation operations check the count before they touch the channel, the message, or the source URL, so an account at its limit is refused with 400 `MAX_FAVORITE_MEMES` even when the rest of the request would have failed. The error body has `max_favorite_memes` reporting the ceiling reached.
|
||||
Fluxer caps a collection at the instance-configured `max_favorite_memes` [limit](/http-api/instance/#limit-keys), with a fallback of 50. Both creation operations check the count before they look up the channel or the message and before they fetch the source URL, so an account at its limit is refused with 400 `MAX_FAVORITE_MEMES` even when the rest of the request would have failed. The error body has `max_favorite_memes` reporting the ceiling reached.
|
||||
|
||||
A meme has at most `max_favorite_meme_tags` tags, with a fallback of 10. Fluxer rejects a longer list with 400 `INVALID_FORM_BODY` and the validation code `MAX_FAVORITE_MEME_TAGS_EXCEEDED` at the `tags` path.
|
||||
|
||||
@@ -101,7 +101,7 @@ One descriptor describes one encoding of one provider GIF. A provider returns se
|
||||
| width<sup>1</sup> | integer | The width of this format in pixels |
|
||||
| height<sup>1</sup> | integer | The height of this format in pixels |
|
||||
|
||||
<sup>1</sup> A non-negative value of at most 2147483647. A descriptor with an empty `src` or `proxy_src`, or a zero dimension, is unusable as a preview and is skipped
|
||||
<sup>1</sup> A non-negative value of at most 2147483647. A descriptor with an empty `src` or `proxy_src`, or a zero dimension, is skipped when Fluxer picks the [preview format](#resolved-gif-entry-object) for Resolve GIF URLs
|
||||
|
||||
A client submitting a format map to [Save meme from URL](#save-meme-from-url) supplies all members of every descriptor, and a descriptor missing one fails validation with 400 `INVALID_FORM_BODY`.
|
||||
|
||||
@@ -199,7 +199,7 @@ Fetches an absolute media URL, stores a durable Fluxer copy, and returns the cre
|
||||
|
||||
<sup>2</sup> An omitted or null value is an empty tag list
|
||||
|
||||
<sup>3</sup> Honoured together only when `gif_provider` names the configured provider. Otherwise Fluxer extracts a slug from `url` with the configured provider
|
||||
<sup>3</sup> Fluxer uses the supplied pair only when both are present and `gif_provider` names the configured provider. Otherwise Fluxer extracts a slug from `url` with the configured provider
|
||||
|
||||
<sup>4</sup> Stored only when the request resolved to a provider GIF and the map is non-empty, and discarded otherwise
|
||||
|
||||
@@ -234,7 +234,7 @@ The operation emits one [Favorite Meme Create](/gateway/events/#favorite-meme-cr
|
||||
|
||||
Copies one attachment or one embed asset from a readable message into the caller's own collection and returns the created [meme object](#meme-object). Requires [VIEW_CHANNEL](/http-api/permissions/) in a guild channel. Emits a [Favorite Meme Create](/gateway/events/#favorite-meme-create) Gateway event.
|
||||
|
||||
A guild caller without [READ_MESSAGE_HISTORY](/http-api/permissions/) can select media only from a message on or after the guild's message history cutoff. Attachments and embeds in the message's forwarded snapshots are selectable alongside its own.
|
||||
A guild caller without [READ_MESSAGE_HISTORY](/http-api/permissions/) can select media only from a message on or after the guild's [message history cutoff](/http-api/messages/). Attachments and embeds in the message's forwarded snapshots are selectable alongside its own.
|
||||
|
||||
### Path parameters
|
||||
|
||||
@@ -288,7 +288,7 @@ A guild caller that can view the channel but lacks `READ_MESSAGE_HISTORY` receiv
|
||||
|
||||
### Side effects
|
||||
|
||||
The meme gets its own copy of the selected asset. Reading the source message renews its attachment decay deadline when attachment decay is enabled. Its content is otherwise unchanged.
|
||||
The meme gets its own copy of the selected asset. Reading the source message renews its attachment decay deadline when attachment decay is enabled. The source message is otherwise unchanged.
|
||||
|
||||
The operation emits one [Favorite Meme Create](/gateway/events/#favorite-meme-create) with the created object to the caller's own sessions.
|
||||
|
||||
@@ -421,7 +421,7 @@ A client uses it to turn stored URL-only favourite GIFs into renderable picker e
|
||||
| --- | --- | --- |
|
||||
| Accept-Language?<sup>1</sup> | string | The preferred locale used for a GIF provider lookup |
|
||||
|
||||
<sup>1</sup> Resolved against the [supported locale registry](/topics/locales/#supported-locales), replaced by the account's own stored locale, and defaulting to `en-US`
|
||||
<sup>1</sup> Fluxer uses the account's stored locale when it has one. Otherwise it matches this header against the [supported locale registry](/topics/locales/#supported-locales), and it falls back to `en-US`
|
||||
|
||||
Fluxer derives a two-letter country from the requesting address by geolocation, with `US` as the fallback.
|
||||
|
||||
@@ -445,7 +445,7 @@ An unresolved URL still returns an entry with its signed proxy URL and an empty
|
||||
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | response body | Every URL was resolved |
|
||||
| 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` |
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ A text-bearing channel is a [channel type](/http-api/channels/#channel-types) th
|
||||
|
||||
Fluxer resolves the channel before it applies any per-route authorisation. A private channel returns 404 `UNKNOWN_CHANNEL` to a caller who is not a recipient. A guild channel returns 403 `MISSING_PERMISSIONS` to a non-member and to a member without [VIEW_CHANNEL](/http-api/permissions/). A guild an operator has marked unavailable returns 403 `MISSING_ACCESS` before the route runs. A guild that no Gateway process serves returns 404 `UNKNOWN_GUILD`, or 403 `ACCESS_DENIED` when its stored record survives.
|
||||
|
||||
The message read, create, modify, and delete routes, [Bulk delete messages](#bulk-delete-messages), [Bulk delete own messages](#bulk-delete-own-messages), and [List pinned messages](#list-pinned-messages) also enforce age verification on an effectively age restricted guild channel. They return 403 `NSFW_CONTENT_AGE_RESTRICTED` until the account satisfies it. The attachment routes, the pin routes, the reaction routes, and [Indicate typing](#indicate-typing) resolve the channel without that check.
|
||||
The message read, create, modify, and delete routes, [Bulk delete messages](#bulk-delete-messages), [Bulk delete own messages](#bulk-delete-own-messages), and [List pinned messages](#list-pinned-messages) also enforce age verification on an age restricted guild channel. A guild channel is age restricted when its own setting says so. A channel with no setting of its own uses its parent category's setting, and then the guild's. They return 403 `NSFW_CONTENT_AGE_RESTRICTED` until the account satisfies it. The attachment routes, the pin routes, the reaction routes, and [Indicate typing](#indicate-typing) resolve the channel without that check.
|
||||
|
||||
[Clear channel read state](#clear-channel-read-state) and [Acknowledge message](#acknowledge-message) resolve no channel. [Acknowledge pins](#acknowledge-pins) reads the channel without checking the caller's access. None of the three checks a permission.
|
||||
|
||||
@@ -61,7 +61,7 @@ A message object is the full stored form of one post in a channel.
|
||||
|
||||
<sup>4</sup> Empty while `SUPPRESS_EMBEDS` is set on the message
|
||||
|
||||
<sup>5</sup> An attachment that a resolved embed owns is excluded from this array and is exposed through that embed's media object instead
|
||||
<sup>5</sup> An attachment that a [rich embed input](#rich-embed-input-objects) names with an `attachment://` URL in `image` or `thumbnail` is excluded from this array. The attachment's URL is in that embed's media object instead
|
||||
|
||||
<sup>6</sup> Echoed only in the create message response and its originating [Message Create](/gateway/events/#message-create) Gateway Dispatch, and never stored on the message
|
||||
|
||||
@@ -69,7 +69,7 @@ A message object is the full stored form of one post in a channel.
|
||||
|
||||
A webhook-authored message has no stored author user. Fluxer builds its author from the webhook, with the webhook ID as `id`, the stored webhook name as `username`, the discriminator `0000`, the stored webhook avatar hash as `avatar`, and `bot` true. A message whose stored author ID no longer resolves to an account is served with a deleted-user placeholder. The placeholder has that ID, the discriminator `0000`, and no avatar.
|
||||
|
||||
`mentions`, `mention_roles`, `embeds`, `attachments`, and `stickers` are always present and can be empty. Every optional field above is omitted entirely, and `referenced_message` is the one that can instead be present and null. A message whose author user and webhook are both absent is omitted from every read.
|
||||
`mentions`, `mention_roles`, `embeds`, `attachments`, and `stickers` are always present and can be empty. A field marked `?` above is omitted from the object when it has no value. `referenced_message` is the one optional field that can also be present and null. A message whose author user and webhook are both absent is omitted from every read.
|
||||
|
||||
:::note[A forward has immutable copies in `message_snapshots`]
|
||||
A reply reference resolves into `referenced_message`, and a forward has none. Fluxer captures the snapshots at forward time, and they never follow later edits to the source. [Message reference types](#message-reference-types) tells the two apart.
|
||||
@@ -131,7 +131,7 @@ A reply reference resolves into `referenced_message`, and a forward has none. Fl
|
||||
|
||||
<sup>1</sup> The only flag a guild moderator can change on a message they did not author
|
||||
|
||||
<sup>2</sup> Setting this flag binds the message to the voice message contract described under [Create message](#create-message)
|
||||
<sup>2</sup> A message with this flag has the voice message restrictions listed under [Create message](#create-message)
|
||||
|
||||
These bits are also the complete sendable set. A create masks the supplied value down to them, and a modify replaces only them and leaves every other stored bit untouched. A bit outside this table is discarded, and the request still succeeds.
|
||||
|
||||
@@ -462,7 +462,7 @@ Every optional field above is omitted entirely.
|
||||
|
||||
## Embed provider object
|
||||
|
||||
Fluxer sets a provider only from resolved embed metadata.
|
||||
Only an embed the unfurler builds from a link has a provider. A rich embed input cannot set one.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -660,7 +660,7 @@ One declared upload inside a [Request attachment upload URLs](#request-attachmen
|
||||
|
||||
<sup>1</sup> A decimal string is converted to the integer
|
||||
|
||||
<sup>2</sup> The issued capability and the stored attachment both use the media type derived from `filename`
|
||||
<sup>2</sup> The issued upload URL and the stored attachment both use the media type derived from `filename`
|
||||
|
||||
### Singlepart attachment upload object
|
||||
|
||||
@@ -680,7 +680,7 @@ Returned when the declared `file_size` is at most 10 MiB.
|
||||
|
||||
<sup>1</sup> This value is the `upload_filename` of a [pre-uploaded attachment](#pre-uploaded-attachment-object) in a later [Create message](#create-message) or [Modify message](#modify-message) request
|
||||
|
||||
<sup>2</sup> The capability is bound to the derived media type and to exactly `file_size` bytes, so a `PUT` of any other length is rejected
|
||||
<sup>2</sup> The upload URL accepts only the derived media type and exactly `file_size` bytes, so a `PUT` of any other length is rejected
|
||||
|
||||
### Multipart attachment upload object
|
||||
|
||||
@@ -698,7 +698,7 @@ Returned when the declared `file_size` exceeds 10 MiB.
|
||||
| upload_mode | string | Always `multipart` |
|
||||
| upload_id | string | Object storage multipart upload identifier of 1 through 1,024 characters |
|
||||
| part_size<sup>1</sup> | integer | Byte size of every part except the last |
|
||||
| parts | array[[attachment upload part](#attachment-upload-part-object) object] | Presigned part capabilities in ascending part number (1-10,000 entries) |
|
||||
| parts | array[[attachment upload part](#attachment-upload-part-object) object] | Presigned part upload URLs in ascending part number (1-10,000 entries) |
|
||||
|
||||
<sup>1</sup> The plan targets 20 parts, so `part_size` is one twentieth of `file_size` rounded up to a whole mebibyte, and it is never smaller than 10 MiB. The last part is the remainder
|
||||
|
||||
@@ -713,7 +713,7 @@ One presigned part of a multipart upload plan.
|
||||
| part_number | integer | One-based part number |
|
||||
| upload_url<sup>1</sup> | string | Presigned URL that accepts exactly this part |
|
||||
|
||||
<sup>1</sup> Each capability is bound to the exact byte count of its part, so a `PUT` of any other length is rejected
|
||||
<sup>1</sup> Each part upload URL accepts only the exact byte count of its part, so a `PUT` of any other length is rejected
|
||||
|
||||
### Multipart upload completion item object
|
||||
|
||||
@@ -758,7 +758,7 @@ The page has no cursor field. The next request repeats the operation with `befor
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| items | array[[partial user](/http-api/users/#partial-user-object) object] | Users in ascending cursor order |
|
||||
| items | array[[partial user](/http-api/users/#partial-user-object) object] | Users in ascending user ID order |
|
||||
| has_more | boolean | Whether another page exists |
|
||||
| next_after<sup>1</sup> | ?snowflake | User ID cursor for the next page |
|
||||
|
||||
@@ -842,7 +842,7 @@ Returns a bulk message response object holding bounded message windows from seve
|
||||
### Limitations
|
||||
|
||||
- This is a user-only operation, and a bot credential is refused with 403 `ACCESS_DENIED`.
|
||||
- Fluxer resolves and authorises each requested channel independently, under exactly the same boundary as [List channel messages](#list-channel-messages).
|
||||
- Fluxer resolves and authorises each requested channel independently, with exactly the same channel checks as [List channel messages](#list-channel-messages).
|
||||
- An inaccessible channel fails the whole request, so the response has no window for any channel.
|
||||
|
||||
### JSON body
|
||||
@@ -915,7 +915,7 @@ Plans from 1 through 10 attachment uploads. Returns a [singlepart](#singlepart-a
|
||||
- The channel must support messages, and one that does not fails with 400 `CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL`.
|
||||
- A guild channel requires [SEND_MESSAGES](/http-api/permissions/) and [ATTACH_FILES](/http-api/permissions/), and a timed-out member is refused with 403 `COMMUNICATION_DISABLED`.
|
||||
|
||||
Fluxer checks each declared `file_size` separately against the attachment size limit it resolves for the caller and the guild context. A declaration above that limit returns 400 `FILE_SIZE_TOO_LARGE` with the resolved ceiling. The resolved ceiling defaults to 26214400 bytes, the 25 MiB non-premium allowance, and 524288000 bytes, the 500 MiB premium allowance. A bot credential is clamped to 52428800 bytes, the 50 MiB bot ceiling, even when the resolved limit is higher.
|
||||
Fluxer checks each declared `file_size` separately against the attachment size limit it resolves for the caller and the guild context. A declaration above that limit returns 400 `FILE_SIZE_TOO_LARGE` with the resolved ceiling. By default the resolved ceiling is 26214400 bytes (25 MiB) for a caller without premium and 524288000 bytes (500 MiB) for a caller with premium. A bot credential is clamped to 52428800 bytes, the 50 MiB bot ceiling, even when the resolved limit is higher.
|
||||
|
||||
### Path parameters
|
||||
|
||||
@@ -1028,7 +1028,7 @@ Creates a message from a JSON body or from multipart form data. Returns the crea
|
||||
### Limitations
|
||||
|
||||
- The channel must be a text-bearing type.
|
||||
- A guild channel requires [VIEW_CHANNEL](/http-api/permissions/) and [SEND_MESSAGES](/http-api/permissions/), an untimed-out membership, and any membership verification the guild requires.
|
||||
- A guild channel requires [VIEW_CHANNEL](/http-api/permissions/) and [SEND_MESSAGES](/http-api/permissions/), an untimed-out membership, and a membership that meets the guild's [verification level](/http-api/guilds/#verification-levels).
|
||||
- A timed-out member is refused with 403 `COMMUNICATION_DISABLED`.
|
||||
- Embeds require [EMBED_LINKS](/http-api/permissions/), attachments require [ATTACH_FILES](/http-api/permissions/), a favourite meme requires both, and active everyone mentions require [MENTION_EVERYONE](/http-api/permissions/).
|
||||
- Slowmode applies to a non-bot caller unless they hold [BYPASS_SLOWMODE](/http-api/permissions/).
|
||||
@@ -1061,7 +1061,7 @@ The request body is read as a multipart form when `Content-Type` contains `multi
|
||||
|
||||
<sup>1</sup> Echoed back on the created message and on its [Message Create](/gateway/events/#message-create) Dispatch so a client can match the result to its optimistic entry. An integer is converted to its decimal string form
|
||||
|
||||
<sup>2</sup> The schema sets no length bound. Exceeding the effective one returns 400 `INVALID_FORM_BODY` with `CONTENT_EXCEEDS_MAX_LENGTH` on the path `content`
|
||||
<sup>2</sup> The schema sets no length bound. Exceeding the effective `max_message_length` described below returns 400 `INVALID_FORM_BODY` with `CONTENT_EXCEEDS_MAX_LENGTH` on the path `content`
|
||||
|
||||
<sup>3</sup> Fluxer keeps only the bits in [message flags](#message-flags) and silently drops every other bit
|
||||
|
||||
@@ -1091,7 +1091,7 @@ A nonce is remembered for the authenticated identity for five minutes, so a clie
|
||||
|
||||
Reusing a nonce in the same channel inside that window returns the message the first request created. Reusing it in a different channel fails with 404 `UNKNOWN_MESSAGE`. The replay check runs after authorisation and body validation, so a retry that is otherwise invalid still fails, and a retry after the window has passed creates a second message.
|
||||
|
||||
A reply applies the caller's message history cutoff to its target, and a forward applies none to its source. A reply whose target is a system message fails with the field code `CANNOT_REPLY_TO_SYSTEM_MESSAGE`, and a reference that resolves to no message fails with 404 `UNKNOWN_MESSAGE`.
|
||||
A reply from a guild caller without [READ_MESSAGE_HISTORY](/http-api/permissions/) fails with 404 `UNKNOWN_MESSAGE` when its target was created before the guild's message history cutoff, or when the guild has no cutoff. A forward has no cutoff check on its source. A reply whose target is a system message fails with the field code `CANNOT_REPLY_TO_SYSTEM_MESSAGE`, and a reference that resolves to no message fails with 404 `UNKNOWN_MESSAGE`.
|
||||
|
||||
Every `FORWARD` reference has `channel_id`. One that does not is refused by body validation before the operation runs, with 400 `INVALID_MESSAGE_DATA` on a JSON body and a per-field 400 `INVALID_FORM_BODY` on a multipart body.
|
||||
|
||||
@@ -1127,7 +1127,7 @@ A body that cannot be parsed as a multipart form fails with the field code `FAIL
|
||||
| More than one file under one index | `MULTIPLE_FILES_FOR_INDEX_NOT_ALLOWED` |
|
||||
| More files than the resolved limit | `TOO_MANY_FILES` |
|
||||
|
||||
Each is a field code inside a 400 `INVALID_FORM_BODY` body. A `FILE_INDEX_EXCEEDS_MAXIMUM` element reports a `maxIndex` member, 10000 against the absolute bound and the resolved limit minus one otherwise.
|
||||
Each is a field code inside a 400 `INVALID_FORM_BODY` body. A `FILE_INDEX_EXCEEDS_MAXIMUM` element has a `maxIndex` member. It is 10000 when the index is below 0 or above 10000, and the resolved limit minus one when the index is at or above that limit.
|
||||
|
||||
The `attachments` array inside `payload_json` uses [direct multipart attachment metadata](#direct-multipart-attachment-metadata-object), and an entry with `upload_filename` is instead read as a [pre-uploaded attachment](#pre-uploaded-attachment-object). An entry whose `id` matches a supplied file index supplies that file's metadata, and two entries claiming the same file fail with the field code `DUPLICATE_ATTACHMENT_IDS_NOT_ALLOWED`.
|
||||
|
||||
@@ -1346,7 +1346,7 @@ Deletes one attachment from the caller's own message. Returns 204 with an empty
|
||||
|
||||
- Only the message author can use this operation, and a guild moderator must use [Modify message](#modify-message) instead.
|
||||
- This route cannot address an attachment that a resolved embed owns.
|
||||
- When the attachment was the message's last remaining payload, the [Delete message](#delete-message) effects apply.
|
||||
- When the attachment is the message's only attachment and the message has no content, embeds, or stickers, Fluxer deletes the whole message with the [Delete message](#delete-message) effects.
|
||||
|
||||
### Path parameters
|
||||
|
||||
@@ -1370,7 +1370,7 @@ The response is 204 whether the message survives or is deleted, so the emitted G
|
||||
|
||||
### Side effects
|
||||
|
||||
The operation permanently deletes the stored attachment object, purges it from media delivery, removes it from the message, and advances the message's edit timestamp. It then emits [Message Update](/gateway/events/#message-update) to every session that can see the channel. When the removal leaves the message with no payload, the complete [Delete message](#delete-message) effect set applies instead, including its Dispatches and guild audit log entry.
|
||||
The operation permanently deletes the stored attachment object, purges it from media delivery, removes it from the message, and advances the message's edit timestamp. It then emits [Message Update](/gateway/events/#message-update) to every session that can see the channel. When the removed attachment was the message's only attachment and the message has no content, embeds, or stickers, Fluxer deletes the whole message instead, with the complete [Delete message](#delete-message) effect set, including its Dispatches and guild audit log entry.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -1539,7 +1539,7 @@ The deletion has no age boundary and no selection. There is no cancellation and
|
||||
|
||||
### Side effects
|
||||
|
||||
Each matching message, with its attachments and reactions, is permanently deleted and removed from search. Deletions emit batched [Message Delete Bulk](/gateway/events/#message-delete-bulk) Dispatches.
|
||||
Each matching message, with its attachments and reactions, is permanently deleted and removed from search. Fluxer emits one [Message Delete Bulk](/gateway/events/#message-delete-bulk) Dispatch for each batch of at most 100 deleted messages.
|
||||
|
||||
The operation emits no [Channel Pins Update](/gateway/events/#channel-pins-update) and no individual [Message Delete](/gateway/events/#message-delete), and it writes no guild audit log entry.
|
||||
|
||||
@@ -1810,7 +1810,7 @@ Adds the authenticated identity's reaction. Returns 204 with an empty body. Emit
|
||||
|
||||
### Limitations
|
||||
|
||||
- The caller must be able to view the text-bearing channel, must not be timed out, and must reach the message under the message history cutoff.
|
||||
- The caller must be able to view the text-bearing channel, must not be timed out. A guild caller without [READ_MESSAGE_HISTORY](/http-api/permissions/) can react only to a message on or after the guild's message history cutoff, and to no message when the guild has no cutoff.
|
||||
- A non-bot caller must have a verified email, and one that does not receives 403 `REACTION_EMAIL_VERIFICATION_REQUIRED`.
|
||||
- Creating a new emoji reaction group in a guild requires [ADD_REACTIONS](/http-api/permissions/), while adding to an existing group does not.
|
||||
- A custom emoji whose source guild is not the channel's guild requires the `feature_global_expressions` entitlement, and a caller without it receives the field code `CUSTOM_EMOJIS_REQUIRE_PREMIUM_OUTSIDE_SOURCE`.
|
||||
@@ -1836,7 +1836,7 @@ Adds the authenticated identity's reaction. Returns 204 with an empty body. Emit
|
||||
<sup>1</sup> See [reaction session correlation](#reaction-emoji-path-value)
|
||||
|
||||
:::note[Permission is checked only for a new group]
|
||||
`ADD_REACTIONS`, Unicode emoji validation, custom emoji existence, and the external emoji checks apply only when the request would open a new group. Joining an existing group is authorised by channel access alone.
|
||||
`ADD_REACTIONS`, Unicode emoji validation, custom emoji existence, and the external emoji checks apply only when the request would open a new group. Joining an existing group skips them. Every other check in the list above still runs, including the timeout, the message history cutoff, and email verification.
|
||||
:::
|
||||
|
||||
[Reaction object](#reaction-object) states the group and user ceilings for a message, and reaching either returns 400 `MAX_REACTIONS`.
|
||||
|
||||
@@ -10,7 +10,7 @@ OAuth2 is how a user grants an application access to their account. The same con
|
||||
|
||||
## Authorisation code grant
|
||||
|
||||
The grant begins at [Authorise application](#authorise-application). That route redirects the browser to the Fluxer consent interface, unless the caller asked for a silent authorisation. [Grant OAuth2 consent](#grant-oauth2-consent) records the decision, issues one authorisation code, and returns the callback URL that has it. The client presents that code to [Exchange OAuth2 token](#exchange-oauth2-token) with its client credentials and receives an access and refresh token pair.
|
||||
The grant begins at [Authorise application](#authorise-application). That route redirects the browser to the Fluxer consent interface, unless the request sets `prompt=none`. [Grant OAuth2 consent](#grant-oauth2-consent) records the decision, issues one authorisation code, and returns the callback URL that has it. The client presents that code to [Exchange OAuth2 token](#exchange-oauth2-token) with its client credentials and receives an access and refresh token pair.
|
||||
|
||||
Proof Key for Code Exchange, or PKCE, supports the `S256` and `plain` challenge methods, and Fluxer binds the challenge to the code when it issues the code. A `code_challenge` supplied without a `code_challenge_method` is treated as `plain`. An exchange for a code that has a challenge supplies a matching `code_verifier`, and an exchange for a code that has none ignores any verifier it is given.
|
||||
|
||||
@@ -18,11 +18,11 @@ Proof Key for Code Exchange, or PKCE, supports the `S256` and `plain` challenge
|
||||
|
||||
Treat authorisation codes, client secrets, and tokens as opaque credentials. Retrying an interrupted exchange with the same inputs is rejected as an invalid grant. Restart the grant instead.
|
||||
|
||||
An authorisation code expires 10 minutes after it is issued, an access token after 7 days, and a refresh token after 30 days. Every refresh exchange issues a replacement with a new 30 day life, so a grant ends 30 days after its last refresh.
|
||||
An authorisation code expires 10 minutes after it is issued, an access token after 7 days, and a refresh token after 30 days. Every refresh exchange issues a new refresh token that expires 30 days after it is issued, so a grant ends 30 days after its last refresh.
|
||||
|
||||
## Route authentication
|
||||
|
||||
Every route with a delegated identity 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`, 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.
|
||||
|
||||
## OAuth2 string normalisation
|
||||
|
||||
@@ -58,7 +58,7 @@ An unrecognised scope value is rejected when the authorisation request is comple
|
||||
Scope order has no semantic meaning, so a client MUST compare scopes as a set.
|
||||
:::
|
||||
|
||||
Fluxer sorts the `scope` string of an [OAuth2 token](#oauth2-token-object) and of an [OAuth2 introspection](#oauth2-introspection-object) into the registry order above. The `scopes` array of a [current OAuth2 authorisation](#current-oauth2-authorisation-object) has the stored scopes in no defined order, with unrecognised values removed. The `scopes` array of an [OAuth2 authorisation](#oauth2-authorisation-object) is in no defined order, and it can hold `bot` and an unrecognised value once more than one refresh token contributes.
|
||||
Fluxer sorts the `scope` string of an [OAuth2 token](#oauth2-token-object) and of an [OAuth2 introspection](#oauth2-introspection-object) into the registry order above. The `scopes` array of a [current OAuth2 authorisation](#current-oauth2-authorisation-object) has the stored scopes in no defined order, with unrecognised values removed. The `scopes` array of an [OAuth2 authorisation](#oauth2-authorisation-object) is in no defined order, and it can hold `bot` or an unrecognised value when the user holds more than one live refresh token for the application.
|
||||
|
||||
## OAuth2 error response object
|
||||
|
||||
@@ -73,7 +73,7 @@ The failure envelope the OAuth2 routes return.
|
||||
|
||||
<sup>1</sup> Always present, and some descriptions are a bare API error code identifier
|
||||
|
||||
These members are the whole body, so the routine that reads the ordinary [error response](/http-api/#error-response) cannot parse it. Fluxer returns every OAuth2 failure with status 400. A client MUST key on `error` and never on `error_description`.
|
||||
These members are the whole body, so the body has none of the `code` and `message` members of the ordinary [error response](/http-api/#error-response). Fluxer returns every OAuth2 failure with status 400. A client MUST key on `error` and never on `error_description`.
|
||||
|
||||
The consent, token, introspection, and revocation routes return this object for a grant, client authentication, scope, redirect, response type, or permission mask failure. Every other failure, including request validation, authentication, authorisation, and rate limiting, uses the ordinary [error response](/http-api/#error-response) with an [API error code](/http-api/errors/).
|
||||
|
||||
@@ -191,7 +191,7 @@ The liveness and scope report Fluxer returns for one presented token.
|
||||
|
||||
Apart from `username` and `exp`, every optional field above is present when `active` is true and absent when it is false. An active access token has `exp` and an active refresh token does not.
|
||||
|
||||
Fluxer reports an unknown token, an expired token, and a token issued to another application as inactive. An inactive body has only `active` set to false, and names no reason. Introspection never resolves the account behind the token, so an active result reports on the token alone.
|
||||
Fluxer reports an unknown token, an expired token, and a token issued to another application as inactive. An inactive body has only `active` set to false, and names no reason. Fluxer does not look up the user account during introspection, so an active result describes the token only and says nothing about the state of that account.
|
||||
|
||||
## Current OAuth2 authorisation object
|
||||
|
||||
@@ -206,7 +206,7 @@ The application, scopes, and expiry behind the access token the caller presented
|
||||
| expires | ISO8601 timestamp | The expiry time of the access token |
|
||||
| user?<sup>2</sup> | [OAuth2 authorised user](#oauth2-authorised-user-object) object | The account the access token was issued for |
|
||||
|
||||
<sup>1</sup> Emitted in no defined order, and `bot` is retained here even though it grants no delegated read
|
||||
<sup>1</sup> Emitted in no defined order, and `bot` stays in the array even though it grants no bearer access
|
||||
|
||||
<sup>2</sup> Present only when the token grants `identify` and the account still exists, so a token restricted to `bot` never has one
|
||||
|
||||
@@ -295,7 +295,7 @@ The application named by an [OAuth2 authorisation](#oauth2-authorisation-object)
|
||||
|
||||
<sup>1</sup> Null when the application has no bot account or that account has no avatar. The stored hash is reported unfiltered, so an animated hash retains its `a_` prefix
|
||||
|
||||
<sup>2</sup> An application has no independent description
|
||||
<sup>2</sup> An application record has no description field, so this value is always null
|
||||
|
||||
## OAuth2 consent response object
|
||||
|
||||
@@ -309,7 +309,7 @@ The callback URL [Grant OAuth2 consent](#grant-oauth2-consent) returns after it
|
||||
|
||||
<sup>1</sup> The URL has the `code` query parameter, and the normalised caller `state` when one was supplied
|
||||
|
||||
Fluxer builds the URL from the registered redirect URI the request resolved to, which is the Fluxer application endpoint when a bot-only authorisation supplied none. The code records the URL serialisation of that resolved URI, and the client later presents that exact serialisation to [Exchange OAuth2 token](#exchange-oauth2-token).
|
||||
Fluxer builds the URL from the registered redirect URI the request resolved to, which is the configured Fluxer web app URL when a bot-only authorisation supplied none. The code records the URL serialisation of that resolved URI, and the client later presents that exact serialisation to [Exchange OAuth2 token](#exchange-oauth2-token).
|
||||
|
||||
## Authorise application
|
||||
|
||||
@@ -336,7 +336,7 @@ Starts an authorisation request. Fluxer returns a redirect for every query that
|
||||
|
||||
<sup>1</sup> The value must parse as an absolute URL with a scheme and a host, and must match a registered redirect URI before a code is issued
|
||||
|
||||
<sup>2</sup> Both values are forwarded to the consent interface unchanged, and their mutual exclusion is enforced by [Grant OAuth2 consent](#grant-oauth2-consent)
|
||||
<sup>2</sup> Both values are forwarded to the consent interface unchanged, and only [Grant OAuth2 consent](#grant-oauth2-consent) rejects a request that supplies both
|
||||
|
||||
<sup>3</sup> The value is forwarded to the consent interface unchanged and is parsed as an integer only by [Grant OAuth2 consent](#grant-oauth2-consent)
|
||||
|
||||
@@ -360,7 +360,7 @@ An error redirect has `error` and the normalised `state` when the caller supplie
|
||||
The error redirect targets the supplied `redirect_uri` only when the named application has registered that exact string, so this route cannot bounce a browser to an arbitrary origin.
|
||||
:::
|
||||
|
||||
Fluxer compares the two strings byte for byte, so a differing scheme, port, trailing slash, query, or fragment does not match. Every other case redirects to the Fluxer application endpoint. That covers an omitted redirect URI, an unknown application, and an application with no registered URI at all.
|
||||
Fluxer compares the two strings byte for byte, so a differing scheme, port, trailing slash, query, or fragment does not match. Every other case redirects to the configured Fluxer web app URL. That covers an omitted redirect URI, an unknown application, and an application with no registered URI at all.
|
||||
|
||||
### Side effects
|
||||
|
||||
@@ -447,7 +447,7 @@ The request body is `application/x-www-form-urlencoded` or `multipart/form-data`
|
||||
A refresh exchange consumes the presented refresh token, so a client that loses the response MUST restart the authorisation code grant.
|
||||
:::
|
||||
|
||||
Earlier access tokens for the same user and application survive a refresh and expire on their own schedule.
|
||||
Access tokens issued earlier for the same user and application stay valid after a refresh. Each one expires 7 days after it is issued.
|
||||
|
||||
### Request headers
|
||||
|
||||
@@ -619,11 +619,11 @@ Revoking a token deletes every access token and every refresh token its user hol
|
||||
| client_id?<sup>2</sup> | snowflake | The client ID, supplied when HTTP Basic is not used |
|
||||
| client_secret?<sup>2</sup> | string | The client secret, supplied when HTTP Basic is not used (1-256 UTF-16 code units after [normalisation](#oauth2-string-normalisation)) |
|
||||
|
||||
<sup>1</sup> Only `refresh_token` matches the presented value against refresh tokens, and access tokens are still tried when no refresh token matches. An absent hint and an `access_token` hint match access tokens only
|
||||
<sup>1</sup> A `refresh_token` hint makes Fluxer look for a matching refresh token first, then for a matching access token when no refresh token matches. An absent hint and an `access_token` hint make Fluxer look for a matching access token only
|
||||
|
||||
<sup>2</sup> Supplying either field together with a well-formed HTTP Basic `Authorization` header is rejected as `invalid_request`
|
||||
|
||||
A client that presents a refresh token MUST send `token_type_hint=refresh_token`, or the revocation takes no effect.
|
||||
A client that presents a refresh token MUST send `token_type_hint=refresh_token`, or Fluxer matches no token and deletes nothing.
|
||||
|
||||
### Response
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ Every bit Fluxer defines appears below.
|
||||
| 1 << 13 | MANAGE_MESSAGES<sup>4</sup> | Delete another member's message, bulk delete messages, remove another member's reaction, and edit the flags and attachments of another member's message |
|
||||
| 1 << 14 | EMBED_LINKS | Have a link in the caller's own message expanded into an embed |
|
||||
| 1 << 15 | ATTACH_FILES | Attach a file to a message |
|
||||
| 1 << 16 | READ_MESSAGE_HISTORY | Read messages that were sent before the current access began |
|
||||
| 1 << 16 | READ_MESSAGE_HISTORY | Read messages sent before the guild's `message_history_cutoff`, or any message when the guild has no cutoff |
|
||||
| 1 << 17 | MENTION_EVERYONE | Use the everyone and here mentions in a guild channel |
|
||||
| 1 << 18 | USE_EXTERNAL_EMOJIS | Use an emoji owned by another guild in a message or a reaction |
|
||||
| 1 << 20 | CONNECT | Connect to a guild voice channel |
|
||||
@@ -105,7 +105,7 @@ An operation that asserts an elevated permission the caller holds but cannot use
|
||||
[List guild roles](#list-guild-roles) asserts no permission, so it never produces `TWO_FACTOR_REQUIRED`.
|
||||
|
||||
:::note[The reported mask keeps every elevated bit]
|
||||
Elevation gates the operation. A `permissions` field reports what the caller's roles grant, so a client hiding an action must compare the guild [MFA level](/http-api/guilds/#mfa-levels) and its own enrolment state.
|
||||
The MFA requirement blocks the operation and leaves the mask unchanged. A `permissions` field reports what the caller's roles grant, so a client hiding an action must compare the guild [MFA level](/http-api/guilds/#mfa-levels) and its own enrolment state.
|
||||
:::
|
||||
|
||||
## Permission computation
|
||||
@@ -158,7 +158,7 @@ A caller manages a role when the caller's rank is strictly above the role's rank
|
||||
Fluxer enforces hierarchy independently of `MANAGE_ROLES`. A caller who holds the permission without outranking the target role receives 403 `MISSING_PERMISSIONS`. A caller whose only `MANAGE_ROLES` comes from the everyone role has no assigned role, so they can manage no role.
|
||||
|
||||
:::caution[A non-owner cannot grant a permission they lack]
|
||||
A requested bit outside the caller's own effective guild permissions fails the whole request with `MISSING_PERMISSIONS`. Fluxer runs that check after it resolves the unassigned and feature-gated bits.
|
||||
A requested bit outside the caller's own effective guild permissions fails the whole request with `MISSING_PERMISSIONS`. Fluxer runs that check after it discards unassigned bits and handles feature-gated bits as [Feature-gated permission bits](#feature-gated-permission-bits) describes.
|
||||
:::
|
||||
|
||||
## Guild role object
|
||||
@@ -213,7 +213,7 @@ Each entry names one role and the rank the caller requests for it.
|
||||
| id | snowflake | The ID of the role |
|
||||
| position?<sup>1</sup> | integer | The rank the caller requests for the role, where a larger value ranks higher |
|
||||
|
||||
<sup>1</sup> Fluxer ranks a role whose entry omits the field by its stored `position` in the same sort, so a role that requested a position above that value still displaces it
|
||||
<sup>1</sup> Fluxer sorts a role whose entry omits the field by its stored `position`, in the same sort as the other entries. A role whose entry requests a higher position than that stored value is placed above it
|
||||
|
||||
### Example
|
||||
|
||||
@@ -304,7 +304,7 @@ Creates a role and returns its [guild role object](#guild-role-object). Requires
|
||||
| color? | integer | The colour of the role as a 24-bit RGB integer (0-16777215, default 0) |
|
||||
| permissions?<sup>1</sup> | decimal string \| integer | The [permission](#permissions) bitfield the role grants |
|
||||
|
||||
<sup>1</sup> An omitted field copies the current permissions of the everyone role unchanged. A supplied value is intersected with the set of defined bits, resolved against the caller's [feature declaration](#feature-gated-permission-bits), then checked against the caller's own permissions
|
||||
<sup>1</sup> An omitted field copies the current permissions of the everyone role unchanged. A supplied value is intersected with the set of defined bits, Fluxer clears `VIEW_CHANNEL_MEMBERS` unless the caller's [feature declaration](#feature-gated-permission-bits) includes `view_channel_members_permission`, then checks the value against the caller's own permissions
|
||||
|
||||
### Response
|
||||
|
||||
@@ -363,7 +363,7 @@ Every field is optional, and an omitted field leaves the stored value unchanged.
|
||||
|
||||
<sup>1</sup> The field is accepted and then ignored when the target is the everyone role, which cannot be renamed, hoisted, given a member list position, or made mentionable
|
||||
|
||||
<sup>2</sup> The supplied value becomes the complete stored mask. Fluxer intersects it with the set of defined bits and resolves it against the caller's [feature declaration](#feature-gated-permission-bits) before checking the caller's authority
|
||||
<sup>2</sup> The supplied value becomes the complete stored mask. Before it checks the caller's authority, Fluxer intersects it with the set of defined bits and keeps each feature-gated bit at its stored value unless the caller's [feature declaration](#feature-gated-permission-bits) names the feature for that bit
|
||||
|
||||
### Response
|
||||
|
||||
@@ -543,7 +543,7 @@ A deleted role cannot be restored, and its permissions, colour, and hoist state
|
||||
|
||||
### Side effects
|
||||
|
||||
The operation removes the role from every member that held it, removes the role record, and frees the guild role slot it occupied. It creates a [ROLE_DELETE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry with the supplied audit reason and emits [Guild Role Delete](/gateway/events/#guild-role-delete) to every session that can see the guild. Subsequent member reads and [Guild Member Update](/gateway/events/#guild-member-update) payloads reflect the removed role.
|
||||
The operation removes the role from every member that held it, removes the role record, and frees the guild role slot it occupied. It creates a [ROLE_DELETE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry with the supplied audit reason and emits [Guild Role Delete](/gateway/events/#guild-role-delete) to every session that can see the guild. The deleted role no longer appears in `roles` on a later member read or [Guild Member Update](/gateway/events/#guild-member-update) payload.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ The complete value is null in each of these cases.
|
||||
| No future phase | The schedule has no phase starting after now |
|
||||
| No target cycle | Neither the future phase price nor the schedule metadata resolves it |
|
||||
| No change | The resolved target billing cycle equals the current one |
|
||||
| Read failed | The payment provider read failed, which is logged and absorbed |
|
||||
| Read failed | The payment provider read failed, and the request still succeeds |
|
||||
|
||||
### Example
|
||||
|
||||
@@ -195,7 +195,7 @@ The complete value is null in each of these cases.
|
||||
|
||||
## Premium state object
|
||||
|
||||
The account's entitlement, the premium checks that apply to it, and the billing and pricing data behind both. [Get premium state](#get-premium-state) and [set premium perks disabled](#set-premium-perks-disabled) both return it.
|
||||
The account's entitlement, the effective entitlement that premium feature checks read, and the billing and pricing data behind both. [Get premium state](#get-premium-state) and [set premium perks disabled](#set-premium-perks-disabled) both return it.
|
||||
|
||||
A client gates a premium feature on `effective.is_premium`.
|
||||
|
||||
@@ -260,7 +260,7 @@ The effective state decides whether premium features are available. It differs f
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| is_premium<sup>1</sup> | boolean | Whether premium access checks are active |
|
||||
| is_premium<sup>1</sup> | boolean | Whether premium features are available to the account |
|
||||
| premium_type<sup>2</sup> | ?integer | The effective [premium type](#premium-types) |
|
||||
| premium_since<sup>2</sup> | ?ISO8601 timestamp | The effective time premium access started |
|
||||
| premium_until<sup>3</sup> | ?ISO8601 timestamp | The time premium access ends, including any stacked gift extension, or null for a lifetime entitlement |
|
||||
@@ -299,7 +299,7 @@ Billing details can lag behind changes made through the payment provider.
|
||||
| stripe_customer_id | ?string | The payment provider customer ID, or null when none was ever created |
|
||||
| current_subscription_price | ?[current subscription price](#current-subscription-price-object) object | The price the account is billed against |
|
||||
| pending_subscription_change | ?[pending subscription change](#pending-subscription-change-object) object | The scheduled billing cycle change |
|
||||
| subscription | ?[billing subscription](#billing-subscription-object) object | The most relevant mirrored subscription |
|
||||
| subscription | ?[billing subscription](#billing-subscription-object) object | The mirrored subscription Fluxer selects by the rule below this table |
|
||||
| invoices<sup>1</sup> | array[[billing invoice](#billing-invoice-object)] | The most recent invoices, newest first |
|
||||
| invoices_has_more<sup>2</sup> | boolean | Whether the account has invoices beyond the returned page |
|
||||
| payment_methods<sup>3</sup> | array[[billing payment method](#billing-payment-method-object)] | The stored payment methods |
|
||||
@@ -444,7 +444,7 @@ The checkout catalogue resolved for the request. A client can render prices with
|
||||
|
||||
<sup>1</sup> The request-time geolocation country, falling back to the `country_code` query value uppercased, or null when neither resolves
|
||||
|
||||
<sup>2</sup> Null unless the deployment configures a complete recurring pair and a complete gift pair for the currency preferences that apply, so one missing gift price nulls the whole catalogue. The amounts come from the mirrored price rows
|
||||
<sup>2</sup> Null unless the deployment configures a complete recurring pair and a complete gift pair for a currency in the currency preference order of `country_code`, which is the default order when `country_code` is null, so one missing gift price nulls the whole catalogue. The amounts come from the mirrored price rows
|
||||
|
||||
Fluxer reports an unresolvable catalogue as null here, and the request still succeeds. [Get price IDs](#get-price-ids) answers 400 `STRIPE_ERROR` for the same condition.
|
||||
|
||||
@@ -452,7 +452,7 @@ Fluxer reports an unresolvable catalogue as null here, and the request still suc
|
||||
|
||||
<RouteHeader method="GET" path="/v1/premium/price-ids" unauthenticated />
|
||||
|
||||
Returns the [price IDs](#price-ids-object) object for a country. A supplied credential only keys the rate limit, so an unrecognised credential falls back to the client IP address.
|
||||
Returns the [price IDs](#price-ids-object) object for a country. Fluxer uses a supplied credential only to count the request against that user's rate limit, so an unrecognised credential falls back to the client IP address.
|
||||
|
||||
The deployment must configure a complete recurring pair and a complete gift pair for at least one currency that the requested country prefers. Fluxer answers 400 `STRIPE_ERROR` when it does not.
|
||||
|
||||
@@ -484,7 +484,7 @@ Returns the [premium state](#premium-state-object) object for the authenticated
|
||||
The route is available even when no payment provider is configured.
|
||||
|
||||
:::note[One read serves the whole billing screen]
|
||||
`billing.current_subscription_price` and `billing.refund_eligibility` cover the same ground as [get current subscription price](#get-current-subscription-price) and [get refund eligibility](/http-api/billing/#get-refund-eligibility), and `billing.pending_subscription_change` has no route of its own.
|
||||
`billing.current_subscription_price` and `billing.refund_eligibility` are the same objects returned by [get current subscription price](#get-current-subscription-price) and [get refund eligibility](/http-api/billing/#get-refund-eligibility), and `billing.pending_subscription_change` has no route of its own.
|
||||
:::
|
||||
|
||||
:::note[Billing details may take time to update]
|
||||
@@ -508,7 +508,7 @@ The route is available even when no payment provider is configured.
|
||||
|
||||
### Side effects
|
||||
|
||||
The read can update the customer's default payment method to match the subscription. It does not change entitlement.
|
||||
The read can change a default payment method. When the customer has a default that differs from the subscription's, Fluxer sets the subscription's default to the customer's. When only the subscription has a default, Fluxer sets it as the customer's default. It does not change entitlement.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -568,7 +568,7 @@ When the requested value changes, every account session receives [User Update](/
|
||||
|
||||
<RouteHeader method="POST" path="/v1/premium/customer-portal" />
|
||||
|
||||
Creates a billing portal session for the authenticated account and returns a [redirect URL](/http-api/billing/#redirect-url-object) object. The returned URL is the only handle on the session.
|
||||
Creates a billing portal session for the authenticated account and returns a [redirect URL](/http-api/billing/#redirect-url-object) object. The object has no session identifier, so the returned URL is the only way to open the session.
|
||||
|
||||
An account that never had a payment provider customer receives 400 `STRIPE_NO_PURCHASE_HISTORY`. A deployment that configures no payment provider receives 400 `STRIPE_PAYMENT_NOT_AVAILABLE`, and a provider call that fails or answers without a URL receives 400 `STRIPE_ERROR`.
|
||||
|
||||
@@ -651,7 +651,7 @@ A deployment that configures no payment provider receives 400 `STRIPE_PAYMENT_NO
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer sets the subscription to cancel at the current period end, sets the account's cancellation flag, and refreshes the mirrored subscription row. Every account session receives [User Update](/gateway/events/#user-update). The ordinary path writes no premium end, so the recorded end is whatever the account already has.
|
||||
Fluxer sets the subscription to cancel at the current period end, sets the account's cancellation flag, and refreshes the mirrored subscription row. Every account session receives [User Update](/gateway/events/#user-update). For a subscription with no schedule, Fluxer writes no premium end, so the recorded end is whatever the account already has.
|
||||
|
||||
A subscription that has a schedule discards its pending billing cycle change and ends in cancellation at the current period end. Fluxer then rewrites the premium end from the refreshed subscription. Premium access continues through the recorded end, and [receive Stripe webhook](/http-api/billing/#receive-stripe-webhook) later applies the final downgrade.
|
||||
|
||||
@@ -722,7 +722,7 @@ A monthly to yearly change credits the first invoice of the new phase with the s
|
||||
|
||||
### Side effects
|
||||
|
||||
An immediate change clears pending cancellation, replaces the subscription price, and updates the billing cycle and premium end. A period end change clears pending cancellation and schedules the target cycle to begin at the current period end without proration. A monthly to yearly change adds the applicable credit to the scheduled phase.
|
||||
An immediate change clears pending cancellation, replaces the subscription price, and updates the billing cycle and premium end. A period end change clears pending cancellation and schedules the target cycle to begin at the current period end without proration. A monthly to yearly change credits the first invoice of the scheduled phase with the smaller of the two amounts, except during a trial.
|
||||
|
||||
A scheduled change becomes visible as `billing.pending_subscription_change` in [get premium state](#get-premium-state). Every account session receives [User Update](/gateway/events/#user-update). Requesting the already active cycle changes nothing.
|
||||
|
||||
@@ -754,7 +754,7 @@ A subscription set to cancel has no next billing period, so the switch would hav
|
||||
| 400<sup>1</sup> | [error response](/http-api/#error-response) | The payment provider is not configured, the account has no stored subscription, or the provider rejected the request |
|
||||
| 404 | [error response](/http-api/#error-response) | The authenticated account record no longer exists and the request returns `UNKNOWN_USER` |
|
||||
|
||||
<sup>1</sup> A refusal that the account can act on is reported as a 200 with `status` `ineligible`, not as an error
|
||||
<sup>1</sup> A missing payment provider returns `STRIPE_PAYMENT_NOT_AVAILABLE` and a missing stored subscription returns `STRIPE_NO_ACTIVE_SUBSCRIPTION`, so this route never returns the `feature_unavailable` or `no_active_subscription` reason. Every other refusal is a 200 with `status` `ineligible`
|
||||
|
||||
### Switch result
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ Only an acknowledgement produces a later Dispatch. A message acknowledgement emi
|
||||
|
||||
## Read state acknowledgement object
|
||||
|
||||
One entry in the body of [Acknowledge read states](#acknowledge-read-states). It names the channel whose entry changes, the message the account has read through, and the mention count that survives the read.
|
||||
One entry in the body of [Acknowledge read states](#acknowledge-read-states). It names the channel whose entry changes, the message the account has read through, and the mention count to store for the channel.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -69,7 +69,7 @@ One entry in the body of [Acknowledge read states](#acknowledge-read-states). It
|
||||
|
||||
<sup>2</sup> Fluxer does not resolve the message either. An entry equal to the stored watermark still produces a write, so it resets the stored mention count
|
||||
|
||||
<sup>3</sup> Fluxer stores the submitted value verbatim, so an acknowledgement leaving mentions above the watermark must send how many remain
|
||||
<sup>3</sup> Fluxer stores the submitted value verbatim, so a client that leaves unread mentions after `message_id` must send the number of those mentions
|
||||
|
||||
<sup>4</sup> With `manual` false an entry strictly below the stored watermark is discarded whole
|
||||
|
||||
@@ -135,13 +135,13 @@ When any entry is manual or has a positive mention count, entries for the same c
|
||||
|
||||
### Side effects
|
||||
|
||||
Each entry updates its channel watermark according to `manual` and sets the submitted mention count.
|
||||
With `manual` true, an entry sets the channel watermark to `message_id`. With `manual` false, Fluxer discards an entry strictly below the stored watermark and leaves the stored entry unchanged. Every entry that is not discarded sets the watermark to `message_id` and stores the submitted mention count.
|
||||
|
||||
One [Message ACK](/gateway/events/#message-ack) goes to the caller's sessions for every submitted entry, including an unchanged one. It reports the resulting `message_id`, `mention_count`, and `version`. It also includes the submitted `manual` value when any entry is manual or has a positive mention count.
|
||||
|
||||
A successful acknowledgement can have no corresponding Dispatch. Reconcile from a later read when needed.
|
||||
When Fluxer fails to send a Message ACK, the request still succeeds and no Dispatch arrives for that entry. A client reconciles from the entries this route returns or from a new Ready.
|
||||
|
||||
Every entry also clears delivered notifications for its channel through the submitted `message_id`, so a discarded or rewound entry clears a different range than the entry stores.
|
||||
Every entry also clears delivered push notifications for its channel through the submitted `message_id`. Fluxer uses the submitted value even when it discards the entry and keeps a higher stored watermark.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Fluxer copies the reported content when the report is submitted. A message repor
|
||||
|
||||
The Digital Services Act routes require no credential. Fluxer still resolves a valid credential presented anyway, so the request draws on that account's rate limit allowance.
|
||||
|
||||
Every route bucket is keyed by the authenticated user ID when a credential resolves and by client IP address otherwise. Report creation applies further policy allowances keyed by the reporter.
|
||||
Every route bucket is keyed by the authenticated user ID when a credential resolves and by client IP address otherwise. Report creation also limits how many reports one reporter creates each hour. The rate limit section of each route that creates a report lists those limits.
|
||||
|
||||
## Content screening
|
||||
|
||||
@@ -57,7 +57,7 @@ Fluxer returns this receipt when it records a submission.
|
||||
|
||||
<sup>1</sup> Always `pending`
|
||||
|
||||
<sup>2</sup> A Digital Services Act notice has its filing time, even when its verification code was issued much earlier
|
||||
<sup>2</sup> For a Digital Services Act notice, this is the time the notice was filed, even when its verification code was issued much earlier
|
||||
|
||||
### Example
|
||||
|
||||
@@ -250,7 +250,7 @@ A reporter without `READ_MESSAGE_HISTORY` can report only a message at or after
|
||||
|
||||
The operation writes one report with a frozen evidence snapshot. The snapshot records the reporter's account ID and email address, the message author's identity and avatar hash, the channel and guild identity, and the guild and channel NSFW and content warning state.
|
||||
|
||||
The snapshot also records a conversation window of up to 25 messages before the reported message, the reported message itself, and up to 25 messages after it. Each context message records its author identity, content, timestamps, type, flags, mentions, embeds, and sticker items, and each attachment is copied byte for byte. A context message whose author no longer exists is dropped from the window, and an attachment whose copy fails is dropped without failing the request. A reporter without `READ_MESSAGE_HISTORY` receives a window trimmed to the guild's message history cutoff.
|
||||
The snapshot also records a conversation window of up to 25 messages before the reported message, the reported message itself, and up to 25 messages after it. Each context message records its author identity, content, timestamps, type, flags, mentions, embeds, and sticker items, and each attachment is copied byte for byte. A context message whose author no longer exists is dropped from the window, and an attachment whose copy fails is dropped without failing the request. When the reporter lacks `READ_MESSAGE_HISTORY`, the window holds only messages created at or after the guild's message history cutoff, and holds no messages when the guild sets no cutoff.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -503,7 +503,7 @@ An unknown code returns 404 `UNKNOWN_INVITE`. A code that resolves to an invite
|
||||
| Status | Body | Condition |
|
||||
| --- | --- | --- |
|
||||
| 200 | [report](#report-object) object | The notice was recorded |
|
||||
| 400 | [error response](/http-api/#error-response) | The ticket is unknown or expired and the request returns `INVALID_DSA_TICKET`, or the target could not be resolved to the reported subject and the request returns `INVALID_DSA_REPORT_TARGET` |
|
||||
| 400 | [error response](/http-api/#error-response) | The ticket is unknown or expired and the request returns `INVALID_DSA_TICKET`, or a supplied Fluxer tag or invite code is malformed or does not match the reported message author, user, or guild and the request returns `INVALID_DSA_REPORT_TARGET` |
|
||||
| 403 | [error response](/http-api/#error-response) | A body string is blocked by content moderation and the request returns `CONTENT_BLOCKED` |
|
||||
| 404 | [error response](/http-api/#error-response) | The referenced channel, message, user, guild, or invite does not exist |
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Message search matches indexed messages against a text query and a filter set. O
|
||||
|
||||
Fluxer resolves the requested scope to the set of channels the caller may search, then runs the query over that set. A private channel search covers the authenticated account alone, and the set holds a guild channel only while the caller can read its history.
|
||||
|
||||
Term matching and typo tolerance can vary by deployment. Filter matching is exact.
|
||||
Term matching and typo tolerance depend on the instance's search backend, Elasticsearch or Meilisearch. Filter matching is exact.
|
||||
|
||||
## Message search result object
|
||||
|
||||
@@ -31,9 +31,9 @@ The body a completed search returns, holding one page of matching messages.
|
||||
|
||||
<sup>2</sup> Contains one entry for each distinct channel represented in `messages`, and never contains a channel with no returned message
|
||||
|
||||
<sup>3</sup> Counted over the whole resolved scope. A single channel context without [READ_MESSAGE_HISTORY](/http-api/permissions/) reports the number returned
|
||||
<sup>3</sup> Counted over the whole resolved scope. In a single channel context without [READ_MESSAGE_HISTORY](/http-api/permissions/), `total` is the number of messages in `messages`
|
||||
|
||||
<sup>4</sup> Present only when the backend produced a cursor. A single channel context, a reconciled offset page, and a Meilisearch instance each return none
|
||||
<sup>4</sup> Present only when the backend produced a cursor. A single channel context, a Meilisearch instance, and a page Fluxer rebuilt after it found deleted messages in the backend results each return none
|
||||
|
||||
[Search messages](#search-messages) never honours a supplied cursor, so page through a result set with `page` alone.
|
||||
|
||||
@@ -53,7 +53,7 @@ Deleted messages are omitted. When using `page`, the total counts only existing
|
||||
|
||||
## Search indexing object
|
||||
|
||||
The other 200 body. Fluxer returns it when a channel the search needs has never been indexed, or when that index predates the instance forced reindex point.
|
||||
The other 200 body. Fluxer returns it when a channel the search needs has never been indexed, or when that channel was last indexed before 2026-05-23 17:30:00 UTC, a fixed date built into Fluxer.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -174,7 +174,7 @@ Every scope that can include a guild channel calls the main Gateway. A Gateway c
|
||||
|
||||
### JSON body
|
||||
|
||||
Every field is optional, though the default `current` scope requires a context. An omitted filter is not applied.
|
||||
Every field is optional, though the default `current` scope requires `context_guild_id` or `context_channel_id`. An omitted filter is not applied.
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A Go Live stream is a screen share on a member's existing voice connection. A stream key names one stream. The operations here record a region preference and manage the stream's JPEG preview image.
|
||||
|
||||
Every route on this page is user-only. Bot and OAuth2 credentials are rejected.
|
||||
Every route on this page is user-only. Fluxer rejects a bot token and an OAuth2 bearer credential with 403 `ACCESS_DENIED`.
|
||||
|
||||
## Stream lifecycle
|
||||
|
||||
@@ -38,15 +38,15 @@ Fluxer checks the scope segment against the resolved channel. A guild-scoped key
|
||||
|
||||
The route then compares the channel segment against the resolved channel ID, and a mismatch returns 400 `STREAM_KEY_CHANNEL_MISMATCH`. On the routes that have a body `channel_id`, a caller MUST send the same value in the body and in the key.
|
||||
|
||||
That comparison is the last check of the read boundary. Channel lookup, the scope check, the channel type check, and the `CONNECT` check all run first, so a body `channel_id` naming a channel the caller cannot see answers 404 `UNKNOWN_CHANNEL` or 403.
|
||||
Fluxer runs that comparison after every other [read access](#access-rules) check. Channel lookup, the scope check, the channel type check, and the `CONNECT` check all run first, so a body `channel_id` naming a channel the caller cannot see returns 404 `UNKNOWN_CHANNEL` or one of the 403 codes listed under [Access rules](#access-rules).
|
||||
|
||||
## Access rules
|
||||
|
||||
The boundaries below apply across this page.
|
||||
[Get stream preview](#get-stream-preview) requires read access. Every other route on this page requires mutation access.
|
||||
|
||||
Read access matches [Get channel](/http-api/channels/#get-channel) for the [resolved channel](#stream-key). A guild channel also requires the [CONNECT](/http-api/permissions/) permission. The caller does not have to own the stream, so any member who can join the channel can read its preview.
|
||||
|
||||
Mutation access requires everything read access does. A guild channel also requires the [STREAM](/http-api/permissions/) permission. The caller must hold a voice state in exactly the resolved channel and the connection identifier the key names. Fluxer rejects a caller that holds no such voice state with 403 `ACCESS_DENIED`, even when it owns the channel.
|
||||
Mutation access requires everything read access does. A guild channel also requires the [STREAM](/http-api/permissions/) permission. The caller must hold a voice state whose channel is the resolved channel and whose `connection_id` is the connection segment of the key. Fluxer rejects a caller that holds no such voice state with 403 `ACCESS_DENIED`, even when it owns the channel.
|
||||
|
||||
:::caution[A preview can exist with no live stream]
|
||||
A caller can record a region and upload a preview for any voice connection it holds, whether or not it is streaming.
|
||||
@@ -114,7 +114,7 @@ Records the preferred media region for a stream and returns 204 with an empty bo
|
||||
| --- | --- | --- |
|
||||
| region?<sup>1</sup> | string | The RTC region ID of 1 through 64 characters |
|
||||
|
||||
<sup>1</sup> An omitted field stores no region for this stream
|
||||
<sup>1</sup> An omitted field removes any region already stored for this stream
|
||||
|
||||
A region identifier is the `id` of an [RTC region object](/http-api/channels/#rtc-region-object), as returned by [List RTC regions](/http-api/channels/#list-rtc-regions).
|
||||
|
||||
@@ -182,7 +182,7 @@ The response `Content-Type` is the media type recorded when the preview was writ
|
||||
|
||||
Replaces the stream's preview with a JPEG sent inline as base64 and returns 204 with an empty body. The caller must satisfy the [mutation access](#access-rules) boundary.
|
||||
|
||||
When the image should travel out of band, use [Create stream preview upload URL](#create-stream-preview-upload-url).
|
||||
To upload the image bytes with a separate `PUT` request, use [Create stream preview upload URL](#create-stream-preview-upload-url).
|
||||
|
||||
### Path parameters
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ The UTF-8 encoding of a submitted document cannot exceed 8388608 bytes, and the
|
||||
|
||||
<sup>3</sup> Measured in characters of the submitted string
|
||||
|
||||
An account holds no theme quota. The route bucket is the only bound on how many themes an account creates, and no route here checks an [instance limit key](/http-api/instance/#limit-keys).
|
||||
An account holds no theme quota. The `theme:share:create` rate limit bucket is the only bound on how many themes an account creates, and no route here checks an [instance limit key](/http-api/instance/#limit-keys).
|
||||
|
||||
## Content screening
|
||||
|
||||
@@ -91,7 +91,7 @@ The object has the identifier and nothing else.
|
||||
|
||||
### Side effects
|
||||
|
||||
Fluxer stores the UTF-8 encoding of the submitted document with the content type `text/css; charset=utf-8`. The identifier addresses the document by the time the caller receives it.
|
||||
Fluxer stores the UTF-8 encoding of the submitted document with the content type `text/css; charset=utf-8`. Fluxer finishes storing the document before it returns the identifier.
|
||||
|
||||
The [Media Proxy](/media-proxy/overview/) serves the document from `/themes/{id}.css`.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ The route is user-only. Fluxer rejects a bot token and an OAuth2 bearer credenti
|
||||
|
||||
## Site resolvers
|
||||
|
||||
Supported sites include the following. Preview availability depends on the site and deployment configuration. Other public URLs can produce previews from their page metadata or media content.
|
||||
The table below lists every site resolver. Whether a site produces a preview depends on the site, and the Klipy and YouTube resolvers also require an API key configured on the instance. Other public URLs can produce previews from their page metadata or media content.
|
||||
|
||||
| Site | Matched URL |
|
||||
| --- | --- |
|
||||
@@ -62,7 +62,7 @@ A refused or failed fetch does not by itself fail the operation. When Fluxer pro
|
||||
|
||||
## Cache behaviour
|
||||
|
||||
This endpoint requests a fresh resolution. Messages may reuse an earlier preview or receive one in a later message update.
|
||||
This endpoint reads no cached result and resolves the URL again. A message can show a cached preview for the same URL, or receive its preview later in a message update.
|
||||
|
||||
## Resolve URL embeds
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ A system account never receives the deleted representation. An account only sche
|
||||
|
||||
## Public user flags
|
||||
|
||||
`flags` holds the publicly visible subset of the account's stored flags. `STAFF` is also absent from an account with the staff-hidden flag.
|
||||
`flags` holds the publicly visible subset of the account's stored flags. `STAFF` is also removed from `flags` while the account has the stored `STAFF_HIDDEN` flag.
|
||||
|
||||
| Value | Name | Description |
|
||||
| --- | --- | --- |
|
||||
@@ -79,7 +79,7 @@ The account-wide preference applied when another account replies to one of this
|
||||
|
||||
| Value | Name | Description |
|
||||
| --- | --- | --- |
|
||||
| 0 | NO_PREFERENCE | Respect the sender's intent on each reply, with no warning when the mention is toggled |
|
||||
| 0 | NO_PREFERENCE | The sender chooses whether each reply mentions this account, with no warning when the mention is toggled |
|
||||
| 1 | PREFER_MENTION | Replies mention this account by default, and the sender is warned when they disable the mention |
|
||||
| 2 | PREFER_NO_MENTION | Replies omit the mention by default, and the sender is warned when they enable it |
|
||||
|
||||
@@ -171,15 +171,15 @@ The private representation of the current account, returned by [Get current user
|
||||
|
||||
<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>12</sup> While the value is set, premium entitlements remain active until it passes, and it replaces the default grace interval that otherwise follows `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>13</sup> The flag is set when the account changes its username or discriminator while holding non-lifetime premium, and it marks the discriminator for reselection when that premium access ends
|
||||
<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>14</sup> The field is declared but never populated, so it is absent from every response
|
||||
|
||||
<sup>15</sup> The value drives the 24-hour cooldown on changing the default voice activity sharing state
|
||||
|
||||
<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 contact is exempt from required actions
|
||||
<sup>16</sup> The array is empty when the account has no stored suspicious activity flag, when it has no email address, or when the account policy exempts its email address from required actions
|
||||
|
||||
<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
|
||||
|
||||
@@ -192,7 +192,7 @@ The private representation of the current account, returned by [Get current user
|
||||
:::
|
||||
|
||||
:::caution[An outstanding required action blocks ordinary routes]
|
||||
While `required_actions` is non-empty, the ordinary login requirement returns 403 `ACCOUNT_SUSPICIOUS_ACTIVITY`. The error body's `data` member has `suspicious_activity_flags`, rebuilt from the surviving list.
|
||||
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.
|
||||
@@ -246,7 +246,7 @@ Each entry is an exact string in the `required_actions` array of the [user objec
|
||||
| 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 clause produces an ordinary verified phone requirement
|
||||
<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
|
||||
|
||||
## Profile field privacy flags
|
||||
|
||||
@@ -262,7 +262,7 @@ A field whose flags value is `0` is visible to nobody, including on the owner's
|
||||
|
||||
The complete account-wide settings record, with one field per setting. `synced_preferences` holds a base64-encoded Protobuf snapshot defined by the [user settings Protobuf reference](/http-api/users/settings-protobuf/). Fluxer stores that snapshot for clients and never interprets it.
|
||||
|
||||
Update semantics belong to the [user settings update object](/http-api/users/settings/#user-settings-update-object). Both settings operations return this object, and so do [Ready](/gateway/events/#ready-object) and [User Settings Update](/gateway/events/#user-settings-update). Every field below is always present.
|
||||
The [user settings update object](/http-api/users/settings/#user-settings-update-object) lists the fields an update accepts and what each supplied or omitted field does to the stored value. Both settings operations return this object, and so do [Ready](/gateway/events/#ready-object) and [User Settings Update](/gateway/events/#user-settings-update). Every field below is always present.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -521,7 +521,7 @@ A guild member profile is the per-guild profile customisation of the target acco
|
||||
|
||||
<sup>1</sup> The value is forced to `null` when profile privacy restricts the viewer
|
||||
|
||||
<sup>2</sup> The value is `null` while the member's premium-dependent guild customisation is sanitised. An animated banner hash has the `a_` prefix
|
||||
<sup>2</sup> Fluxer sets the value to `null` when the account no longer has premium and the membership has a guild avatar, banner, biography, or accent colour. It stays `null` until the account joins the guild again. An animated banner hash has the `a_` prefix
|
||||
|
||||
## Mutual guild object
|
||||
|
||||
@@ -557,9 +557,9 @@ The profile read returned by [Get user profile](#get-user-profile) for one targe
|
||||
|
||||
<sup>1</sup> The pair requires the `guild_id` query parameter and requires both the caller and the target to be members of that guild
|
||||
|
||||
<sup>2</sup> All are omitted while the profile is restricted or the target hides the premium badge, `premium_since` is also omitted while the target hides the activation time, and `premium_lifetime_sequence` while it hides the sequence
|
||||
<sup>2</sup> All three are omitted while the profile is restricted or the target hides the premium badge. `premium_since` is also omitted while the target hides the activation time. `premium_lifetime_sequence` is also omitted while the target hides the sequence
|
||||
|
||||
<sup>3</sup> The collection is omitted unless its query parameter is true, and always when the target is the caller
|
||||
<sup>3</sup> Each array is omitted unless its query parameter, `with_mutual_friends` or `with_mutual_guilds`, is true. Both are always omitted when the target is the caller
|
||||
|
||||
<sup>4</sup> The array is empty when the profile is restricted. It otherwise holds only verified connections whose [profile field privacy flags](#profile-field-privacy-flags) admit the caller
|
||||
|
||||
@@ -640,7 +640,7 @@ A read that passes the access check can still be restricted. A restricted read n
|
||||
|
||||
### Side effects
|
||||
|
||||
Reading a profile can clear expired premium state after its grace period. That change emits no Gateway Dispatch and does not prevent the profile from being returned.
|
||||
When the target's premium access and its grace period have both ended, reading the profile clears the target's stored premium subscription fields. That change emits no Gateway Dispatch and does not prevent the profile from being returned.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ A guild configuring [message_history_cutoff](/http-api/guilds/#guild-object) sti
|
||||
|
||||
### Side effects
|
||||
|
||||
The cleanup deletion emits no [Saved Message Delete](/gateway/events/#saved-message-delete).
|
||||
When this read deletes an entry whose message no longer exists, Fluxer emits no [Saved Message Delete](/gateway/events/#saved-message-delete).
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ Returns the current [user](/http-api/users/#user-object) object.
|
||||
### Limitations
|
||||
|
||||
- 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 neutralised representation, and `email` only when it also holds the `email` 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.
|
||||
|
||||
This route alone admits an account with an outstanding [required action](/http-api/users/#required-actions), so a client can read and poll the actions it has to complete.
|
||||
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
|
||||
|
||||
@@ -123,7 +123,7 @@ 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 the required action barrier, 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).
|
||||
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.
|
||||
|
||||
@@ -141,7 +141,7 @@ An animated avatar requires the animated avatar entitlement and is otherwise rej
|
||||
|
||||
The username, display name, biography, and pronouns are matched against the instance profile substring blocklist, and a match returns 403 [`CONTENT_BLOCKED`](/http-api/errors/). An account with the `STAFF` flag is exempt. The biography and pronouns are also scanned against the phrase and URL blocklists.
|
||||
|
||||
The global content filter applied to every POST, PUT, and PATCH JSON body runs the same pair of blocklists over the whole body before the handler. It reads `username`, `global_name`, `email`, `timezone`, and `discriminator` when supplied as a string, alongside the biography and pronouns. It skips `avatar`, `banner`, `password`, `new_password`, `email_token`, `mfa_code`, `webauthn_response`, `webauthn_challenge`, and every field whose name ends in `_flags`, and it ignores any value shorter than 3 characters. A match returns the same 403 `CONTENT_BLOCKED`, and no flag exempts an account from it.
|
||||
The global content filter applied to every POST, PUT, and PATCH JSON body runs the phrase and URL blocklists over the whole body before the handler. It reads `username`, `global_name`, `email`, `timezone`, and `discriminator` when supplied as a string, alongside the biography and pronouns. It skips `avatar`, `banner`, `password`, `new_password`, `email_token`, `mfa_code`, `webauthn_response`, `webauthn_challenge`, and every field whose name ends in `_flags`, and it ignores any value shorter than 3 characters. A match returns the same 403 `CONTENT_BLOCKED`, and no flag exempts an account from it.
|
||||
|
||||
### Secondary controls
|
||||
|
||||
@@ -172,7 +172,7 @@ Supplying `new_password` on a claimed account deletes every other authentication
|
||||
|
||||
### Side effects
|
||||
|
||||
A successful change emits [User Update](/gateway/events/#user-update) to the account. When a [partial user object](/http-api/users/#partial-user-object) field changes, other accounts observe the new value. Changing the username, discriminator, or display name also reindexes the account's guild member records in every guild whose member index exists. A body that takes the early return changes nothing and emits no Dispatch. Any other accepted body emits the Dispatch even when no stored value changed.
|
||||
A successful change emits [User Update](/gateway/events/#user-update) to the account. When a [partial user object](/http-api/users/#partial-user-object) field changes, other accounts observe the new value. Changing the username, discriminator, or display name also updates the account in guild member search for every guild whose members are indexed. A body with no `email_token` and no field beyond the sudo verification fields changes nothing and emits no Dispatch. Any other accepted body emits the Dispatch even when no stored value changed.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ The account-level codes `ACCESS_DENIED`, `ACCOUNT_SUSPICIOUS_ACTIVITY`, `SUDO_MO
|
||||
|
||||
## Email send controls
|
||||
|
||||
Each send-bearing operation passes through its own route bucket and a longer email-send control keyed by the account. Exhausting either produces HTTP 429 even when the other has room.
|
||||
Each operation that sends an email counts against its own route bucket and against a 15-minute email-send control for the account. Exhausting either produces HTTP 429 even when the other has room.
|
||||
|
||||
| Control | Allowance | Operations |
|
||||
| --- | --- | --- |
|
||||
@@ -56,7 +56,7 @@ Each send-bearing operation passes through its own route bucket and a longer ema
|
||||
|
||||
## Email change ticket states
|
||||
|
||||
The email change ticket is pending-original, pending-new, or completed. A step allowed in one state is rejected in every other with the transition code named below.
|
||||
The email change ticket is pending-original, pending-new, or completed. A step allowed in one state is rejected in every other with the code named in the paragraphs below.
|
||||
|
||||
| Current state | Operation | Next state |
|
||||
| --- | --- | --- |
|
||||
@@ -68,7 +68,7 @@ The email change ticket is pending-original, pending-new, or completed. A step a
|
||||
|
||||
A ticket created without original email verification starts in the pending-new state and receives its `original_proof` directly from [Start email change](#start-email-change). Requesting or verifying a new address while the ticket is still pending original fails with `ORIGINAL_EMAIL_MUST_BE_VERIFIED_FIRST`.
|
||||
|
||||
Verifying the original stage on a ticket that never required it fails with `ORIGINAL_VERIFICATION_NOT_REQUIRED`. Resending the original code fails with `ORIGINAL_EMAIL_ALREADY_VERIFIED` on a cleared ticket and on a ticket that never required the stage, so the response does not tell the two apart. On a cleared ticket, verifying the original address again returns the same `original_proof`.
|
||||
Verifying the original stage on a ticket that never required it fails with `ORIGINAL_VERIFICATION_NOT_REQUIRED`. Resending the original code fails with `ORIGINAL_EMAIL_ALREADY_VERIFIED` on a ticket that has passed original address verification and on a ticket that never required the stage, so the response does not tell the two apart. On a ticket that has passed original address verification, verifying the original address again returns the same `original_proof`.
|
||||
|
||||
Resending the new address code before an address has been requested fails with `NO_NEW_EMAIL_REQUESTED`. A pending-original ticket returns the same code.
|
||||
|
||||
@@ -114,7 +114,7 @@ The state of a newly created email change ticket.
|
||||
|
||||
## New email request object
|
||||
|
||||
The state of a ticket with a candidate address bound to it and a code in flight to that address.
|
||||
The state of a ticket with a candidate address bound to it and a code sent to that address.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -192,7 +192,7 @@ The replacement session [Complete password change](#complete-password-change) re
|
||||
| token<sup>1</sup> | string | The authentication token for the replacement session |
|
||||
| auth_session_id_hash | string | The base64url-encoded hash of the replacement authentication session |
|
||||
|
||||
<sup>1</sup> The same operation destroys the token the client authenticated with, so use this value before issuing another request
|
||||
<sup>1</sup> The same operation destroys the token the client authenticated with, so the client sends every later request with this value
|
||||
|
||||
## Start email change
|
||||
|
||||
@@ -202,7 +202,7 @@ Creates an email change ticket. Returns an [email change start](#email-change-st
|
||||
|
||||
Only an account that holds an email address or is unclaimed can start an email change. Every other account fails with `MUST_HAVE_EMAIL_TO_CHANGE_IT`.
|
||||
|
||||
Original address verification applies only when the account holds a verified email address. A bounce clears the verified state. An account that has done nothing since the bounce gets a ticket that skips the original stage. [Apply email change](#apply-email-change) marks the address verified again without clearing the bounced marker. An account that has applied a change since the bounce therefore gets a ticket that does require the stage.
|
||||
Original address verification applies only when the account holds a verified email address. A bounce clears the verified state. An account whose address has not been verified again since the bounce gets a ticket that skips the original stage. [Apply email change](#apply-email-change) marks the address verified again without clearing the bounced marker. An account that has applied a change since the bounce therefore gets a ticket that does require the stage.
|
||||
|
||||
Sending an original address code counts against the original address control.
|
||||
|
||||
@@ -291,10 +291,10 @@ Fluxer trims the address and then checks it in order.
|
||||
|
||||
- An empty address fails with `EMAIL_IS_REQUIRED`.
|
||||
- An address equal to the ticket's recorded original fails with `NEW_EMAIL_MUST_BE_DIFFERENT`.
|
||||
- A domain without DNS eligibility fails with `INVALID_EMAIL_ADDRESS`.
|
||||
- A domain that publishes no mail exchange or address records fails with `EMAIL_DOMAIN_CANNOT_RECEIVE_MAIL`.
|
||||
- An address another account already owns fails with `EMAIL_ALREADY_IN_USE`.
|
||||
|
||||
Ownership is checked again when the resulting email token is applied, so passing here reserves nothing. Calling the operation again with a different address replaces the pending address on the same ticket.
|
||||
Ownership is checked again when the resulting email token is applied, so a successful request here does not reserve the address. Calling the operation again with a different address replaces the pending address on the same ticket.
|
||||
|
||||
The send counts against the new address control, and the ticket applies its own 30-second cooldown.
|
||||
|
||||
@@ -429,7 +429,7 @@ The body extends the [sudo verification object](/http-api/users/mfa/#sudo-verifi
|
||||
The body has no profile field and no `new_password` field, and Fluxer strips unknown keys before it handles the request.
|
||||
|
||||
:::caution[An unclaimed account never sends `password` here]
|
||||
Sudo mode does not apply to it, so Fluxer reads the field as an ordinary account update. Such an account can update only `new_password`, `has_dismissed_premium_onboarding`, and `has_unread_gift_inventory`. The request fails with `UNCLAIMED_ACCOUNTS_CAN_ONLY_SET_EMAIL_VIA_TOKEN` on the path `password`.
|
||||
Sudo mode does not apply to an unclaimed account, so Fluxer reads `password` as an ordinary account update field. Such an account can update only `new_password`, `has_dismissed_premium_onboarding`, and `has_unread_gift_inventory`. The request fails with `UNCLAIMED_ACCOUNTS_CAN_ONLY_SET_EMAIL_VIA_TOKEN` on the path `password`.
|
||||
:::
|
||||
|
||||
An unclaimed account that also wants a first password makes a single [Modify current user](/http-api/users/current-user/#modify-current-user) call with `email_token` and `new_password`.
|
||||
@@ -465,7 +465,7 @@ Every one of them requires the bounced marker. An account without it is rejected
|
||||
|
||||
The flow also requires the account to hold no verified address, which is the state a bounce leaves. [Apply email change](#apply-email-change) marks an address verified without clearing the bounced marker. An account that took the ordinary flow after its bounce has both states, so it gets `ORIGINAL_EMAIL_MUST_BE_VERIFIED_FIRST` here and changes its address through the ordinary flow.
|
||||
|
||||
[Ticket and code contract](#ticket-and-code-contract) applies to its codes, cooldown, and new address send control.
|
||||
The codes and the 30-second cooldown in this flow follow [Ticket and code contract](#ticket-and-code-contract). The sends in this flow count against the new address control.
|
||||
|
||||
## Request replacement email for bounced address
|
||||
|
||||
@@ -473,7 +473,7 @@ The flow also requires the account to hold no verified address, which is the sta
|
||||
|
||||
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: it must pass DNS eligibility, 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. 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.
|
||||
|
||||
The send counts against the new address control.
|
||||
|
||||
@@ -656,7 +656,7 @@ The ticket moves to its verified state and stores a verification proof. The acco
|
||||
|
||||
Consumes a verified ticket and replaces the account password. Returns a [password change completion](#password-change-completion-object) object on success. Emits an [Auth Session Change](/gateway/events/#auth-session-change) Gateway event with the replacement token.
|
||||
|
||||
No sudo verification applies. A ticket outside its verified state or a proof that does not match fails with `INVALID_OR_EXPIRED_TICKET` or `INVALID_PROOF_TOKEN`. A replacement password in the public breached-password corpus is rejected with `PASSWORD_IS_TOO_COMMON`. The ticket is marked completed once the password is written, so a retry fails with `TICKET_ALREADY_COMPLETED`.
|
||||
No sudo verification applies. A ticket outside its verified state fails with `INVALID_OR_EXPIRED_TICKET`. A proof that does not match fails with `INVALID_PROOF_TOKEN`. A replacement password in the public breached-password corpus is rejected with `PASSWORD_IS_TOO_COMMON`. The ticket is marked completed once the password is written, so a retry fails with `TICKET_ALREADY_COMPLETED`.
|
||||
|
||||
### JSON body
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Multi-factor authentication asks for a second proof of identity after the accoun
|
||||
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.
|
||||
|
||||
:::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 issuing user and operation context and expires after five minutes, so a registration challenge cannot be redeemed as a sudo assertion.
|
||||
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: registration, sudo, MFA login, or discoverable login. It expires after five minutes, so a registration challenge cannot be redeemed as a sudo assertion.
|
||||
:::
|
||||
|
||||
## TOTP parameters
|
||||
@@ -93,7 +93,7 @@ The accepted proof depends on the authenticators the account has configured.
|
||||
|
||||
The `totp` method reads `mfa_code` as a current authenticator code or an unconsumed backup code and requires the account to hold a TOTP secret. The `webauthn` method reads `webauthn_response` and `webauthn_challenge` together and requires a registered credential.
|
||||
|
||||
A request that has no accepted proof fails with 403 `SUDO_MODE_REQUIRED`. Its body has the [sudo mode methods object](#sudo-mode-methods-object) members, and a client can present the correct challenge. A proof that is present but wrong fails instead with 400 `INVALID_FORM_BODY` and a [validation error](/http-api/#validation-error-object) entry. A mismatched password produces path `password` with code `INVALID_PASSWORD`. A rejected TOTP code, backup code, or WebAuthn assertion produces path `mfa_code` with code `INVALID_MFA_CODE`, so a client cannot tell which of the three was rejected.
|
||||
A request that has no accepted proof fails with 403 `SUDO_MODE_REQUIRED`. Its body has the [sudo mode methods object](#sudo-mode-methods-object) members, so a client can tell which proof to ask the user for. A proof that is present but wrong fails instead with 400 `INVALID_FORM_BODY` and a [validation error](/http-api/#validation-error-object) entry. A mismatched password produces path `password` with code `INVALID_PASSWORD`. A rejected TOTP code, backup code, or WebAuthn assertion produces path `mfa_code` with code `INVALID_MFA_CODE`, so a client cannot tell which of the three was rejected.
|
||||
|
||||
The `totp` method also consumes a per-account allowance of 10 multi-factor attempts in 15 minutes, shared by every sudo-gated operation on every resource. Fluxer charges the allowance before it checks the code, so a wrong code and a correct code both draw on it. A correct code resets the counter to zero. While it is exhausted, a correct code returns the same `INVALID_MFA_CODE` entry as a wrong one. The `webauthn` method draws on no allowance. The login MFA allowances on [HTTP authentication](/http-api/authentication/) are counted separately.
|
||||
|
||||
@@ -202,7 +202,7 @@ The backup codes and the proof a verified challenge returns.
|
||||
|
||||
An account holds at most 10 WebAuthn credentials. Both [Create WebAuthn registration options](#create-webauthn-registration-options) and [Register WebAuthn credential](#register-webauthn-credential) enforce this limit.
|
||||
|
||||
Fluxer verifies every assertion against the relying party identifier and the allowed origins configured for the instance. The credential's signature counter strictly increases, unless both the stored and reported counters are zero, which is how an authenticator without a counter appears.
|
||||
Fluxer verifies every assertion against the relying party identifier and the allowed origins configured for the instance. Fluxer rejects an assertion whose reported signature counter is not greater than the stored counter. The exception is a stored and a reported counter of zero, which is how an authenticator without a counter appears.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -243,7 +243,7 @@ Fluxer never returns the credential's public key, signature counter, or reported
|
||||
|
||||
## WebAuthn assertion object
|
||||
|
||||
The browser WebAuthn `PublicKeyCredential` serialisation. Its field names are camelCase, because the boundary accepts the exact structure the WebAuthn client API produces.
|
||||
The browser WebAuthn `PublicKeyCredential` serialisation. Its field names are camelCase, because Fluxer accepts the exact structure the WebAuthn client API produces.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -337,7 +337,7 @@ Every operation that issues a WebAuthn authentication challenge returns this obj
|
||||
| allowCredentials?<sup>3</sup> | array[[WebAuthn credential descriptor](#webauthn-credential-descriptor-object) object] | Credentials accepted for this operation |
|
||||
| userVerification<sup>4</sup> | string | User verification requirement, one of `discouraged`, `preferred`, or `required` |
|
||||
|
||||
<sup>1</sup> Expires five minutes after issue and is bound to the exact context that issued it, so a sudo challenge cannot be redeemed as a login assertion
|
||||
<sup>1</sup> Expires five minutes after issue and is bound to the operation that issued it, so a sudo challenge cannot be redeemed as a login assertion
|
||||
|
||||
<sup>2</sup> Every operation emits the fixed value `60000`, a standard PublicKeyCredential request option
|
||||
|
||||
@@ -478,7 +478,7 @@ The body extends the [sudo verification object](#sudo-verification-object) with
|
||||
|
||||
### Side effects
|
||||
|
||||
TOTP is enabled and 10 backup codes are issued. Any backup code the account already held stays in place, so the response has only the 10 new codes. The resulting authenticator types are also applied to every bot account owned by the user. [User Update](/gateway/events/#user-update) reaches the user's sessions and each owned bot whose authenticator types changed.
|
||||
TOTP is enabled and 10 backup codes are issued. Any backup code the account already held stays in place, so the response has only the 10 new codes. Fluxer also copies the account's authenticator types to every bot account the user owns. [User Update](/gateway/events/#user-update) reaches the user's sessions and each owned bot whose authenticator types changed.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -516,7 +516,7 @@ Disabling TOTP invalidates every backup code on the account, including the code
|
||||
|
||||
### Side effects
|
||||
|
||||
TOTP is disabled and every backup code stops working. Fluxer removes the TOTP authenticator type, along with the unassigned legacy authenticator value `1` when the account still held it. The resulting authenticator types are also applied to every bot account owned by the user. [User Update](/gateway/events/#user-update) goes to the user's sessions and each owned bot whose authenticator types changed.
|
||||
TOTP is disabled and every backup code stops working. Fluxer removes the TOTP authenticator type, along with the unassigned legacy authenticator value `1` when the account still held it. Fluxer also copies the account's authenticator types to every bot account the user owns. [User Update](/gateway/events/#user-update) goes to the user's sessions and each owned bot whose authenticator types changed.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -775,7 +775,7 @@ Fluxer consumes the registration challenge before it re-checks the credential li
|
||||
|
||||
### Side effects
|
||||
|
||||
The credential is added to the account. When it is the account's first WebAuthn credential, the WebAuthn authenticator type is also applied to every bot account owned by the user.
|
||||
The credential is added to the account. When it is the account's first WebAuthn credential, Fluxer also copies the account's authenticator types, which now include WebAuthn, to every bot account the user owns.
|
||||
|
||||
[WebAuthn Credentials Update](/gateway/events/#webauthn-credentials-update) reaches the user's sessions with the complete current credential summaries. When the authenticator types changed, [User Update](/gateway/events/#user-update) also reaches the user and each affected owned bot.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ A single note the caller holds for one target account.
|
||||
| --- | --- | --- |
|
||||
| note<sup>1</sup> | string | The note text the caller stored for the target account |
|
||||
|
||||
<sup>1</sup> Never null and never the empty string. A stored note is always 1 to 256 characters after normalisation, and every clearing form deletes the record
|
||||
<sup>1</sup> Never null and never the empty string. A stored note is always 1 to 256 characters after normalisation, and an omitted, null, or empty `note` deletes the stored note
|
||||
|
||||
## User notes record
|
||||
|
||||
@@ -100,7 +100,7 @@ A target with no stored note returns 404 `UNKNOWN_USER`. The same status covers
|
||||
|
||||
Creates, replaces, or deletes the caller's note for one target account. Returns 204 with an empty body. Emits a [User Note Update](/gateway/events/#user-note-update) Gateway event.
|
||||
|
||||
The supplied text becomes the complete stored note. An unresolved target ID returns 404 `UNKNOWN_USER`, even when the request would only delete a note. The caller can store a note for any account that exists, including a bot account, its own account, and an account it holds no relationship with.
|
||||
The supplied text becomes the complete stored note. A target ID that names no account returns 404 `UNKNOWN_USER`, even when the request would only delete a note. The caller can store a note for any account that exists, including a bot account, its own account, and an account it holds no relationship with.
|
||||
|
||||
:::caution[An omitted, null, or empty note deletes it]
|
||||
An empty string is [read as `null`](/http-api/#input-normalisation), so the forms are indistinguishable. Any value that normalises to the empty string, such as one made only of whitespace, fails validation with `STRING_LENGTH_INVALID`. There is no separate delete route.
|
||||
|
||||
@@ -69,7 +69,7 @@ Keep phone numbers, verification codes, and challenge codes out of logs, analyti
|
||||
|
||||
## Provider lookup verdicts
|
||||
|
||||
Outbound SMS requires the number to pass the following checks. The first matching verdict applies. A number routed directly to an inbound challenge does not need this lookup.
|
||||
Outbound SMS requires the number to pass the following checks. Fluxer evaluates the rows in order and uses the verdict of the first row whose condition matches. A number routed directly to an inbound challenge does not need this lookup.
|
||||
|
||||
| Order | Condition | Verdict |
|
||||
| --- | --- | --- |
|
||||
@@ -112,7 +112,7 @@ A reason explains why the attempt could not complete over outbound SMS.
|
||||
|
||||
## 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, and [Verify phone code](#verify-phone-code) then takes the number itself.
|
||||
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
|
||||
|
||||
@@ -213,7 +213,7 @@ Several independent policies can force the inbound channel, and each one overrid
|
||||
|
||||
An attempt that survives that step passes through phone attempt risk controls keyed by account, by client IP address, and by /24 network. A hard block returns 429 `PHONE_RATE_LIMIT_EXCEEDED` with a `Retry-After` of 86400. A captcha decision returns 400 `CAPTCHA_REQUIRED`, and this route reads no [CAPTCHA](/topics/captcha/) solution header, so the attempt cannot be retried with a solution. An inbound decision returns a challenge with the `behavioural_risk` reason.
|
||||
|
||||
An earlier attempt against the same account or number can record a provider cooldown, and a later request under that cooldown returns 429 `PHONE_RATE_LIMIT_EXCEEDED`. The destination is then resolved through [provider lookup](#provider-lookup-verdicts). A number that has already completed verification twice is refused with 400 `PHONE_ALREADY_USED`.
|
||||
When the SMS provider throttles an earlier send or code check for the same account or number, Fluxer records a cooldown. A request during that cooldown returns 429 `PHONE_RATE_LIMIT_EXCEEDED`. The destination is then resolved through [provider lookup](#provider-lookup-verdicts). A number that has already completed verification twice is refused with 400 `PHONE_ALREADY_USED`.
|
||||
|
||||
Further controls bound outbound delivery to 3 sends per 6 hours for each account and 3 sends per 5 days for each number. Either denial returns 429 `PHONE_RATE_LIMIT_EXCEEDED`. The provider can also refuse the send. An invalid destination returns 400 `INVALID_PHONE_NUMBER`, and a throttle returns 429 `PHONE_RATE_LIMIT_EXCEEDED`. Every other refusal, an unreachable provider included, returns 400 `SMS_VERIFICATION_UNAVAILABLE`. On an instance where the inbound flow or its receiving number is unconfigured, an attempt routed inbound returns the same code.
|
||||
|
||||
@@ -290,7 +290,7 @@ The configured SMS provider checks the number and the code together, so the prov
|
||||
<sup>1</sup> Stripped of control and formatting characters and trimmed before the length check, and the normalised value is what the provider checks
|
||||
|
||||
:::caution[A third verification consumes the code before refusing]
|
||||
A number can complete verification twice, and each success refreshes that record for a further 31 days. A third attempt is refused with 400 `PHONE_ALREADY_USED`, but only after the provider has accepted the code.
|
||||
A number can complete verification twice, and each success restarts a 31-day window in which Fluxer counts successes for that number. A third attempt is refused with 400 `PHONE_ALREADY_USED`, but only after the provider has accepted the code.
|
||||
:::
|
||||
|
||||
### Response
|
||||
|
||||
@@ -11,7 +11,7 @@ A private channel is a conversation outside any guild. It is either a direct mes
|
||||
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`.
|
||||
|
||||
:::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 reopening the pair resolves the stored channel.
|
||||
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.
|
||||
:::
|
||||
|
||||
## Preloaded messages object
|
||||
@@ -82,7 +82,7 @@ One body field selects the channel type. `recipient_id` opens a direct message.
|
||||
|
||||
A request supplying `recipients` also consumes the `user:group_dm:create` bucket and is subject to the CAPTCHA check below, whatever the resulting participant count.
|
||||
|
||||
Instance policy can disable private channel creation entirely, in which case every request returns 400 `DIRECT_MESSAGES_DISABLED` after the body has been validated and the group DM bucket and CAPTCHA have been applied.
|
||||
When the instance [community policy](/http-api/instance/#community-policy-object) sets `direct_messages_disabled` to true, every request returns 400 `DIRECT_MESSAGES_DISABLED` after the body has been validated and the group DM bucket and CAPTCHA have been applied.
|
||||
|
||||
An unclaimed account holds no password and did not arrive through SSO. Fluxer rejects an unclaimed caller with 400 `UNCLAIMED_ACCOUNT_CANNOT_JOIN_GROUP_DMS` when `recipients` is supplied, and with 400 `UNCLAIMED_ACCOUNT_CANNOT_SEND_DIRECT_MESSAGES` otherwise. An ordinary caller then needs a verified email address, failing which the request returns 403 `DIRECT_MESSAGE_EMAIL_VERIFICATION_REQUIRED`. A bot caller is exempt from the email requirement and is never unclaimed.
|
||||
|
||||
@@ -110,7 +110,7 @@ Every participant, the caller included, holds fewer open group DMs than the inst
|
||||
|
||||
Fluxer then evaluates each other recipient independently and collects every failure, using the [unaddable reasons](#group-dm-unaddable-reasons) above. One failing recipient rejects the whole request with 400 `GROUP_DM_RECIPIENTS_NOT_ADDABLE`. The response has the rejected entries in the top-level `unaddable_recipients` member and the accepted user IDs in `addable_recipients`, so the caller can retry with a smaller set.
|
||||
|
||||
An ordinary caller never adds a recipient across a block in either direction, because blocking deletes the friendship both ways and a friend request crosses no block.
|
||||
An ordinary caller cannot add a recipient it has blocked or a recipient that has blocked it. Blocking deletes the friendship on both accounts, and a friend request between the two accounts creates no new friendship while either block exists.
|
||||
|
||||
:::note[Group DM admission reads no block state]
|
||||
Fluxer holds a bot caller to no friendship. A recipient that has blocked the bot is added when the two share a guild and the recipient's [group DM add permission flags](/http-api/users/#group-dm-add-permission-flags) admit it.
|
||||
@@ -220,7 +220,7 @@ A guild channel the caller cannot see fails resolution first and returns 403 `MI
|
||||
Fluxer appends a newly pinned channel after every channel already pinned. A channel later closed, left, or deleted stays in the pinned set, and only [Unpin private channel](#unpin-private-channel) or deletion of the caller's account removes an entry.
|
||||
|
||||
:::caution[Unpinning needs the channel to resolve]
|
||||
Unpinning resolves the channel first, so an entry whose channel the caller can no longer resolve stays in the pinned set.
|
||||
Unpinning resolves the channel first, so an entry for a deleted channel, or for a group DM the caller is no longer a recipient of, stays in the pinned set.
|
||||
:::
|
||||
|
||||
:::note[Pinning is idempotent and the Dispatch is unconditional]
|
||||
|
||||
@@ -11,7 +11,7 @@ A relationship describes a friendship, block, or pending friend request from the
|
||||
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`.
|
||||
|
||||
:::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 blocked account observes nothing.
|
||||
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).
|
||||
:::
|
||||
|
||||
A deleted account has the deleted flag with no pending deletion timestamp, and is not a valid friend request target. An account inside a scheduled deletion window has a timestamp, so it stays a valid target.
|
||||
@@ -171,13 +171,13 @@ A target the caller has blocked returns 400 `CANNOT_SEND_FRIEND_REQUEST_TO_BLOCK
|
||||
<sup>1</sup> The friend source evaluation is skipped when the target has no stored settings, which admits the request as though the target permitted requests from anyone
|
||||
|
||||
:::caution[A flagged caller receives an indistinguishable success]
|
||||
A caller with the `SPAMMER` [public user flag](/http-api/users/#public-user-flags), or one whose send trips the deployment's direct contact spam policy, gets a one-sided request. Fluxer writes only the caller's `OUTGOING_REQUEST` record. [Relationship Add](/gateway/events/#relationship-add) reaches the caller alone. The response is an ordinary `OUTGOING_REQUEST` object.
|
||||
A caller with the `SPAMMER` [public user flag](/http-api/users/#public-user-flags), or one whose send triggers the deployment's direct contact spam policy, gets a one-sided request. Fluxer writes only the caller's `OUTGOING_REQUEST` record. [Relationship Add](/gateway/events/#relationship-add) reaches the caller alone. The response is an ordinary `OUTGOING_REQUEST` object.
|
||||
:::
|
||||
|
||||
That path also skips the rules that follow it: the target's block of the caller, the target's [friend source flags](/http-api/users/#friend-source-flags), the app store reviewer rule, the [relationship limit](#relationship-limit), and friendly bot auto-acceptance. The caller's own block of the target is still enforced and still returns 400 `CANNOT_SEND_FRIEND_REQUEST_TO_BLOCKED_USER`. The policy path can also set the `SPAMMER` flag on the caller, which makes every later send one-sided.
|
||||
The one-sided request path also skips the rules that follow it: the target's block of the caller, the target's [friend source flags](/http-api/users/#friend-source-flags), the app store reviewer rule, the [relationship limit](#relationship-limit), and friendly bot auto-acceptance. The caller's own block of the target is still enforced and still returns 400 `CANNOT_SEND_FRIEND_REQUEST_TO_BLOCKED_USER`. When the direct contact spam policy suppresses a send and its action is `flag_spammer`, Fluxer also sets the `SPAMMER` flag on the caller, which makes every later send one-sided.
|
||||
|
||||
:::note[The suppression entries enter at different points]
|
||||
A caller that already has the flag enters before the pending request lookup, so it can hold both an `INCOMING_REQUEST` and an `OUTGOING_REQUEST` record for one account. A caller suppressed during the call enters after the lookup and accepts the pending request normally.
|
||||
Fluxer checks for the `SPAMMER` flag before it looks for a pending request from the target. A caller that already has the flag gets a one-sided request even when the target has a pending request, so it can hold both an `INCOMING_REQUEST` and an `OUTGOING_REQUEST` record for one account. Fluxer applies the direct contact spam policy after that lookup, so a caller without the flag accepts a pending request from the target normally.
|
||||
:::
|
||||
|
||||
### Path parameters
|
||||
@@ -209,7 +209,7 @@ The body can be omitted, in which case it is treated as an empty object.
|
||||
|
||||
A new request writes matching `OUTGOING_REQUEST` and `INCOMING_REQUEST` records and emits [Relationship Add](/gateway/events/#relationship-add) to both accounts. Both records are written with `share_voice_activity` set to true.
|
||||
|
||||
When the target has already sent the caller a request, the operation accepts that request. Acceptance replaces both pending records with FRIEND records and emits [Relationship Update](/gateway/events/#relationship-update) to both accounts, each side taking its own sharing default. No acceptance path on this route opens a direct message channel, unlike [Accept request or block user](#accept-request-or-block-user).
|
||||
When the target has already sent the caller a request, the operation accepts that request. Acceptance replaces both pending records with FRIEND records and emits [Relationship Update](/gateway/events/#relationship-update) to both accounts, and each FRIEND record has `share_voice_activity` set to its owning account's `default_share_voice_activity`. No acceptance path on this route opens a direct message channel, unlike [Accept request or block user](#accept-request-or-block-user).
|
||||
|
||||
A bot target that has `FRIENDLY_BOT` without `FRIENDLY_BOT_MANUAL_APPROVAL` accepts in the same call, so the caller observes [Relationship Add](/gateway/events/#relationship-add) followed by [Relationship Update](/gateway/events/#relationship-update). Both flags are read only on a bot account. The response body of that call is the bot's own record, and its `id` and `user` name the caller and its `share_voice_activity` is the bot's default. The Dispatch the caller receives has the caller's record.
|
||||
|
||||
@@ -378,7 +378,7 @@ The body can be omitted, in which case it is treated as an empty object and the
|
||||
|
||||
Each matched request removes the caller's `INCOMING_REQUEST` record and the sender's `OUTGOING_REQUEST` record, and emits [Relationship Remove](/gateway/events/#relationship-remove) to the caller and to the sender. A call that matches nothing changes nothing.
|
||||
|
||||
A failed request can leave some requests removed without returning a count. Read the relationships again to reconcile.
|
||||
A call that fails partway can remove some incoming friend requests and return no count. Call [List relationships](#list-relationships) again to read what remains.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ Fluxer reports every entry in the resulting `errors` array against the path `syn
|
||||
| --- | --- | --- |
|
||||
| Encoded string longer than 699052 characters | 400 `INVALID_FORM_BODY` | `CONTENT_EXCEEDS_MAX_LENGTH` and `INVALID_FORMAT` |
|
||||
| Encoded string outside the base64 alphabet | 400 `INVALID_FORM_BODY` | `INVALID_FORMAT` |
|
||||
| Decoded message above 524288 bytes | 400 `INVALID_FORM_BODY` | `TOO_LARGE` |
|
||||
| Bytes that do not decode as `SyncedPreferences` | 400 `INVALID_FORM_BODY` | `INVALID_FORMAT` |
|
||||
| Decoded message above 524288 bytes | 400 `VALIDATION_ERROR` | `TOO_LARGE` |
|
||||
| Bytes that do not decode as `SyncedPreferences` | 400 `VALIDATION_ERROR` | `INVALID_FORMAT` |
|
||||
|
||||
An over-length string draws two entries for the one path.
|
||||
|
||||
@@ -204,7 +204,7 @@ Field numbers 42 and 43 are reserved, together with the names `attachment_media_
|
||||
| --- | --- | --- |
|
||||
| 0 | HDR_DISPLAY_MODE_UNSPECIFIED | No explicit mode is selected |
|
||||
| 1 | HDR_DISPLAY_MODE_FULL | Display HDR media without limiting its range |
|
||||
| 2 | HDR_DISPLAY_MODE_STANDARD | Display HDR media using the standard presentation |
|
||||
| 2 | HDR_DISPLAY_MODE_STANDARD | Limit HDR media to standard dynamic range |
|
||||
|
||||
## Accessibility overrides object
|
||||
|
||||
@@ -419,7 +419,7 @@ One entry names one grouping in the favourites view. The client chooses the iden
|
||||
|
||||
## Recent mentions settings object
|
||||
|
||||
The `recent_mentions` field controls which mentions appear in the recent mentions view. The filters combine, and `include_guilds` selects by channel.
|
||||
The `recent_mentions` field controls which mentions appear in the recent mentions view. Each filter set to false removes its mentions, and a mention appears only when no filter removes it. `include_guilds` tests the channel the mention is in.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -609,7 +609,7 @@ The `sound` field controls sound playback, master volume, and per-sound override
|
||||
|
||||
### Sound identifiers
|
||||
|
||||
Both maps are keyed by an arbitrary string, and Fluxer stores and returns any key unchanged. The first-party client uses the identifiers below, and a key outside this set has no defined playback effect.
|
||||
Both maps are keyed by an arbitrary string, and Fluxer stores and returns any key unchanged. The first-party client uses the identifiers below, and the first-party client ignores a key outside this set.
|
||||
|
||||
| Value | Description |
|
||||
| --- | --- |
|
||||
|
||||
@@ -22,7 +22,7 @@ Every route here is user-only and rejects a bot or OAuth2 bearer credential with
|
||||
<sup>1</sup> The account stays connected and keeps receiving Gateway traffic while this value is stored
|
||||
|
||||
:::note[The settings boundary accepts only a chosen status]
|
||||
Gateway presence also uses offline, which describes an account with no active session. The settings boundary rejects offline in both `status` and `status_resets_to`, and invisible is the stored form of appearing offline while connected.
|
||||
Gateway presence also uses offline, which describes an account with no active session. [Modify current user settings](#modify-current-user-settings) rejects offline in both `status` and `status_resets_to`, and invisible is the stored form of appearing offline while connected.
|
||||
:::
|
||||
|
||||
## Theme values
|
||||
@@ -193,8 +193,8 @@ Further members are accepted and ignored. `guild_positions` is an array of at mo
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| status? | string | [Presence status](#presence-status-values) |
|
||||
| status_resets_at?<sup>1</sup> | ?ISO8601 timestamp \| integer | The moment at which the scheduled reset applies, or null to clear it |
|
||||
| status_resets_to? | ?string | [Presence status](#presence-status-values) applied by the scheduled reset, or null to clear it |
|
||||
| status_resets_at?<sup>1</sup> | ?ISO8601 timestamp \| integer | The time at which the first-party client sets `status` to `status_resets_to`, or null to clear it |
|
||||
| status_resets_to? | ?string | [Presence status](#presence-status-values) that the first-party client sets at `status_resets_at`, or null to clear it |
|
||||
| theme? | string | [Theme value](#theme-values) |
|
||||
| locale?<sup>2</sup> | string | Interface [locale](/topics/locales/#supported-locales) |
|
||||
| restricted_guilds?<sup>3</sup> | array[snowflake] | The guilds where member direct messages are restricted (max 200) |
|
||||
@@ -238,9 +238,9 @@ Further members are accepted and ignored. `guild_positions` is an array of at mo
|
||||
|
||||
<sup>4</sup> `flags` is a legacy alias for `friend_source_flags`, which wins when both are supplied. `friend_source_flags` is bounded to 0 through 2147483647 while `flags` accepts any integer
|
||||
|
||||
<sup>5</sup> The stored value is collapsed before it is written. `FRIENDS_ONLY` wins over `NOBODY`, and either collapse preserves `SILENT_EVERYONE`
|
||||
<sup>5</sup> Fluxer reduces the value before it stores it. A value with `FRIENDS_ONLY` is stored as `FRIENDS_ONLY`, and a value with `NOBODY` and no `FRIENDS_ONLY` is stored as `NOBODY`. Both keep `SILENT_EVERYONE` when it is supplied, and any other value is stored unchanged
|
||||
|
||||
<sup>6</sup> `FRIENDS_ONLY`, then `NOBODY`, then `EVERYONE` are tested in that order against the already collapsed value, so `EVERYONE` applies only when neither of the other two is supplied
|
||||
<sup>6</sup> A value with `FRIENDS_ONLY`, `NOBODY`, or `EVERYONE` is stored as one of those flags alone. `FRIENDS_ONLY` wins over the other two, and `NOBODY` wins over `EVERYONE`. Any other value is stored unchanged
|
||||
|
||||
<sup>7</sup> The array replaces the layout in full
|
||||
|
||||
@@ -254,19 +254,19 @@ Further members are accepted and ignored. `guild_positions` is an array of at mo
|
||||
|
||||
<sup>12</sup> The field is staff-only
|
||||
|
||||
<sup>13</sup> The value runs to at most 699052 characters of base64 and decodes to at most 524288 bytes, the 512 KiB ceiling. A longer string fails at the boundary, an oversized decode fails with `TOO_LARGE`, and an undecodable value fails with `INVALID_FORMAT`
|
||||
<sup>13</sup> The value runs to at most 699052 characters of base64 and decodes to at most 524288 bytes, the 512 KiB ceiling. A longer string returns 400 `INVALID_FORM_BODY`, an oversized decode fails with `TOO_LARGE`, and an undecodable value fails with `INVALID_FORMAT`
|
||||
|
||||
Fluxer decodes an accepted `synced_preferences` value, re-encodes it canonically, and stores the result, so a snapshot with only zero values is stored as cleared and read back as the empty string.
|
||||
|
||||
:::caution[A rebuilt folder layout drops unresolved references]
|
||||
The submitted array replaces the layout in full and is retained as sent, so a client that rebuilds it from only the items it can render loses the rest. Send unresolved references back and omit them only from the rendered view.
|
||||
The submitted array replaces the stored layout in full, and Fluxer keeps guild IDs it cannot resolve. A client that rebuilds the array from only the guilds it can render deletes every other guild ID from the stored layout. Send those guild IDs back, and hide them only in the rendered view.
|
||||
:::
|
||||
|
||||
:::note[Fluxer edits the layout on its own]
|
||||
Joining a guild prepends it to the uncategorised folder unless some folder already lists it. Leaving or losing a guild removes its ID from every folder and drops any non-uncategorised folder left empty. Each edit emits [User Settings Update](/gateway/events/#user-settings-update).
|
||||
:::
|
||||
|
||||
Fluxer prepends an empty uncategorised folder to a submitted layout that omits one. A guild ID listed in more than one folder is kept only in the highest-ranked folder. A name is the primary key, so a named folder outranks an unnamed one whatever its identity, and an ordinary folder outranks the uncategorised folder only when both are named or both unnamed. The earliest folder in the submitted order wins a tie.
|
||||
Fluxer prepends an empty uncategorised folder to a submitted layout that omits one. A guild ID listed in more than one folder is kept only in the highest-ranked folder. Fluxer ranks the folders in two steps. A folder with a non-blank name outranks a folder without one. Between two folders that are both named or both unnamed, an ordinary folder outranks the uncategorised folder. The earliest folder in the submitted order wins a tie.
|
||||
|
||||
## Guild folder input object
|
||||
|
||||
@@ -308,7 +308,7 @@ A custom status as submitted with a settings update.
|
||||
|
||||
<sup>3</sup> `emoji_name` is ignored without validation when `emoji_id` has a value. An `emoji_id` of null leaves `emoji_name` in place, and it is then exactly one Unicode emoji
|
||||
|
||||
A custom emoji ID that does not resolve fails with `CUSTOM_EMOJI_NOT_FOUND` against `custom_status.emoji_id`. Fluxer drops the resolved emoji for an account without the global expression entitlement, so the request succeeds with text alone.
|
||||
A custom emoji ID that does not resolve fails with `CUSTOM_EMOJI_NOT_FOUND` against `custom_status.emoji_id`. Fluxer drops the resolved emoji for an account whose `feature_global_expressions` [limit](/http-api/instance/#limit-keys) resolves to 0, so the request succeeds with text alone.
|
||||
|
||||
:::caution[Hide expired custom statuses]
|
||||
An expired status can remain in settings without an expiry Dispatch. Compare `expires_at` with the current time before rendering. A new Gateway session omits the expired status.
|
||||
@@ -362,7 +362,7 @@ Modifies the account-wide settings and returns the complete [user settings](/htt
|
||||
|
||||
An account with no stored settings record returns 404 `UNKNOWN_USER`. A locale change also emits a [User Update](/gateway/events/#user-update) Gateway event.
|
||||
|
||||
An unresolvable custom status emoji returns 400 `INVALID_FORM_BODY` with `CUSTOM_EMOJI_NOT_FOUND` against `custom_status.emoji_id`. The trusted domain, age restriction, and synced preferences decisions return 400 `VALIDATION_ERROR` with their own codes in `errors`.
|
||||
An unresolvable custom status emoji returns 400 `INVALID_FORM_BODY` with `CUSTOM_EMOJI_NOT_FOUND` against `custom_status.emoji_id`. Rejections of `trusted_domains`, of an age-restricted filter, and of an oversized or undecodable `synced_preferences` value return 400 `VALIDATION_ERROR` with their own codes in `errors`.
|
||||
|
||||
### JSON body
|
||||
|
||||
@@ -383,7 +383,7 @@ Fluxer emits [User Settings Update](/gateway/events/#user-settings-update) to th
|
||||
|
||||
Every [User Settings Update](/gateway/events/#user-settings-update) republishes the caller's current [Presence Update](/gateway/events/#presence-update) to eligible sessions, so a custom status change becomes visible without a further request.
|
||||
|
||||
A status of invisible forces every one of the caller's sessions to invisible. A later status of online, idle, or dnd releases them only when at least one session is currently invisible.
|
||||
A status of invisible forces every one of the caller's sessions to invisible. When at least one session is invisible, a later status of online, idle, or dnd sets every one of the caller's sessions to that status.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -421,7 +421,7 @@ Outside the window the cooldown restarts on every accepted request, including on
|
||||
|
||||
### Side effects
|
||||
|
||||
An accepted request writes `default_share_voice_activity` and then walks every friendship. A friendship whose caller-side value already equals the submitted value is skipped, and its stored `version` does not advance. Every friendship, skipped or rewritten, produces one [Relationship Update](/gateway/events/#relationship-update) to the caller and one to the friend, so re-sending the current value still sends the full set out. The friend's Dispatch is emitted only when the reciprocal friendship exists.
|
||||
An accepted request writes `default_share_voice_activity` and then walks every friendship. A friendship whose caller-side value already equals the submitted value is skipped, and its stored `version` does not advance. Every friendship, skipped or rewritten, produces one [Relationship Update](/gateway/events/#relationship-update) to the caller and one to the friend, so re-sending the current value still sends those Dispatches for every friendship. The friend's Dispatch is emitted only when the reciprocal friendship exists.
|
||||
|
||||
It then records the change time in `last_voice_activity_sharing_change_at` and emits [User Update](/gateway/events/#user-update) to the caller.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
|
||||
|
||||
A webhook posts messages into one guild channel under a name and avatar of its own.
|
||||
|
||||
Management routes take a user session token or a bot token. Every other webhook operation uses the webhook ID and secret token in its own path as the complete credential and ignores the `Authorization` header.
|
||||
Management routes take a user session token or a bot token. Every other webhook operation uses the webhook ID and secret token in its own path as the complete credential. A sent `Authorization` header grants no access there, and Fluxer still resolves it, as [Rate limit keying](#rate-limit-keying) describes..
|
||||
|
||||
The [Messages resource](/http-api/messages/) defines the message, rich embed input, and message reference input objects.
|
||||
|
||||
@@ -26,7 +26,7 @@ Fluxer scans a submitted webhook `name` against the instance phrase and URL bloc
|
||||
|
||||
Every route bucket on this page is keyed on the caller identity as well as on the path parameter in its bucket name. The caller identity is the authenticated account when a request resolves one and the client IP address otherwise, so a token-authenticated operation is bounded per address.
|
||||
|
||||
Fluxer resolves an `Authorization` credential on a token-authenticated operation for this keying alone. [Rate limits](/topics/rate-limits/) defines the keying in full.
|
||||
When a request to a token-authenticated operation sends an `Authorization` header, Fluxer reads that credential only to choose the rate limit key. [Rate limits](/topics/rate-limits/) defines the keying in full.
|
||||
|
||||
The [global HTTP limit](/topics/rate-limits/) applies to every management route. [Get webhook with token](#get-webhook-with-token), [Update webhook with token](#update-webhook-with-token), and [Delete webhook with token](#delete-webhook-with-token) consume it under the caller identity. Every other route on this page is exempt and is bounded only by its own route bucket.
|
||||
|
||||
@@ -36,7 +36,7 @@ Fluxer refuses a call from the official web client on [Execute webhook](#execute
|
||||
|
||||
A request that sends no `Origin`, or any other `Origin` value, passes the check. It runs after the route rate limit and before path validation.
|
||||
|
||||
Both token paths also have a second CORS policy that answers every origin, which the [cross-origin request contract](/http-api/#cross-origin-requests) states in full.
|
||||
`/v1/webhooks/{webhook_id}/{token}` and `/v1/webhooks/{webhook_id}/{token}/messages/{message_id}` also have a second CORS policy that allows every origin, which the [cross-origin request contract](/http-api/#cross-origin-requests) states in full.
|
||||
|
||||
## Webhook object
|
||||
|
||||
@@ -98,7 +98,7 @@ Every operation returning a [webhook object](#webhook-object) or a token webhook
|
||||
|
||||
## Webhook message body
|
||||
|
||||
[Execute webhook](#execute-webhook) accepts this JSON body. Every field may be omitted, and `{}` is a valid body that fails later as an empty message.
|
||||
[Execute webhook](#execute-webhook) accepts this JSON body. Every field may be omitted, and `{}` passes schema validation and then returns 400 `CANNOT_SEND_EMPTY_MESSAGE`.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -117,11 +117,11 @@ Every operation returning a [webhook object](#webhook-object) or a token webhook
|
||||
| username?<sup>8</sup> | ?string | Per-message webhook name override (1-80 characters), or null |
|
||||
| avatar_url?<sup>8</sup> | ?string | Absolute `http` or `https` per-message webhook avatar URL override of at most 2,048 characters, or null |
|
||||
|
||||
<sup>1</sup> A webhook author always resolves an effective [max_message_length](/http-api/instance/#limit-keys) of at least 4000 characters, and a higher configured value for the guild raises it further. Exceeding it returns 400 `INVALID_FORM_BODY` with the validation code `CONTENT_EXCEEDS_MAX_LENGTH`
|
||||
<sup>1</sup> Webhook message content is limited to the resolved [max_message_length](/http-api/instance/#limit-keys) value for the guild or 4000 characters, whichever is larger. Exceeding it returns 400 `INVALID_FORM_BODY` with the validation code `CONTENT_EXCEEDS_MAX_LENGTH`
|
||||
|
||||
<sup>2</sup> Bounded by the resolved [max_embeds_per_message](/http-api/instance/#limit-keys) value for the guild, defaulting to 10. Exceeding it returns 400 `INVALID_FORM_BODY` with `TOO_MANY_EMBEDS`
|
||||
|
||||
<sup>3</sup> A JSON body attaches no file. A multipart body uses the ordinary message attachment contract
|
||||
<sup>3</sup> A JSON body attaches no file. A multipart body attaches files as described in [Execute webhook](#execute-webhook)
|
||||
|
||||
<sup>4</sup> A forward reference requires both `channel_id` and `message_id`, and it must not accompany content, embeds, or attachments. See [message references](#message-references)
|
||||
|
||||
@@ -295,7 +295,7 @@ These objects define the body [Execute GitHub webhook](#execute-github-webhook)
|
||||
| review? | ?[GitHub review](#github-review-object) object | Pull request review data, or null |
|
||||
| sender | [GitHub user](#github-user-object) object | Event sender |
|
||||
|
||||
<sup>1</sup> The value gates rendering for most event types, and the exact accepted action for each type is stated in [GitHub event types](#github-event-types)
|
||||
<sup>1</sup> An event type that lists one or more required actions in [GitHub event types](#github-event-types) renders a message only when `action` is one of those values
|
||||
|
||||
<sup>2</sup> A pull request uses the same representation as an issue, so only the fields listed in the [GitHub issue structure](#github-issue-object) are consumed
|
||||
|
||||
@@ -522,7 +522,7 @@ The value of the `X-GitHub-Event` request header selects the rendering. An event
|
||||
|
||||
<sup>4</sup> A forced push requires `head_commit` and compare, and renders a compare link as its whole description
|
||||
|
||||
<sup>5</sup> A conclusion of `skipped` on the relevant suite suppresses the message
|
||||
<sup>5</sup> A `check_suite` event whose check suite has the conclusion `skipped` produces no message. A `check_run` event produces no message when its parent check suite has the conclusion `skipped`
|
||||
|
||||
An ordinary push requires compare and at least one commit, and renders one line for each commit. The line set is not capped by count, and the description built from them is truncated to the ordinary 350-character ceiling.
|
||||
|
||||
@@ -642,7 +642,7 @@ A backfilled incident or maintenance item appends `Backfilled` to the footer tex
|
||||
|
||||
<sup>3</sup> A true value appends `Backfilled` to the embed footer, separated from the page footer text by a vertical bar. When the page supplies no footer text, `Backfilled` is the whole footer
|
||||
|
||||
<sup>4</sup> The embed timestamp is the latest update's `created_at`, then `updated_at`, then `created_at`, and the first value the runtime can parse as a date wins
|
||||
<sup>4</sup> The embed timestamp is the first of these values that parses as a date: the `created_at` of the latest update, then this object's `updated_at`, then this object's `created_at`
|
||||
|
||||
### Instatus maintenance update object
|
||||
|
||||
@@ -840,7 +840,7 @@ The decoded bytes must be at most the resolved [avatar_max_size](/http-api/insta
|
||||
|
||||
<sup>1</sup> An avatar failure names the `avatar` path with `BASE64_LENGTH_INVALID`, `INVALID_BASE64_FORMAT`, `IMAGE_SIZE_EXCEEDS_LIMIT`, or `INVALID_IMAGE_FORMAT`
|
||||
|
||||
<sup>2</sup> The reached allowance is in a top-level member named after its limit key
|
||||
<sup>2</sup> The error response has a top-level `max_webhooks_per_guild` or `max_webhooks_per_channel` field whose value is the allowance that was reached
|
||||
|
||||
| Condition | Error |
|
||||
| --- | --- |
|
||||
@@ -1021,7 +1021,7 @@ The operation removes the webhook, frees its guild and channel webhook slot, and
|
||||
|
||||
## Token routes
|
||||
|
||||
The routes below take the matching webhook ID and token in their paths as the complete credential. The `Authorization` header is neither required nor read on any of them.
|
||||
The routes below take the matching webhook ID and token in their paths as the complete credential. None of them requires the `Authorization` header, and a header sent to one grants no access. Fluxer still resolves it, as [Rate limit keying](#rate-limit-keying) describes.
|
||||
|
||||
## Get webhook with token
|
||||
|
||||
@@ -1302,7 +1302,7 @@ Any other type returns 400 `CANNOT_MODIFY_SYSTEM_WEBHOOK`, and a forward, which
|
||||
|
||||
### Side effects
|
||||
|
||||
The operation replaces the supplied fields, and a content change marks the message edited. Existing mentions stay unchanged and `allowed_mentions` is ignored. Supplying embeds replaces the complete collection and validates its attachment references. Sessions that can read the channel receive [Message Update](/gateway/events/#message-update).
|
||||
The operation replaces the supplied fields, and a content change marks the message edited. Existing mentions stay unchanged and `allowed_mentions` is ignored. Supplying embeds replaces the complete collection. An embed image or thumbnail URL of the form `attachment://{filename}` returns 400 `INVALID_FORM_BODY` unless `{filename}` names an attachment already on the message with a supported image file extension. Sessions that can read the channel receive [Message Update](/gateway/events/#message-update).
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -1403,7 +1403,7 @@ An unrecognised event type, a recognised type whose required fields or action ar
|
||||
|
||||
<RouteHeader method="POST" path="/v1/webhooks/{webhook_id}/{token}/slack" />
|
||||
|
||||
Accepts a Slack-compatible callback, converts it to the Fluxer message contract, and creates one webhook-authored message.
|
||||
Accepts a Slack-compatible callback, converts it to Fluxer message content and embeds, and creates one webhook-authored message.
|
||||
|
||||
A successful callback emits a [Message Create](/gateway/events/#message-create).
|
||||
|
||||
@@ -1441,9 +1441,9 @@ The success body is the literal string `ok` under the `text/html` content type,
|
||||
|
||||
Conversion creates one webhook-authored message with the converted content and embeds, and emits [Message Create](/gateway/events/#message-create) to sessions that can read the channel.
|
||||
|
||||
A supplied username replaces the author name on that message. A supplied `icon_url` that parses as an absolute URL is fetched through the media boundary and stored as the message avatar. The stored webhook name and avatar do not change. When the URL cannot be fetched, the callback still succeeds and the message has no avatar override.
|
||||
A supplied username replaces the author name on that message. A supplied `icon_url` that parses as an absolute URL is fetched through the Media Proxy and stored as the message avatar. The stored webhook name and avatar do not change. When the URL cannot be fetched, the callback still succeeds and the message has no avatar override.
|
||||
|
||||
The webhook execution default applies, and every mention in the converted content is suppressed. The callback has no nonce, so a repeated callback creates a second message.
|
||||
Fluxer suppresses every mention in the converted content, as [Execute webhook](#execute-webhook) does when `allowed_mentions` is omitted. The callback has no nonce, so a repeated callback creates a second message.
|
||||
|
||||
### Rate limit
|
||||
|
||||
@@ -1469,7 +1469,7 @@ A rendered callback emits a [Message Create](/gateway/events/#message-create).
|
||||
The body is an [Instatus callback](#instatus-callback-object) object. A body the schema rejects returns 400 `INVALID_FORM_BODY`.
|
||||
|
||||
:::note[Deduplication is per webhook and callback]
|
||||
Only the first callback for a webhook and a given incident update, maintenance update, or component update is processed within a 24-hour window. A repeated callback is acknowledged and creates no second message. Fluxer processes every callback that has no such identifier.
|
||||
Only the first callback for a webhook and a given incident update, maintenance update, or component update is processed within a 24-hour window. A repeated callback is acknowledged and creates no second message. Fluxer processes every callback whose incident or maintenance item has no `id`, whose component update has no `component_id` or `created_at`, or that has no incident, maintenance item, or component update.
|
||||
:::
|
||||
|
||||
A callback that renders nothing, and one whose message creation fails, both leave the identifier free for a later retry.
|
||||
|
||||
Reference in new issue
Block a user