Skip to content
Micup API

Admin feed moderation

Admin feed routes remove a feed post, a comment on one or a reply to a comment, or put a public post that reports took off Discover back on it. Those routes require feed:moderate; the feed category routes require feed:category:manage instead. Reports about feed content arrive through the Admin Reports API with report type FEED_POST (3) or FEED_COMMENT (4); their reported_message_id is the post, comment or reply ID, and a comment report’s first context entry is its post. A reply report’s second context entry is the comment it answers, whose ID the reply delete route takes.

DELETE/v1/admin/feed/posts/{post_id}Audit reason

Removes a post with its images or video, likes, comments and replies, and returns 204 with an empty body. Requires feed:moderate.

FieldTypeDescription
post_idsnowflakeThe ID of the post
StatusBodyCondition
204emptyThe post was removed and the audit entry was written
403error responseThe caller lacks feed:moderate
404error responseNo post has the ID and the request returns UNKNOWN_FEED_POST

Home feeds drop the post on their next read, and its notifications leave the author’s inbox. The author receives a system DM in their locale saying a moderator removed one of their posts, through the same delivery job as System DMs. No Dispatch is emitted.

One Admin audit entry is recorded with the action delete_feed_post and the target type feed_post, targeting the post ID. Its metadata is author_id, and evidence_keys when the post had images or a video: the comma-separated reports bucket keys of their copies.

100 requests per minute for each authenticated user, on the admin:feed:moderation bucket.

DELETE/v1/admin/feed/posts/{post_id}/discover-hideAudit reason

Undoes the hide that enough reports put on a public post, and returns 204 with an empty body. The post’s reports are cleared, it returns to the Discover ranking with the score its reactions earned, and new reports cannot take it off Discover again for 72 hours. Requires feed:moderate.

FieldTypeDescription
post_idsnowflakeThe ID of the post
StatusBodyCondition
204emptyThe hide was undone and the audit entry was written
403error responseThe caller lacks feed:moderate
404error responseNo post has the ID and the request returns UNKNOWN_FEED_POST

Discover shows the post again from its next list build, about a minute later; a post that is not public or is older than 72 hours stays off Discover. The author is not told, as they were not told of the hide. No Dispatch is emitted.

One Admin audit entry is recorded with the action restore_feed_post_discover and the target type feed_post, targeting the post ID. Its metadata is author_id.

100 requests per minute for each authenticated user, on the admin:feed:moderation bucket.

DELETE/v1/admin/feed/posts/{post_id}/comments/{comment_id}Audit reason

Removes a top-level comment on a post with its replies and returns 204 with an empty body. Requires feed:moderate.

FieldTypeDescription
post_idsnowflakeThe ID of the post
comment_idsnowflakeThe ID of the comment
StatusBodyCondition
204emptyThe comment was removed and the audit entry was written
403error responseThe caller lacks feed:moderate
404error responseThe post has no comment with the ID and the request returns UNKNOWN_FEED_COMMENT

The post’s comment count drops by one plus the number of replies. The comment’s author receives a system DM in their locale saying a moderator removed one of their comments; the authors of its replies are not told. No Dispatch is emitted.

One Admin audit entry is recorded with the action delete_feed_comment and the target type feed_comment, targeting the comment ID. Its metadata is post_id and author_id.

100 requests per minute for each authenticated user, on the admin:feed:moderation bucket.

DELETE/v1/admin/feed/posts/{post_id}/comments/{comment_id}/replies/{reply_id}Audit reason

Removes a reply to a comment on a post and returns 204 with an empty body. Requires feed:moderate.

FieldTypeDescription
post_idsnowflakeThe ID of the post
comment_idsnowflakeThe ID of the top-level comment the reply answers
reply_idsnowflakeThe ID of the reply
StatusBodyCondition
204emptyThe reply was removed and the audit entry was written
403error responseThe caller lacks feed:moderate
404error responseThe comment has no reply with the ID and the request returns UNKNOWN_FEED_COMMENT

The post’s comment count and the comment’s reply count drop by one. The reply’s author receives a system DM in their locale saying a moderator removed one of their comments. No Dispatch is emitted.

One Admin audit entry is recorded with the action delete_feed_comment_reply and the target type feed_comment, targeting the reply ID. Its metadata is post_id, parent_id (the comment the reply answers) and author_id.

100 requests per minute for each authenticated user, on the admin:feed:moderation bucket.

The feed categories and the phrases refused in posts filed under one live in the instance configuration under the feed_category_config key, at most 50 categories and 500 phrases, and no public instance route returns them. Every route in this section requires feed:category:manage, which feed:moderate does not grant, and records an Admin audit entry, reads included. Writes are saved with a compare-and-set, and every API process picks them up through the instance configuration refresh. A value outside its range returns 400 INVALID_FORM_BODY.

FieldTypeDescription
idsnowflakeThe ID of the category
namestringThe name (1-32 characters)
emojistringThe emoji shown before the name (1-16 characters)
positionintegerThe display order, lowest first (0-1000)
enabledbooleanWhether the category is listed and takes new posts
countriesarray[string]Upper case ISO 3166-1 alpha-2 codes the category is shown in, at most 50; empty shows it everywhere
created_atISO8601 timestampWhen the category was created
FieldTypeDescription
categoriesarray[feed category admin object]Every category, disabled ones included, by position
blocked_phrasesarray[string]The phrases refused in posts filed under a category
GET/v1/admin/feed/categoriesAudit reason

Returns the feed categories admin object, read from storage. Requires feed:category:manage.

StatusBodyCondition
200feed categories admin objectThe categories were returned
403error responseThe caller lacks feed:category:manage

One entry is recorded with action view_feed_categories, target type feed_category, target ID 0, and metadata keys result_count and phrase_count.

60 requests per minute for each authenticated user, on the admin:feed:categories bucket.

POST/v1/admin/feed/categoriesAudit reason

Adds a category and returns the feed category admin object. There is no delete; past 50 categories, reuse one that is turned off. Requires feed:category:manage.

FieldTypeDescription
namestringThe name (1-32 characters)
emojistringThe emoji (1-16 characters)
position?integerThe display order (0-1000, default 0)
enabled?booleanWhether the category is on (default true)
countries?array[string]Two-letter country codes, in any case; repeats are dropped (default empty)
StatusBodyCondition
200feed category admin objectThe category was created
400error responseA value is outside its range, or 50 categories exist
403error responseThe caller lacks feed:category:manage

One entry is recorded with action create_feed_category, target type feed_category, the ID of the category as target ID, and metadata keys name, emoji, countries, enabled and position.

60 requests per minute for each authenticated user, on the admin:feed:categories bucket.

PATCH/v1/admin/feed/categories/{category_id}Audit reason

Changes a category and returns the feed category admin object. Any field of the create body is optional, and at least one is required; an omitted field stays as it is. A category turned off is no longer listed and takes no new posts, and its page is empty; posts already filed keep its name. Requires feed:category:manage.

FieldTypeDescription
category_idsnowflakeThe ID of the category
StatusBodyCondition
200feed category admin objectThe category was saved
400error responseA value is outside its range, or the body is empty
403error responseThe caller lacks feed:category:manage
404error responseNo category has the ID and the request returns NOT_FOUND

One entry is recorded with action update_feed_category, target type feed_category, the ID of the category as target ID, and a <field>_old and <field>_new metadata key for every field it changed; countries are joined with commas.

60 requests per minute for each authenticated user, on the admin:feed:categories bucket.

PUT/v1/admin/feed/categories/phrasesAudit reason

Replaces the list of phrases refused in posts filed under a category and returns the feed categories admin object. The phrases are stored in lower case without repeats. Requires feed:category:manage.

FieldTypeDescription
blocked_phrasesarray[string]The whole list, at most 500 phrases of 3-64 characters each, each keeping at least 3 letters or digits once folded
StatusBodyCondition
200feed categories admin objectThe list was saved
400error responseA phrase is outside its length or keeps fewer than 3 letters or digits, or the list is too long
403error responseThe caller lacks feed:category:manage

One entry is recorded with action update_feed_category_phrases, target type feed_category, target ID 0, and metadata keys phrase_count_old and phrase_count_new; the phrases themselves are not recorded.

60 requests per minute for each authenticated user, on the admin:feed:categories bucket.

The feed topics live in feed_topics; the topic limits and the suggestion job’s switches live in the instance configuration under the feed_topic_config key. Every route in this section requires feed:category:manage, the same admins as the categories, and records an Admin audit entry, reads included. A topic lives at most 72 hours from when it was published. At most max_live topics (8 by default, 1-20), fixture topics apart, can be live or waiting to start at once, and at most max_per_day (20 by default, 1-20) can be published a UTC day, removed ones included. Fixture topics have their own cap, max_live_fixtures (4 by default, 1-20): the suggestion job publishes no fixture past it and queues it instead, while an admin may publish a queued fixture past it. The suggestion job reads the Gemini key from the integrations.gemini.api_key configuration (FLUXER_GEMINI_API_KEY) and the fixture key from integrations.api_football.api_key (FLUXER_API_FOOTBALL_KEY), each also from a _FILE path; the older GEMINI_API_KEY and APIFOOTBALL_KEY names still apply. Neither key is ever in this configuration. Only public headlines and patch note titles reach the LLM; rising feed words come to the queue as they are. A value outside its range returns 400 INVALID_FORM_BODY.

FieldTypeDescription
idsnowflakeThe ID of the topic
titlestringThe title (1-60 characters)
description?stringOne sentence (1-140 characters)
image?stringThe hash of the image, served at branding/feed-topic-{id}/{image}.webp
category_idsnowflakeThe category the topic and its posts are filed under
starts_atISO8601 timestampWhen it starts taking posts
ends_atISO8601 timestampWhen it stops taking posts
statusstringscheduled, live, ended or removed
sourcestringfixture, news, patch, feed_trend or manual
source_ref?stringThe item it came from, a fixture ID or a link
created_by?snowflakeThe admin who published it; null when the system did
created_atISO8601 timestampWhen it was published
post_countintegerHow many posts its page lists
FieldTypeDescription
max_liveintegerTopics live or waiting to start at once, fixture topics apart (1-20)
max_live_fixturesintegerFixture topics live or waiting to start at once; binds the suggestion job, not an admin (1-20)
max_per_dayintegerTopics published a UTC day (1-20)
default_hoursintegerThe life of a topic given no end (1-72)
auto_publish_fixturesbooleanWhether fixture topics of the chosen leagues publish on their own
fixture_category_id?snowflakeThe category fixture topics go under
fixture_leaguesarray[integer]API-Football league IDs whose matches become topics, at most 20
llm_enabledbooleanWhether the job asks the LLM to turn headlines into proposals
llm_modelstringThe Gemini model the job asks (gemini-3.8-flash by default)
sourcesarray[object]{id, enabled} for each source the job knows; the addresses are fixed in code
FieldTypeDescription
topicsarray[feed topic admin object]The topics published in the last four days, every status, newest first
configfeed topic config objectThe topic config, every source the job knows listed
FieldTypeDescription
idsnowflakeThe ID of the proposal
titlestringThe proposed title
description?stringThe proposed sentence
category_id?snowflakeThe proposed category; null when none fit
sourcestringfixture, news, patch or feed_trend
source_refsarray[string]The items behind it, at most 10
reason?stringThe job’s short reason
sensitive1booleanWhether the proposal is held for an admin; never published on its own
statusstringpending, published or rejected
starts_at?ISO8601 timestampThe start a fixture proposal brings
ends_at?ISO8601 timestampThe end a fixture proposal brings
topic_id?snowflakeThe topic published from it
created_atISO8601 timestampWhen it was proposed

1 Flagged by the LLM, holding a category phrase in its own text or in a headline it rests on, or resting on no headline

FieldTypeDescription
proposalsarray[feed topic proposal object]The proposals of the day, newest first, at most 100
GET/v1/admin/feed/topicsAudit reason

Returns the feed topics admin object, read from storage. Requires feed:category:manage.

StatusBodyCondition
200feed topics admin objectThe topics were returned
403error responseThe caller lacks feed:category:manage

One entry is recorded with action view_feed_topics, target type feed_topic, target ID 0, and metadata key result_count.

60 requests per minute for each authenticated user, on the admin:feed:topics bucket.

POST/v1/admin/feed/topicsAudit reason

Publishes a topic and returns the feed topic admin object. A topic from a pending proposal (proposal_id) takes the proposal’s source and times unless the body sets them, and the proposal is marked published. Requires feed:category:manage.

FieldTypeDescription
titlestringThe title (1-60 characters)
description??stringOne sentence (1-140 characters)
category_idsnowflakeAn enabled category
image?stringA base64 PNG, JPEG or WebP image, still, at most 512 KB, 96-1,920 pixels wide and 96-1,080 high
starts_at?ISO8601 timestampThe start (default now)
ends_at?ISO8601 timestampThe end, after the start and now, at most 72 hours after publishing (default the start plus default_hours)
proposal_id?snowflakeA pending proposal this publishes
StatusBodyCondition
200feed topic admin objectThe topic was published
400error responseA value is outside its range, the category is off, the proposal is not pending, or max_live or max_per_day is reached
403error responseThe caller lacks feed:category:manage
404error responseNo proposal has the ID and the request returns NOT_FOUND

One entry is recorded with action create_feed_topic, target type feed_topic, the ID of the topic as target ID, and metadata keys title, category_id, source, starts_at and ends_at, and proposal_id when it was published from a proposal.

60 requests per minute for each authenticated user, on the admin:feed:topics bucket.

PATCH/v1/admin/feed/topics/{topic_id}Audit reason

Changes a topic and returns the feed topic admin object. At least one field is required; an omitted field stays as it is. status ended stops the topic now; removed takes it down for good, hides it from members and drops its name from posts. A removed topic cannot be changed. Requires feed:category:manage.

FieldTypeDescription
topic_idsnowflakeThe ID of the topic
FieldTypeDescription
title?stringThe title (1-60 characters)
description??stringOne sentence (1-140 characters), or null to drop it
image??stringA new image as in the publish body, or null to drop it
ends_at?ISO8601 timestampA new end, after now and the start, at most 72 hours after publishing
status?stringended or removed
StatusBodyCondition
200feed topic admin objectThe topic was saved
400error responseA value is outside its range, the body is empty, or the topic is removed
403error responseThe caller lacks feed:category:manage
404error responseNo topic has the ID and the request returns NOT_FOUND

One entry is recorded with action update_feed_topic, target type feed_topic, the ID of the topic as target ID, and <field>_old and <field>_new metadata for each of title, description, image, starts_at, ends_at and status that changed.

60 requests per minute for each authenticated user, on the admin:feed:topics bucket.

GET/v1/admin/feed/topics/proposalsAudit reason

Returns the feed topic proposals admin object for a UTC day. Proposals are kept for a week. Requires feed:category:manage.

FieldTypeDescription
date?stringThe UTC day as YYYY-MM-DD (default today)
StatusBodyCondition
200feed topic proposals admin objectThe proposals were returned, possibly none
403error responseThe caller lacks feed:category:manage

One entry is recorded with action view_feed_topic_proposals, target type feed_topic, target ID 0, and metadata keys result_count and, when given, date.

60 requests per minute for each authenticated user, on the admin:feed:topics bucket.

POST/v1/admin/feed/topics/proposals/{proposal_id}/rejectAudit reason

Marks a pending proposal rejected and returns the feed topic proposal object. The job does not propose the same title again for three days. Requires feed:category:manage.

FieldTypeDescription
proposal_idsnowflakeThe ID of the proposal
StatusBodyCondition
200feed topic proposal objectThe proposal was rejected
400error responseThe proposal was already published or rejected
403error responseThe caller lacks feed:category:manage
404error responseNo proposal has the ID and the request returns NOT_FOUND

One entry is recorded with action reject_feed_topic_proposal, target type feed_topic_proposal, the ID of the proposal as target ID, and metadata keys title and source.

60 requests per minute for each authenticated user, on the admin:feed:topics bucket.

PUT/v1/admin/feed/topics/configAudit reason

Replaces the feed topic config and returns it. Every field is required. Requires feed:category:manage.

StatusBodyCondition
200feed topic config objectThe config was saved
400error responseA value is outside its range
403error responseThe caller lacks feed:category:manage

One entry is recorded with action update_feed_topic_config, target type feed_topic, target ID 0, and metadata keys max_live, max_live_fixtures, max_per_day, default_hours, auto_publish_fixtures, fixture_category_id (when set), llm_enabled, llm_model, source_count and league_count, and sources_on and sources_off (the source IDs the update turned on and off, comma separated) when any changed.

60 requests per minute for each authenticated user, on the admin:feed:topics bucket.