Skip to content
Fluxer API

Enforcement decisions

An enforcement decision records why staff restricted an account or community. The route that applies the restriction records the decision in the same request, and the decision sends a notice to the holder. Every route that suspends, warns, limits, closes or removes something takes a decision or a basis.

The restriction itself is still stored on the account, as temp_banned_until and the DISABLED flag for a suspension or pending_deletion_at and the DELETED flag for a closure. The account's enforcement_decision_id is the suspension or closure decision behind its current state.

TermMeaning
subjectThe account, bot or community the decision is about
holderThe person who gets the notice, which is the account itself, a bot's owner or a community's owner
kindwarning, suspension, closure, feature_limit, content_removal, profile_reset or community_removal
reason codeAn enforcement deletion reason. Codes 1, 2, 19, 32 and 33 are refused
handlingstandard, protected or restricted, set by the reason code. It decides how much detail the notice gives
evidenceA snapshot of the subject's messages, or for a community the messages posted in it, taken when the decision is made
notesInternal staff notes. The X-Audit-Log-Reason header of the request is the first note
noticeThe statement of reasons the holder gets by email, system DM or at sign-in
HandlingReason codesNotice detail
standardEvery other codeThe rule, where and when it happened, and the note to the user
protected7, 10, 11, 15, 23The same, but messages in direct and group conversations show the month only
restricted9, 13, 24The rule only, with no locations and no note to the user

A restricted reason allows only a permanent suspension or a content removal, and its evidence keeps only the message ID, channel ID, time and attachment count.

A decision is active until staff end it as lifted or reversed. Fluxer ends a temporary suspension that ran out as expired when the holder next signs in, or in a task that runs every six hours. The system user (ID 0) is ended_by, no notice is sent, and staff can still end the decision as reversed.

The API computes two more states each time it returns a decision, and never stores them.

  • expired: an active suspension or feature limit whose ends_at has passed, before Fluxer ends it
  • replaced: an active suspension or closure that a later decision replaced

A closure replaces an active suspension and lifts it. A replaced decision gets no ended email.

Routes whose action is not always enforcement take a basis in place of a decision. Only the enforcement basis records a decision and sends a notice.

FieldTypeDescription
typestringenforcement (a rule was broken), holder_request (the holder asked) or housekeeping (no conduct involved)
decision?1enforcement decision input objectWhy the subject is restricted

1 Required for enforcement and refused for the others. A bulk job takes a narrower decision

RouteBases
Schedule user deletionenforcement, holder_request, housekeeping
Clear user profile fieldsenforcement, holder_request, housekeeping
Shred user messagesenforcement, holder_request, housekeeping
Delete all user messagesenforcement, holder_request, housekeeping
Delete messageholder_request, housekeeping
Delete guildenforcement, holder_request

Each feature limit decision limits one feature, and ending one never touches another.

LimitFlagReason codes
direct_messagesSPAMMER3, 6, 8, 18, 21
reportsREPORT_BANNED29
purchasesPURCHASE_DISABLED12, 14

Only a reports limit ends, after 90 days by default and at most 365. The next report from the account after a reports limit's ends_at clears REPORT_BANNED and is accepted.

Shred user messages snapshots the first 50 messages as evidence, or every message for a restricted reason, before it queues the job, and it refuses evidence. Delete all user messages refuses restricted reasons.

A profile reset saves the old values of the cleared fields as profile_values and needs no evidence or statement.

A community removal has the community as its subject and sends the notice to its owner. Evidence must be posted in that community, linked reports must be about it, and restricted reasons are refused.

Removed content cannot be restored, and these decisions end only as reversed.

A decision that resolves linked reports tells each reporter what happened to the item.

OutcomeDecisions
removedA removed message, a profile reset or a community removal
restrictedA suspension, closure or feature limit
warnedA warning, and the reporter is told only that action was taken

A content removal or profile reset that leaves the reported item in place leaves the report pending. Update report sets the outcome for a manual resolution.

A permanent suspension, or a closure for a broken rule, can block the networks the account used in the last 30 days. Set block_networks on Suspend user or Schedule user deletion, and use List network block candidates to see which addresses qualify. The blocks last 90 days unless the request sets another length, and they are removed when the decision ends. These are separate from plain IP blocks.

Fluxer never blocks an exempt address or a special-purpose range, such as a private, loopback or link-local range, and leaves an address that is already blocked as it is. It lists all of these in skipped_ips. It refuses an address where another account that is not suspended or closed was last active, matching IPv6 addresses by their /64.

The notice says the networks were blocked and until when, and never shows an address.

A decision with automation set to automated waits in the review queue until a person ends it or records a warning, suspension or closure on the same account. Its decided_by is the system user (ID 0).

Every route that records a decision takes this object as decision.

FieldTypeDescription
reason_codeintegerAn enforcement deletion reason
sourcestringreport after a report, own_review otherwise
automationstringflagged when automated tools flagged it first, none otherwise
report_ids?1array[snowflake]Reports about the subject that the decision resolves (at most 20)
evidence?2array[object]channel_id and message_id of messages the decision relies on (at most 50)
statement?3stringNote to the user, shown in the notice (1-500 characters)
rights_holder?4stringThe rights holder or the right concerned (1-200 characters)
confirmations?5array[string]evidence_preserved and reports_filed_or_not_required

1 Required when source is report. Each report must be about the subject. Pending reports are resolved and their reporters told the outcome. The subject's messages in each report's snapshot, or for a community the messages posted in it, become evidence too

2 Every message must exist and be the subject's own, or for a community be posted in it. A message in a direct or group conversation needs a linked report that covers it

3 The decision needs an evidence message or a statement, except for a profile reset. Restricted reasons refuse it

4 Required for code 31 (COPYRIGHT) and refused for every other code

5 Both are required for restricted reasons

The X-Audit-Log-Reason header is required on every route that takes a decision.

Fluxer checks ACLs before it looks anything up. Evidence needs message:lookup, remove_evidence needs message:shred, reports need report:view and report:resolve, and block_networks needs ban:ip:add and user:view:ip. Every other failure is a 400 INVALID_FORM_BODY with one of these codes.

  • ENFORCEMENT_INTERNAL_NOTE_REQUIRED when the X-Audit-Log-Reason header is missing
  • ENFORCEMENT_KIND_NOT_ALLOWED when the reason code does not allow this kind or action, or a bot is given anything other than a suspension or closure
  • ENFORCEMENT_LIMIT_NOT_ALLOWED when the reason code does not allow this feature limit
  • ENFORCEMENT_LIMIT_END_INVALID when a reports limit ends in the past or after 365 days, or another limit has an end
  • ENFORCEMENT_REPORT_REQUIRED when source is report and no report is linked
  • ENFORCEMENT_REPORT_MISMATCH when a linked report does not exist or is about someone else
  • ENFORCEMENT_EVIDENCE_NOT_ALLOWED when a shred has evidence messages
  • ENFORCEMENT_EVIDENCE_NOT_FOUND when an evidence message does not exist
  • ENFORCEMENT_EVIDENCE_NOT_SUBJECT when an evidence message is not the subject's, or not posted in the community
  • ENFORCEMENT_EVIDENCE_DM_UNREPORTED when an evidence message is in a conversation no linked report covers
  • ENFORCEMENT_FACTS_REQUIRED when there is no evidence message and no statement
  • ENFORCEMENT_EVIDENCE_REQUIRED when a restricted reason has no evidence, or remove_evidence has nothing to remove
  • ENFORCEMENT_STATEMENT_NOT_ALLOWED when a restricted reason has a statement
  • ENFORCEMENT_PERMANENT_REQUIRED when a restricted reason has a temporary suspension
  • ENFORCEMENT_CONFIRMATIONS_REQUIRED when a restricted reason is missing a confirmation
  • ENFORCEMENT_RIGHTS_HOLDER_REQUIRED when code 31 has no rights_holder
  • ENFORCEMENT_RIGHTS_HOLDER_NOT_ALLOWED when another code has a rights_holder
  • ENFORCEMENT_NETWORK_BLOCK_NOT_ALLOWED when block_networks is given with a temporary suspension or a closure not for a broken rule
  • ENFORCEMENT_NETWORK_UNKNOWN when the account did not use an address in block_networks in the last 30 days
  • ENFORCEMENT_NETWORK_SHARED when another unrestricted account was last active on an address in block_networks

A person's suspension or closure notice is sent by email only. Every other notice, including any notice about a bot, is also sent as a system DM to the holder. notice_status is undelivered when no channel can deliver the notice. A suspension or closure notice that was not sent is delivered at the holder's next sign-in. The notice uses the instance's own name.

FieldTypeDescription
decision_idsnowflakeThe ID of the decision
subject_typestringuser or guild
subject_idsnowflakeThe ID of the account, bot or community
subject_name?stringThe bot username or community name when the decision was made, null for a person's own account
recipient_user_id?snowflakeThe holder, or null when there is none
kindstringThe kind of decision
reason_codeintegerThe deletion reason
sourcestringreport or own_review
automationstringnone, flagged or automated
statement?stringNote to the user
rights_holder?stringThe rights holder or the right concerned
ends_at?ISO8601 timestampWhen a suspension or feature limit ends, null when permanent
deletion_at?ISO8601 timestampWhen a closed account is deleted
limit?stringdirect_messages, reports or purchases for a feature limit
profile_fieldsarray[string]The fields a profile reset cleared
removed_message_count?integerMessages the decision removed
removed_all_messagesbooleanWhether every message of the account was removed
payment_at?ISO8601 timestampThe charge a payment decision is about
report_ids1array[snowflake]Linked reports
decided_bysnowflakeThe staff member who decided, or 0 for an automated decision
created_atISO8601 timestampWhen the decision was made
statusstringactive or ended
end_outcome?stringlifted, reversed or expired
ended_at?ISO8601 timestampWhen the decision ended
ended_by?snowflakeWho ended it
end_statement?stringNote to the user sent with the ended email
notice_statusstringpending, sent or undelivered
notice_channelsarray[string]email, system_dm and sign_in
notice_locale?stringThe locale the notice was written in
notice_text?stringThe plain text that was sent
notice_sent_at?ISO8601 timestampWhen the holder got the notice
expiredbooleanSee Lifecycle
replacedbooleanSee Lifecycle

1 Empty without report:view

Get enforcement decision adds these fields.

FieldTypeDescription
notesarray[object]Internal notes in order, each with note_id, author_id, body and created_at
evidence1array[object]Snapshot messages, each with message_id, channel_id, timestamp, content, attachments and attachment_count
profile_values1?objectProfile values before a reset
network_blocks2array[object]The decision's unexpired network blocks, each with ip and expires_at
noticeobjectThe notice as the holder sees it, in English

1 Message content, attachment details and profile values are null without message:lookup. Evidence is deleted a year after the decision, and each note a year after it was written

2 Each ip is masked to its /24 or /64 without user:view:ip

GET/v1/admin/enforcement-decisions

Lists the decisions about one account or community, newest first, or the review queue. You need enforcement:view.

FieldTypeDescription
review_queue?1booleanList the automated decisions waiting for a person
subject_type?stringuser or guild
subject_id?snowflakeThe ID of the account or community
limit?integerMaximum number of decisions (1-100, default 50)
before?snowflakeReturn decisions older than this decision

1 Without review_queue=true, subject_type and subject_id are required

FieldTypeDescription
decisionsarray[enforcement decision object]The decisions, without the fields Get enforcement decision adds
StatusBodyCondition
200response bodyThe decisions were listed

The rate limit is 200 requests per minute, on the admin:lookup bucket.

GET/v1/admin/enforcement-decisions/{decision_id}

Returns one decision with its notes, evidence and notice. You need enforcement:view.

StatusBodyCondition
200enforcement decision objectThe decision was found
404error responseUNKNOWN_ENFORCEMENT_DECISION when no decision has this ID

The rate limit is 200 requests per minute, on the admin:lookup bucket.

POST/v1/admin/enforcement-decisions/{decision_id}/notesAudit reason

Appends an internal note that staff can see and nobody can change. You need enforcement:annotate.

FieldTypeDescription
bodystringThe note (1-2000 characters)
FieldTypeDescription
note_idsnowflakeThe ID of the note
author_idsnowflakeThe staff member who wrote it
bodystringThe note
created_atISO8601 timestampWhen it was written
StatusBodyCondition
200response bodyThe note was added
404error responseUNKNOWN_ENFORCEMENT_DECISION when no decision has this ID

The rate limit is 100 requests per minute, on the admin:user:modify bucket.

POST/v1/admin/enforcement-decisions/{decision_id}/endAudit reason

Ends an active decision, or reverses a suspension that ended as expired, and emails the holder. You need enforcement:view and the ACL of the route that made the decision, plus ban:ip:remove when the decision has network blocks.

KindACL
suspensionuser:temp_ban
closureuser:delete
warning, feature_limituser:update:flags
profile_resetuser:update:profile
community_removalguild:delete

A content_removal needs message:shred, or message:delete_all when every message was removed.

KindWhat ending it does
suspensionClears temp_banned_until, enforcement_decision_id and DISABLED
closureCancels the deletion and clears DELETED, deletion_reason_code and enforcement_decision_id
feature_limitRemoves its flag unless another active decision has the same limit

Ending any other kind changes nothing on the account. Ending a closure also unblocks the email address if the closure blocked it, and ending a suspension or closure removes its network blocks.

FieldTypeDescription
outcome1stringlifted or reversed
statement?stringNote to the user, sent in the email (1-500 characters)

1 lifted ends a restriction that was correct and reversed says the decision was wrong. Only suspensions, closures and feature limits can be lifted, and a suspension that ended as expired or a suspension or closure no longer on the account can only be reversed

StatusBodyCondition
200enforcement decision objectThe decision ended
400error responseINVALID_FORM_BODY with ENFORCEMENT_OUTCOME_NOT_ALLOWED when the decision cannot be lifted
404error responseUNKNOWN_ENFORCEMENT_DECISION when no decision has this ID
409error responseENFORCEMENT_DECISION_ENDED when the decision already ended, or ended as expired and was given lifted
409error responseENFORCEMENT_DECISION_REPLACED when lifted is given for a suspension or closure that a later one replaced
409error responseENFORCEMENT_DELETION_STARTED when the deletion of a closed account has started

The holder is emailed that the restriction ended or was reversed, with the statement when one is given. Every kind except a person's suspension or closure also gets a system DM.

The rate limit is 100 requests per minute, on the admin:user:modify bucket.