Skip to content
Fluxer API

Reports

A safety report tells moderators that a message or a user profile broke the rules, and any user account can file one. Before a report is filed, the reporter answers a short series of questions. Fluxer serves those questions as a report flow, and the answers decide the reason the report is filed under. Digital Services Act notices need no account, only a verified email address, and they can also name a guild. The Admin Reports API covers reading, triaging, and closing a stored report.

OperationCredentialTarget
Get report flowNoneThe questions for one target type
Submit message report flowUser sessionOne message the reporter can read
Submit user report flowUser sessionOne user profile, optionally with a guild for context
Send DSA report emailNoneAn email address to verify
Verify DSA report emailNoneA verification code, exchanged for a ticket
Create DSA reportA ticketA message, user, or guild

Fluxer discards a body member that no schema here defines, so a misspelt member is dropped without an error. A body that is not parseable JSON returns 400 INVALID_FORM_BODY with the validation code INVALID_FORMAT at the body path.

Submit message report flow and Submit user report flow accept a user session token only. A bot token and an OAuth2 bearer credential are both rejected with 403 ACCESS_DENIED. The account must also be claimed and email-verified. An account holding no password and no SSO identity is rejected with 400 UNCLAIMED_ACCOUNT_CANNOT_SUBMIT_REPORTS, and one whose address is unverified with 403 REPORT_EMAIL_VERIFICATION_REQUIRED. An account with the report ban flag is rejected with 403 REPORT_BANNED.

Get report flow and 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, and Get report flow renders the copy in that account’s saved locale unless the request names one.

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.

The instance-wide content filter screens the JSON body of Submit message report flow and Submit user report flow before the route runs. A string of at least 3 characters matching the instance phrase blocklist, or a URL matching the instance URL blocklist, is rejected with 403 CONTENT_BLOCKED. Every string those two bodies define is an ID, a hash, or a locale, and the filter exempts all of them, so only a member the schema does not define can be blocked.

Every route under /reports/dsa is exempt by path, so Fluxer never screens a Digital Services Act notice. A notice about hate speech or a phishing link has to quote the slur or the URL it reports.

Every accepted submission writes one report and returns it as a report object. A report filed through a flow also stores its reason, the category that reason maps to, the submitted steps, the revision hash, and the resolved locale. Submission emits no Gateway Dispatch.

Two kinds of repeat are refused with 409 CONFLICT:

  • The same reporter reporting the same message again within 365 days of the earlier report, unless that report was deleted.
  • The same reporter reporting the same user again within 24 hours of the earlier report.

Nothing deduplicates a Digital Services Act notice.

Fluxer tells the reporter when a moderator resolves the report. A reporter with an account receives a system direct message, and an email when the account has an address. A Digital Services Act notice gets a receipt email at its verified address as soon as it is filed, and a decision email at the same address when it is resolved. Update report describes when no notice is sent.

Every report about a user, a guild, or a message an account sent also keeps a copy of the reported profile as it was at submission:

  • The account’s username, discriminator, display name, bio, pronouns, avatar, and banner.
  • The account’s guild nickname, bio, pronouns, join time, avatar, and banner, when the report names a guild the account is a member of.
  • The guild’s name, vanity URL code, icon, banner, and splash, for a report about a guild.

Fluxer copies each image into report storage. An image whose copy fails keeps only its hash, and the report is still filed. An account whose deletion has started is recorded by ID alone. Moderators read the snapshot as the report profile snapshot object.

A report flow is the set of screens a reporter moves through before a report is filed. Fluxer serves one flow for each target type. Get report flow returns every screen of it in one response, already translated, so a client renders the whole flow from that response and hard-codes no question.

Each screen is one of three kinds, told apart by the member that is set.

KindMemberWhat the reporter does
ChoiceoptionsPicks one option. At least one option is not a link
ChecklistchecklistTicks at least min_checked items, then continues
Infonext_screen_idReads the text and continues, optionally opening a link option first

An option’s outcome says what choosing it does. A checklist has one outcome for the whole screen. A screen with urgent set covers one of the most serious harms, such as threats, self-harm, sexual content, or a risk to a minor. A client shows an urgency notice on it and on the summary of every walk that passes through it.

A flow has up to two variants, one for each surface. in_app is the variant a signed-in client shows. dsa is the variant the public Digital Services Act form shows before it calls Create DSA report.

The two variants share their screens and differ in a few options. Only in_app has the options that end on the thank-you screen without a report, the options for a reporter worried about someone’s safety, and a copyright option that links to the copyright help page. Its user flow also starts with an info screen. On dsa the copyright option files a report instead. The guild flow exists on the dsa surface alone.

A self-hosted instance drops the copyright link. It keeps the crisis line, and keeps the guidelines links only when its operator has set a guidelines URL. Without one its flow has a null guidelines_url. Its in_app revision hashes therefore differ from those of the hosted instance, and differ again between a self-hosted instance with a guidelines URL and one without. Its dsa revision hashes match the hosted ones.

A walk starts at start_screen_id. Each screen the reporter passes through adds one step:

  • A choice screen records the chosen option_id.
  • A checklist screen records the ticked item_ids.
  • An info screen records its screen_id alone.

The walk follows screen outcomes and next_screen_id until an option or a checklist reaches a submit outcome. The client then shows a summary and submits every step in order. A link outcome opens its url and adds no step.

An end outcome finishes the walk without a report. The client shows the notice it names, or a thank-you screen when it names none. Such a walk is never submitted, and Fluxer rejects a submission that contains one.

The report is filed under the reason of the final submit outcome. A few checklist items name a more specific reason of their own, which takes precedence, and the flow object does not expose it.

Fluxer checks a submitted walk against the current variant. The first step must be the start screen, each later step must be the screen the previous answer led to, every answer must exist on its screen, and the last answer must reach a submit outcome.

revision_hash identifies the structure of one variant: its screens, options, items, and outcomes. It does not depend on the locale or the product name, and it changes whenever a screen, option, item, or outcome changes. A submission sends back the hash of the flow the reporter answered.

WalkHashResult
ValidCurrentThe report is filed
ValidOutdatedThe report is filed with the hash that was sent
InvalidCurrent400 INVALID_REPORT_FLOW_ANSWERS with step_index
InvalidOutdated409 REPORT_FLOW_OUTDATED

An option or item ID never changes meaning once it ships, so a valid walk from an outdated flow still means what the reporter saw. step_index is the zero-based index of the first step that does not fit the current flow. After REPORT_FLOW_OUTDATED, fetch the flow again and restart the walk.

Fluxer renders the copy in one supported locale. Get report flow uses its locale query parameter when one is sent, and the negotiated request locale otherwise. The response locale names the locale used, and the copy names the instance’s own product name.

Resolving a tag never fails. Matching ignores case and accepts underscores in place of hyphens, nb and nn resolve to no, and a bare language resolves to its preferred regional locale, so pt becomes pt-BR. A tag that matches nothing resolves to en-US.

A submission stores the locale the reporter saw, resolved the same way from its locale member, or the request locale when that member is omitted. The Admin Reports API describes a stored walk in English whatever that locale is.

Get report flow returns every screen of one variant in this object.

FieldTypeDescription
target_typestringReport flow target type
surfacestringReport flow surface
revision_hash1stringThe revision hash of this variant
localestringThe locale the copy was rendered in
start_screen_idstringThe ID of the screen a walk starts on
guidelines_url2?stringThe instance’s community guidelines page
screensarray[report flow screen object]Every screen of the variant
noticesarray[report flow notice object]Every notice an end outcome in the variant names

1 16 lowercase hexadecimal characters

2 The guidelines URL the operator set. Without one, a hosted instance links its own guidelines page and a self-hosted instance has null. A client links its summary disclaimer to it

An abridged user flow:

{
"target_type": "user",
"surface": "in_app",
"revision_hash": "9c41d2a07e5b18f3",
"locale": "en-US",
"start_screen_id": "profile_intro",
"guidelines_url": "https://fluxer.app/guidelines",
"screens": [
{
"id": "profile_intro",
"title": "Report profile",
"subtitle": "Tell us what's wrong with this profile. They won't be told who reported them.",
"urgent": false,
"options": [
{
"id": "learn_more",
"label": "Read the Fluxer Community Guidelines",
"outcome": {"type": "link", "screen_id": null, "reason": null, "notice_id": null, "url": "https://fluxer.app/guidelines"}
}
],
"options_heading": "Learn more",
"checklist": null,
"next_screen_id": "profile_parts"
},
{
"id": "profile_parts",
"title": "Which parts of their profile are a problem?",
"subtitle": "Pick as many as you need.",
"urgent": false,
"options": [],
"options_heading": null,
"checklist": {
"items": [
{"id": "photo", "label": "Pictures", "description": "What they use as avatar and banner"},
{"id": "name", "label": "Names", "description": "Username, display name or community nickname"},
{"id": "profile_text", "label": "Profile text", "description": "Bio, custom status or pronouns"}
],
"min_checked": 1,
"outcome": {"type": "screen", "screen_id": "root_user", "reason": null, "notice_id": null, "url": null}
},
"next_screen_id": null
}
],
"notices": [
{
"id": "need_more_info_profile",
"title": "We can't act on this yet",
"body": "To review this, we need to see where they say how old they are or make it clear they're under Fluxer's minimum age. Report that message or profile instead."
}
]
}

One question, checklist, or information page in a report flow.

FieldTypeDescription
idstringThe screen ID, unique within the flow
titlestringThe question or heading
subtitle?stringSupporting text shown under the title
urgentbooleanWhether the screen covers one of the most serious harms
options1array[report flow option object]The options, in display order
options_heading?stringA heading shown above the options
checklist?report flow checklist objectThe checklist, set only on a checklist screen
next_screen_id?stringThe screen the Next button leads to, set only on an info screen

1 Empty on a checklist screen, and holding only link options on an info screen

FieldTypeDescription
idstringThe option ID, unique within its screen
labelstringThe option text
outcomereport flow outcome objectWhat choosing the option does

Every member is always present. The members that do not apply to type are null.

FieldTypeDescription
typestringReport flow outcome type
screen_id?stringThe next screen when type is screen
reason1?stringThe report reason when type is submit
notice_id?stringThe notice to show when type is end, or null for the thank-you screen
url?stringThe page to open when type is link

1 A plain string, so a client MUST tolerate a reason the catalogue does not list yet

FieldTypeDescription
itemsarray[report flow checklist item object]The items, in display order
min_checkedintegerThe fewest items the reporter must tick before continuing
outcomereport flow outcome objectWhere the screen leads, always a screen or submit outcome
FieldTypeDescription
idstringThe item ID, unique within its checklist
labelstringThe item text
description?stringA second line shown under the label

A notice explains why a walk ended without a report, for example when a specific message has to be reported instead.

FieldTypeDescription
idstringThe notice ID
titlestringThe notice heading
bodystringThe notice text

One answered screen in a submission. A submission lists its steps in walk order.

FieldTypeDescription
screen_idstringThe ID of the screen (1-48 characters)
option_id?1stringThe ID of the chosen option (1-48 characters)
item_ids?1array[string]The IDs of the ticked items, 1-32 unique IDs of 1-48 characters each

1 Sent on a choice screen and a checklist screen respectively. An info screen’s step sends neither, and a step that sends the member its screen does not use is invalid

[
{"screen_id": "profile_intro"},
{"screen_id": "profile_parts", "item_ids": ["photo"]},
{"screen_id": "root_user", "option_id": "abuse"},
{"screen_id": "profile_abuse", "option_id": "harassment"}
]

Fluxer returns this receipt when it records a submission.

FieldTypeDescription
report_idsnowflakeThe snowflake identifying this report
status1stringReport status
reported_at2ISO8601 timestampTime the report row was written

1 Always pending

2 For a Digital Services Act notice, this is the time the notice was filed, even when its verification code was issued much earlier

{
"report_id": "1501314428688998182",
"status": "pending",
"reported_at": "2026-08-04T11:22:19.000Z"
}

The fixed acknowledgement returned by Send DSA report email.

FieldTypeDescription
okbooleanAlways true, because a failure is reported as an error status instead
{
"ok": true
}

The single credential Create DSA report accepts. Fluxer binds the ticket to the verified address it was issued for, and that address becomes the reporter identity on the resulting notice.

FieldTypeDescription
ticket1stringThe opaque credential Create DSA report consumes

1 64 lowercase hexadecimal characters encoding 32 random bytes, valid for one hour from verification, and consumed by the first Create DSA report that writes a notice with it

{
"ticket": "6b1f0a92c73d4e58a0b1c2d3e4f5061728394a5b6c7d8e9f0a1b2c3d4e5f6071"
}
ValueDescription
pendingThe report has been recorded and no moderator decision exists yet
resolvedA moderator has closed the report
unknownThe stored status is neither of the two values above

Only pending and resolved are ever stored. A client MUST still tolerate a status it does not recognise.

ValueDescription
messageA message, on both surfaces
userA user profile, on both surfaces
guildA guild, on the dsa surface only
ValueDescription
in_appThe flow a signed-in client shows, and the default
dsaThe flow the public Digital Services Act form shows
ValueDescription
screenGo to the screen named by screen_id
submitShow the summary, then file a report under reason
endFinish without a report and show the notice named by notice_id, or a thank-you screen
linkOpen url and stay on the current screen

A report filed through a flow stores a reason, together with the category that reason maps to for the report’s target type. Moderators can filter on both. The catalogue grows over time, so a client MUST tolerate a reason it does not list.

The second column gives the reason value, then the category it stores on a message report, a user report, and a guild report, in that order.

ReasonValue: message, user, guild category
Spamspam: spam, spam_account, spam
Fake or deceptive accountfake_account: spam, spam_account, spam
Scraping, automation or other platform abuseplatform_abuse: spam, spam_account, spam
Buying or selling accounts, logins or card detailsaccount_trading: illegal_activity, spam_account, illegal_activity
Faked member or reaction countsmetric_manipulation: spam, spam_account, spam
Harassment or bullyingharassment: harassment, harassment, harassment
Stalking or unwanted contactunwanted_contact: harassment, harassment, harassment
Raid or coordinated attackraid: harassment, harassment, raid_coordination
False claims used to target a personfalse_claims_about_person: harassment, harassment, harassment
Slurs, hateful symbols or stereotypeshate_slurs: hate_speech, hate_speech, hate_speech
Dehumanizing a grouphate_dehumanizing: hate_speech, hate_speech, hate_speech
Calls for violence or exclusion against a group1hate_incitement: hate_speech, hate_speech, hate_speech
Genocide or atrocity denial or celebrationatrocity_denial: hate_speech, hate_speech, hate_speech
Misgendering, deadnaming, outing or identity deniallgbtq_targeting: hate_speech, hate_speech, hate_speech
Promoting conversion therapyconversion_therapy: hate_speech, hate_speech, hate_speech
Other attacks on people for who they arehate_other: hate_speech, hate_speech, hate_speech
Threat of violence1violent_threat: violent_content, harassment, harassment
Encouraging violence or instructions to harm1violence_incitement: violent_content, inappropriate_profile, violent_content
Glorifying real-world violenceviolence_glorification: violent_content, inappropriate_profile, violent_content
Real violence shown to shockgraphic_violence: violent_content, inappropriate_profile, violent_content
Terrorism or violent extremismterrorism_extremism: violent_content, inappropriate_profile, extremist_community
False claims likely to cause serious harmharmful_false_claims: other, other, other
Fake or AI media presented as realdeceptive_synthetic_media: other, other, other
Sexual images or messages nobody asked forsexual_unwanted: harassment, harassment, harassment
Adult content outside an 18+ spaceadult_content_unmarked: nsfw_violation, inappropriate_profile, nsfw_violation
Intimate images shared without consent1intimate_image_abuse: doxxing, inappropriate_profile, harassment
Sexual exploitation, coercion or trafficking1sexual_exploitation: illegal_activity, inappropriate_profile, illegal_activity
Sexualized fictional child1minor_fictional_sexualization: child_safety, child_safety, child_safety
Sexual comments about minors1minor_sexualization: child_safety, child_safety, child_safety
Flirting with or sexually messaging a minor1minor_sexual_contact: child_safety, child_safety, child_safety
A minor sharing sexual content themselves1minor_sexual_content: child_safety, child_safety, child_safety
Child sexual abuse material1csam: child_safety, child_safety, child_safety
Grooming a minor1minor_grooming: child_safety, child_safety, child_safety
Predatory contact with a minor1minor_contact: child_safety, child_safety, child_safety
A minor in an 18+ spaceminor_in_adult_space: child_safety, child_safety, child_safety
Community for dating between minors1minor_dating_community: child_safety, child_safety, child_safety
Private information shared without consentdoxxing: doxxing, inappropriate_profile, harassment
Under the minimum ageunderage: underage_user, underage_user, underage_user
Worried about someone’s safety1wellbeing_concern: self_harm, other, self_harm
Encouraging self-harm or sharing methodsself_harm_promotion: self_harm, inappropriate_profile, self_harm
Encouraging suicidesuicide_promotion: self_harm, inappropriate_profile, self_harm
Encouraging disordered eatingeating_disorder_promotion: self_harm, inappropriate_profile, self_harm
Graphic self-harm imagesself_harm_graphic: self_harm, inappropriate_profile, self_harm
Pressuring someone to harm themselvesself_harm_pressure: self_harm, inappropriate_profile, self_harm
Pretending to be staff or supportimpersonation_staff: impersonation, impersonation, impersonation
Impersonating a personimpersonation_person: impersonation, impersonation, impersonation
Impersonating a well-known personimpersonation_public_figure: impersonation, impersonation, impersonation
Impersonating a company, brand or groupimpersonation_organization: impersonation, impersonation, impersonation
Conning people out of money or accountsfraud: illegal_activity, spam_account, illegal_activity
Drug, weapon or other illegal salesillegal_goods: illegal_activity, inappropriate_profile, illegal_activity
Other illegal activityillegal_activity: illegal_activity, inappropriate_profile, illegal_activity
Phishing, malware or hacking attemptsmalware_phishing: malicious_links, spam_account, malware_distribution
Getting around a banban_evasion: other, other, other
Community allows serious rule-breakingpermits_violations: other, other, other
Copyright or trademark complaintcopyright: other, other, other
Something not listedother: other, other, other

1 Highest priority, so moderators see a report with this reason first

Every report stores one category from this registry. A report filed through a flow stores the category its reason maps to for the target type.

ValueDescription
harassmentHarassment, bullying, or intimidation
hate_speechHatred against protected groups
violent_contentContent depicting or promoting violence
extremist_communityA guild promoting extremist or terrorist ideologies
raid_coordinationA guild coordinating attacks on other communities
spamUnsolicited bulk messages or promotional content
spam_accountAn account used for spamming or bulk messaging
nsfw_violationAdult content posted outside age-restricted channels
inappropriate_profileInappropriate or offensive profile content
illegal_activityContent promoting or facilitating illegal activities
doxxingContent revealing private personal information
self_harmContent promoting self-harm or suicide
child_safetyContent that endangers minors or depicts child abuse
underage_userSomeone who appears to be under the minimum required age
malicious_linksLinks to malware, phishing, or other malicious sites
malware_distributionDistribution of malware or malicious software
impersonationFalsely claiming to be another person or entity
otherAnything the other categories do not cover

A user report only ever stores harassment, hate_speech, spam_account, impersonation, underage_user, inappropriate_profile, child_safety, or other.

No instance configuration changes this registry. A value outside it returns 400 INVALID_FORM_BODY.

ValueDescription
ATAustria
BEBelgium
BGBulgaria
HRCroatia
CYCyprus
CZCzechia
DKDenmark
EEEstonia
FIFinland
FRFrance
DEGermany
GRGreece
HUHungary
IEIreland
ITItaly
LVLatvia
LTLithuania
LULuxembourg
MTMalta
NLNetherlands
PLPoland
PTPortugal
RORomania
SKSlovakia
SISlovenia
ESSpain
SESweden
GET/v1/reports/flows/{target_type}Unauthenticated

Returns the report flow object for one target type and surface, with its copy rendered in one locale.

The response has Cache-Control: private, no-cache and Vary: Accept-Language. A client can keep the flow for a session, and no-cache makes the refetch after 409 REPORT_FLOW_OUTDATED reach Fluxer instead of a stored copy.

FieldTypeDescription
target_typestringReport flow target type
FieldTypeDescription
surface?stringReport flow surface (default in_app)
locale?1stringThe language tag of the client UI (1-35 characters)

1 Resolved as Locale describes, and the request locale is used when it is omitted. A client SHOULD send it, because the language of its UI can differ from the account’s saved locale

StatusBodyCondition
200report flow objectThe flow was rendered
400error responseThe target type or surface is unknown and the request returns INVALID_FORM_BODY
400error responseguild was requested on a surface other than dsa and the request returns INVALID_FORM_BODY

40 requests per 10 seconds, on the report:list bucket.

POST/v1/reports/flows/message/submissions

Files a report about a message from the reporter’s answers to the message flow on the in_app surface, and returns a report object. The reporter must be able to read the message.

A message a bot sent is reported as the bot account, like any other account. A message a webhook sent has no author account, so the report records the webhook instead, with the name and avatar the message was sent with.

  • The reporter must be able to open the containing channel and read the message.
  • A reporter who authored the message receives 400 CANNOT_REPORT_OWN_MESSAGE.
  • A channel that does not exist and a direct message the reporter is not a recipient of both return 404 UNKNOWN_CHANNEL.
  • A guild channel the reporter is not a member of returns 403 ACCESS_DENIED, and a member without VIEW_CHANNEL receives 403 MISSING_PERMISSIONS.
  • A thread the reporter cannot view returns 403 MISSING_ACCESS, as Channel resolution describes. A thread, a forum channel, or a media channel returns 404 UNKNOWN_CHANNEL to a user request without the channel_threads client capability.
  • An age-restricted guild channel returns 403 NSFW_CONTENT_AGE_RESTRICTED for an account that is not age verified.
  • A message not stored under channel_id, a message with neither an author account nor a webhook, and any message a reporter without READ_MESSAGE_HISTORY cannot reach all return 404 UNKNOWN_MESSAGE.
  • An author whose account no longer exists returns 404 UNKNOWN_USER.

Fluxer checks the answers right after the claimed account and verified email checks, before the report ban check and before it reads the channel. An invalid walk still counts against the report:create bucket but uses none of the further allowances below.

FieldTypeDescription
channel_idsnowflakeThe ID of the channel containing the reported message
message_idsnowflakeThe ID of the reported message
revision_hashstringThe revision_hash of the flow the reporter answered (1-64 characters)
stepsarray[report flow step object]The answered screens in walk order (1-16 steps)
locale?1stringThe language tag the reporter saw the flow in (1-35 characters)

1 Stored with the report after it is resolved as Locale describes. The request locale is stored when it is omitted

StatusBodyCondition
200report objectReport was created
400error responseThe answers do not fit the current flow and the request returns INVALID_REPORT_FLOW_ANSWERS
400error responseThe account is unclaimed and the request returns UNCLAIMED_ACCOUNT_CANNOT_SUBMIT_REPORTS
400error responseThe reporter is the message author and the request returns CANNOT_REPORT_OWN_MESSAGE
403error responseA body string is blocked and the request returns CONTENT_BLOCKED
403error responseThe credential is a bot token or OAuth2 bearer and the request returns ACCESS_DENIED
403error responseThe reporter is not a member of the guild and the request returns ACCESS_DENIED
403error responseIts email is unverified and the request returns REPORT_EMAIL_VERIFICATION_REQUIRED
403error responseThe account is report banned and the request returns REPORT_BANNED
403error responseThe reporter lacks VIEW_CHANNEL and the request returns MISSING_PERMISSIONS
403error responseThe reporter cannot view the thread and the request returns MISSING_ACCESS
403error responseThe channel is age restricted and the request returns NSFW_CONTENT_AGE_RESTRICTED
404error responseThe channel, the message, or the message author is not visible to the reporter
409error responseThe answers came from an outdated flow and do not fit the current one, returning REPORT_FLOW_OUTDATED
4091error responseThe reporter already reported this message, returning CONFLICT

1 The reporter, the channel, and the message form a submission key that expires 365 days after the report. A second report of the same message by the same reporter within that time is refused whichever answers it sends. A submission that fails or is rate limited releases the key, and so does deleting the report

The operation writes one report with a frozen evidence snapshot. The snapshot records the reporter’s account ID, the message author’s identity and avatar hash, the channel and guild identity, and the guild and channel NSFW and content warning state. For a message a webhook sent it records the webhook’s ID and the name and avatar hash the message was sent with in place of the author. It also records the webhook’s own name, avatar hash, type, channel, guild, and creation time, and the ID, username, discriminator, display name, and avatar hash of the account that created the webhook. Only the creator ID is recorded when that account has been deleted. The creator is recorded for moderators and is not the reported account. The report also stores the reason, its category, the steps, the revision hash, and the resolved locale.

For a message an account sent, the report also keeps a profile snapshot of the author, with the author’s guild profile when the channel is in a guild.

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 message a webhook sent is recorded under its webhook. A context message whose author account no longer exists stays in the window under a deleted user identity that keeps the author ID. An attachment whose copy fails, and an embedded file that is no longer stored, is listed as missing on its context message with its metadata, and the request still succeeds. When the reporter lacks READ_MESSAGE_HISTORY, the window holds only messages created at or after the earlier of the guild’s message history cutoff and the time the reporter joined the guild.

10 requests per hour, on the report:create bucket, which is shared with Submit user report flow.

Further allowances apply, each counted over one hour:

  • One reporter creates at most 5 reports across all report types.
  • One reporter creates at most 3 message reports in one channel.
  • One message receives at most 20 reports across all reporters.
  • When the reported message is in a guild, one reporter creates at most 4 message reports in that guild.

A duplicate submission returns 409 CONFLICT before any of them is consumed.

POST/v1/reports/flows/user/submissions

Files a report about another user’s profile from the reporter’s answers to the user flow on the in_app surface, and returns a report object. The report can name the guild the reporter saw the profile in.

  • A reporter naming their own account receives 400 CANNOT_REPORT_YOURSELF.
  • A user_id that resolves to no account returns 404 UNKNOWN_USER.
  • A guild_id that resolves to no guild, or to a guild the reporter is not a member of, returns 404 UNKNOWN_GUILD.

Fluxer checks the answers right after the claimed account and verified email checks, before the report ban check and before it looks up the user. An invalid walk still counts against the report:create bucket but not against the per-reporter allowance below.

FieldTypeDescription
user_idsnowflakeThe ID of the reported user
guild_id?snowflakeThe ID of the guild in which the reporter saw the profile
revision_hashstringThe revision_hash of the flow the reporter answered (1-64 characters)
stepsarray[report flow step object]The answered screens in walk order (1-16 steps)
locale?1stringThe language tag the reporter saw the flow in (1-35 characters)

1 Stored with the report after it is resolved as Locale describes. The request locale is stored when it is omitted

StatusBodyCondition
200report objectReport was created
400error responseThe answers do not fit the current flow and the request returns INVALID_REPORT_FLOW_ANSWERS
400error responseThe account is unclaimed and the request returns UNCLAIMED_ACCOUNT_CANNOT_SUBMIT_REPORTS
400error responseThe reporter is the reported user and the request returns CANNOT_REPORT_YOURSELF
403error responseA body string is blocked and the request returns CONTENT_BLOCKED
403error responseThe credential is a bot token or OAuth2 bearer and the request returns ACCESS_DENIED
403error responseIts email is unverified and the request returns REPORT_EMAIL_VERIFICATION_REQUIRED
403error responseThe account is report banned and the request returns REPORT_BANNED
404error responseThe reported user does not exist and the request returns UNKNOWN_USER
404error responseThe supplied guild_id is not a guild the reporter is in and the request returns UNKNOWN_GUILD
409error responseThe answers came from an outdated flow and do not fit the current one, returning REPORT_FLOW_OUTDATED
4091error responseThe reporter already reported this user within 24 hours, returning CONFLICT

1 The reporter and the reported user form a submission key that expires 24 hours after the report. A submission that fails or is rate limited releases the key, and so does deleting the report

The operation writes one report. It records the reporter’s account ID, and the reported user’s ID and avatar hash. A supplied guild_id adds the guild’s ID, name, icon hash, NSFW flag, and content warning state. The report also stores the reason, its category, the steps, the revision hash, and the resolved locale. The steps name the profile parts the reporter selected, and the report keeps a profile snapshot of the account, with its guild profile when guild_id is supplied. No message evidence is captured.

10 requests per hour, on the report:create bucket, which is shared with Submit message report flow.

One reporter creates at most 5 reports each hour across all report types. Fluxer checks that allowance right after the report ban check and before it looks up the user, so a reporter who has used it up receives 429 even for a duplicate. Otherwise a duplicate submission returns 409 CONFLICT without consuming it.

POST/v1/reports/dsa/email/sendUnauthenticated

Issues a Digital Services Act verification code for an email address and returns a success object.

An instance where members sign in with a username sends no email, and returns 400 EMAIL_UNAVAILABLE_ON_INSTANCE before it issues a code.

The address must be deliverable. A domain with no MX, A, or AAAA record returns 400 INVALID_FORM_BODY with EMAIL_DOMAIN_CANNOT_RECEIVE_MAIL on email. A temporary DNS failure does not reject the address.

FieldTypeDescription
email1stringThe address that receives the verification code (1-254 characters)

1 Trimmed and lowercased before use, so two spellings that differ only in case address the same verification record

StatusBodyCondition
2001success objectThe code was generated and handed to the email provider
400error responseThe address failed validation and the request returns INVALID_FORM_BODY
400error responseThe domain publishes no usable mail DNS record and the request returns INVALID_FORM_BODY
429error responseThe address was sent a code in the last minute, or used its 3 sends this hour
503error responseThe email could not be sent and the request returns SERVICE_UNAVAILABLE

1 Also returned on an instance with email delivery switched off, where no email leaves the instance

The code expires after 10 minutes. Codes use uppercase letters and digits in XXXX-XXXX form.

A new code can go to the same address once a minute has passed since the last one, or once that code has expired. It replaces the previous code and restarts the 10 minute window. A request within the minute returns 429 with retry_after and leaves the code already sent usable.

When the provider refuses the message, or the address is recorded as a hard bounce, Fluxer keeps no code for the address, so a code sent to it earlier stops working too, and returns 503 SERVICE_UNAVAILABLE. That attempt still counts towards the 3 sends an address gets each hour.

The operation creates no ticket and emits no Gateway Dispatch.

5 requests per hour, on the dsa:report:email:send bucket.

One address gets at most 3 sends each hour, and at most one each minute while its last code is unexpired. The minute check runs first, so a request it refuses does not count towards the hourly 3. Both return 429 RATE_LIMITED with retry_after.

POST/v1/reports/dsa/email/verifyUnauthenticated

Consumes a verification code and returns the ticket object that Create DSA report requires. An instance where members sign in with a username returns 400 EMAIL_UNAVAILABLE_ON_INSTANCE.

FieldTypeDescription
email1stringThe address that received the verification code (1-254 characters)
codestringThe nine-character code in XXXX-XXXX form, using uppercase letters and digits

1 Trimmed and lowercased before the verification record is looked up, exactly as Send DSA report email normalises it

StatusBodyCondition
200ticket objectThe code matched the unexpired verification record for the address
4001error responseThe address or code failed validation, or no unexpired record matches, returning INVALID_DSA_VERIFICATION_CODE

1 An address with no verification record, an expired record, a wrong code, and an attempt over the per-address limit all produce the same error code. A malformed address or code fails schema validation and returns 400 INVALID_FORM_BODY.

The operation deletes the verification record and writes one ticket that expires one hour later. A wrong code leaves the outstanding code usable until it expires or the address reaches the attempt limit below. The operation emits no Gateway Dispatch.

Each address has 5 verification attempts in 10 minutes, counted across letter case, and right and wrong codes both count. The attempt over that limit deletes the outstanding code and returns 400 INVALID_DSA_VERIFICATION_CODE. Sending a new code starts a new count.

10 requests per hour, on the dsa:report:email:verify bucket.

POST/v1/reports/dsaUnauthenticated

Consumes a ticket, files the notice from the reporter’s answers to the flow on the dsa surface, and returns a report object. This is the only way to report a guild.

Fetch the flow with Get report flow and surface=dsa for the target type the notice is about, walk it as Walking a flow describes, and send the steps with the details below. report_type selects which target fields the body has.

A ticket that is unknown or past its one hour expiry returns 400 INVALID_DSA_TICKET before the answers are checked. The ticket is consumed when the notice is written, so a notice that is rejected or rate limited leaves it usable. Nothing deduplicates a repeat notice.

Article 16(2)Fields
(a) An explanation of the problemadditional_info
(b) The exact location of the contentmessage_link, user_id, user_tag, guild_id, invite_code
(c) The name and email address of the reporterreporter_full_legal_name, ticket
(d) A statement of good faithgood_faith_confirmed

A notice whose walk resolves to the child_safety category may omit the legal name, as Article 16(2)(c) allows for child sexual abuse material. Every other notice needs one, and Fluxer checks this after it resolves the walk.

FieldTypeDescription
ticketstringThe ticket returned by Verify DSA report email (1-128 characters)
report_typestringThe discriminator selecting the variant, one of message, user, or guild
revision_hashstringThe revision_hash of the dsa flow the reporter answered (1-64 characters)
stepsarray[report flow step object]The answered screens in walk order (1-16 steps)
additional_info1stringThe reporter’s explanation of the problem (1-1000 characters)
good_faith_confirmedbooleanAlways true, confirming the notice is accurate and complete to the reporter’s knowledge
reporter_full_legal_name?2stringThe full legal name of the person filing the notice (1-160 characters)
reporter_country_of_residencestringEU country code of the reporter’s residence
locale?3stringThe language tag the reporter saw the flow in (1-35 characters)

1 A value that is empty or only whitespace returns 400 INVALID_FORM_BODY. When the reporter believes the content is illegal, the explanation says which law it breaks and why

2 Required unless the walk resolves to the child_safety category. A missing name otherwise returns 400 INVALID_FORM_BODY on reporter_full_legal_name

3 Stored with the notice after it is resolved as Locale describes. The request locale is stored when it is omitted

FieldTypeDescription
message_link1stringThe link to the reported message (1-2048 characters)
reported_user_tag?2stringThe message author’s tag in username#1234 form, or the bare username on an instance without user tags (1-37 characters)

1 Parsed as an absolute URL whose path is /channels/<guild_id>/<channel_id>/<message_id>, with @me in place of the guild ID for a direct message or group DM. Fluxer ignores the origin

2 When supplied it must be the tag of the account that authored the linked message. A message a webhook sent has no author account, so no tag matches it

The link must name a message that exists, in the channel it names, and in the guild that channel belongs to. Every link or tag that does not resolve returns the same 404 UNKNOWN_MESSAGE, and the ticket stays usable. That covers a value that is not an absolute URL, a path with fewer than four segments, a first segment other than channels, a channel or message segment that is not a decimal snowflake, and a guild segment that does not match the channel. It also covers a missing channel or message, a message stored in another channel, a message whose author account no longer exists, and a tag that does not match the author.

FieldTypeDescription
user_id?1snowflakeThe ID of the reported user
user_tag?1stringThe reported user’s tag in username#1234 form, or the bare username on an instance without user tags (1-37 characters)

1 Supply at least one of the two. A body that supplies neither returns 400 INVALID_FORM_BODY, and an ID or tag that resolves to no account returns 404 UNKNOWN_USER

When both are supplied they must resolve to the same account. A mismatch returns 400 INVALID_DSA_REPORT_TARGET.

FieldTypeDescription
guild_idsnowflakeThe ID of the reported guild
invite_code?1stringInvite code or invite URL for the reported guild (1-64 characters)

1 This variant checks no access to the guild, so the code is never required. When supplied it is reduced to its final / separated segment, and a complete invite URL is accepted

An unknown code returns 404 UNKNOWN_INVITE. A code that resolves to an invite of another type, or to an invite for another guild, returns 400 INVALID_DSA_REPORT_TARGET.

StatusBodyCondition
200report objectThe notice was recorded
400error responseThe ticket is unknown or expired and the request returns INVALID_DSA_TICKET
400error responseA user ID and tag name different accounts, and the request returns INVALID_DSA_REPORT_TARGET
400error responseA user tag is malformed and the request returns INVALID_DSA_REPORT_TARGET
400error responseThe invite code belongs to another guild or type and the request returns INVALID_DSA_REPORT_TARGET
400error responseThe answers do not fit the current dsa flow and the request returns INVALID_REPORT_FLOW_ANSWERS
400error responseA required field is missing or malformed and the request returns INVALID_FORM_BODY
404error responseThe message link or author tag does not resolve and the request returns UNKNOWN_MESSAGE
404error responseThe reported user, guild, or invite does not exist
409error responseThe answers came from an outdated flow and do not fit the current one, returning REPORT_FLOW_OUTDATED

The operation resolves the target, deletes the ticket, and writes one report under a newly generated snowflake.

A message notice about a guild channel that @everyone can view and read the history of captures the same conversation window as Submit message report flow, including the byte copy of every attachment. For any other guild channel, and for a direct message or group DM, it captures only the reported message and its attachments. A message notice also keeps a profile snapshot of an author account, with the author’s guild profile in a guild channel.

A user notice records the reported user’s ID, avatar hash, and profile snapshot. A guild notice records the guild’s ID, name, icon hash, NSFW flag, content warning state, and the sanitised invite code when one was supplied, together with the guild’s profile snapshot.

Every notice also records additional_info, the reporter’s legal name when one was supplied, the country of residence, the good faith confirmation, and the verified address behind the ticket. It stores the reason, its category, the steps, the revision hash, and the resolved locale like any flow report. A notice has no account ID even when the request presented a credential.

Once the notice is stored, Fluxer emails a receipt with the report ID to the verified address. A receipt that cannot be sent is logged and does not fail the request. When a moderator resolves the notice, the same address receives the decision.

5 requests per hour, on the dsa:report:create bucket. The verified address behind the ticket files at most 5 reports each hour.