18 endpoints.
| Method | Path | Summary |
|---|---|---|
GET | /api/communities/{communityId}/member-posts | The moderation list (قائمة الإشراف) |
DELETE | /api/communities/{communityId}/member-posts/{tweetId} | Delete a post, with a reason (حذف منشور) |
POST | /api/communities/{communityId}/member-posts/{tweetId}/feature | Feature a post, with a reason (تمييز منشور) |
POST | /api/communities/{communityId}/member-posts/{tweetId}/hide | Hide a post, with a reason (إخفاء منشور) |
POST | /api/communities/{communityId}/member-posts/{tweetId}/restore | Restore a deleted post (استرجاع منشور محذوف) |
POST | /api/communities/{communityId}/member-posts/{tweetId}/unfeature | Remove the featured badge (إلغاء التمييز) |
POST | /api/communities/{communityId}/member-posts/{tweetId}/unhide | Show a hidden post again (إظهار منشور مخفي) |
GET | /api/communities/{communityId}/member-posts/allowances | Members and the quota each is under (استثناءات الأعضاء) |
DELETE | /api/communities/{communityId}/member-posts/allowances/{memberId} | Remove an allowance so the member inherits again (حذف الاستثناء) |
PUT | /api/communities/{communityId}/member-posts/allowances/{memberId} | Set one member's allowance (تعيين استثناء لعضو) |
GET | /api/communities/{communityId}/member-posts/mine | My own posts, in every state (منشوراتي بكل حالاتها) |
GET | /api/communities/{communityId}/member-posts/notifications/me | Do I get notified about other members' posts? (إشعارات منشورات الأعضاء) |
PUT | /api/communities/{communityId}/member-posts/notifications/me | Mute or unmute member-post notifications (كتم إشعارات منشورات الأعضاء) |
GET | /api/communities/{communityId}/member-posts/publishers | The «أعضاء محدّدون» picker roster (من يستطيع النشر) |
PUT | /api/communities/{communityId}/member-posts/publishers | Replace the publisher list (حفظ قائمة من يستطيع النشر) |
GET | /api/communities/{communityId}/member-posts/quota/me | My posting allowance today (حصتي اليومية) |
GET | /api/communities/{communityId}/member-posts/settings | Read the member-posting settings (قراءة إعدادات نشر الأعضاء) |
PUT | /api/communities/{communityId}/member-posts/settings | Update the member-posting settings (تعديل إعدادات نشر الأعضاء) |
GET /api/communities/{communityId}/member-postsEvery home-feed post in one of four tabs, with the counts for all four. Not only posts by members: a moderation tool that hides the moderators'' own posts has a blind spot exactly where accountability matters. The tab counts ignore the search box, because a badge answers "how many are in that tab".
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
status | query | no | enum: published, hidden, featured, deleted | — |
q | query | no | string | Search the post body and the author's community name |
authorId | query | no | string | A CommunityMember id |
Responses
| Status | Description | Body |
|---|---|---|
200 | A page of posts plus the four tab counts | — |
DELETE /api/communities/{communityId}/member-posts/{tweetId}A SOFT delete: the row stays and appears in the المحذوفة tab, where it can be restored. The author keeps seeing it on their own profile with the reason. The post''s slot in today''s quota is NOT returned.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
tweetId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The post's new state | — |
400 | MODERATION_REASON_REQUIRED | — |
409 | POST_ALREADY_DELETED | — |
POST /api/communities/{communityId}/member-posts/{tweetId}/featureOnly a visible post can be featured. The post KEEPS its place in the feed and gains a badge — it is never moved into a separate section fed by a separate request, which is how a post once disappeared from the platform entirely for three months when that request failed.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
tweetId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The post's new state | — |
409 | POST_NOT_VISIBLE, POST_ALREADY_FEATURED | — |
POST /api/communities/{communityId}/member-posts/{tweetId}/hideThe reason is required, at least 10 characters, recorded in the moderation log and shown to the post''s author. Hiding also clears any featured badge: "endorsed but invisible" is not a state the product has a meaning for. Refuses with 409 when the post is already hidden or deleted — the request is well formed, the post is simply not in a state where the action means anything.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
tweetId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The post's new state | — |
400 | MODERATION_REASON_REQUIRED, MODERATION_REASON_TOO_SHORT | — |
409 | POST_ALREADY_HIDDEN, POST_ALREADY_DELETED | — |
POST /api/communities/{communityId}/member-posts/{tweetId}/restoreRestores to PUBLISHED, not to whatever it was before deletion — the previous state is not stored, and guessing would surprise a moderator who expects restore to mean visible again.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
tweetId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The post's new state | — |
409 | POST_NOT_DELETED | — |
POST /api/communities/{communityId}/member-posts/{tweetId}/unfeatureAuth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
tweetId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The post's new state | — |
409 | POST_NOT_FEATURED | — |
POST /api/communities/{communityId}/member-posts/{tweetId}/unhideNo reason required. The author is notified either way. Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
tweetId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The post's new state | — |
409 | POST_NOT_HIDDEN | — |
GET /api/communities/{communityId}/member-posts/allowancesLists MEMBERS only — admins and moderators are exempt by role, so a permanently uneditable row for them would invite an admin to try to change it. Each row carries the effective quota, where it came from, and any active posting restriction (read-only here; restrictions are applied from member management).
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
q | query | no | string | Search the member's community name |
Responses
| Status | Description | Body |
|---|---|---|
200 | A page of members with their effective quotas | — |
DELETE /api/communities/{communityId}/member-posts/allowances/{memberId}Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
memberId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | Removed. Idempotent when there was no override. | — |
PUT /api/communities/{communityId}/member-posts/allowances/{memberId}mode is UNLIMITED or CUSTOM. There is no INHERIT mode — to make a member inherit the community default again, DELETE the allowance.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
memberId | path | yes | string | A CommunityMember id, not a User id |
Responses
| Status | Description | Body |
|---|---|---|
200 | The saved allowance | — |
400 | CUSTOM_WITHOUT_QUOTA, UNLIMITED_WITH_QUOTA, MEMBER_EXEMPT_FROM_QUOTA | — |
GET /api/communities/{communityId}/member-posts/mineThe caller''s own posts for their profile tabs, INCLUDING hidden and deleted ones with the moderator''s reason — the transparency this feature exists for. It takes no member id and never will: the membership is resolved from the authenticated user alone, because an id parameter here would let anyone read anyone else''s removed posts and why they were removed. The PUBLIC profile read stays GET /api/tweets/user/userId, filtered to published for everyone.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
status | query | no | enum: all, published, hidden, featured, deleted | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | A page of the caller's own posts. Empty for a non-member, never a 403. | — |
GET /api/communities/{communityId}/member-posts/notifications/me{ muted: boolean }. A row in member_post_notification_mutes means muted; no row means notified, which is the default. Answers muted: false for a caller with no approved membership rather than refusing — a settings screen that shows an error where a switch should be is a broken screen. This is the MEMBER''s own switch and is independent of the community-wide one (memberPosts.notifyOnMemberPost, off by default): the admin decides whether the notification exists at all, the member decides whether to receive it.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The caller's preference | — |
PUT /api/communities/{communityId}/member-posts/notifications/meBody is { muted: boolean }. Idempotent in both directions — muting twice and unmuting something never muted both succeed, because a toggle that 409s over a state nobody is defending just looks broken. Takes NO member id: the membership comes from the authenticated user, or the route would let anyone change anyone else''s preferences.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The saved preference | — |
400 | INVALID_MUTED | — |
403 | NOT_A_COMMUNITY_MEMBER | — |
GET /api/communities/{communityId}/member-posts/publishersMembers of this community, each flagged with isPublisher. Returns CANDIDATES rather than only the chosen, because a picker that shows only the already- selected cannot be used to select anyone new. MEMBERS only: admins and moderators post through posts:manage / posts:create whatever this list says, so a checkbox beside their name would change nothing. Each row also carries hasPostPermission — surfaced, not hidden, because an individually granted permission is a second open door and an admin who cannot see it will report "I removed them and they can still post" as a bug here.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
q | query | no | string | Search the member's community name |
selectedOnly | query | no | boolean | Only members already in the list |
Responses
| Status | Description | Body |
|---|---|---|
200 | A page of candidates, plus selectedCount over the whole community | — |
PUT /api/communities/{communityId}/member-posts/publishersBody is { memberIds: string[] } of CommunityMember ids, and it REPLACES the list rather than patching it — the screen expresses a selection, not a stream of deltas. An empty array is legal and means nobody is individually selected; it is how an admin removes the last person. Ids that are not approved MEMBERS of this community are refused as a whole with INVALID_PUBLISHER_MEMBERS and nothing is saved, rather than being dropped silently.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The saved list size | — |
400 | INVALID_PUBLISHER_PAYLOAD, INVALID_PUBLISHER_ID, TOO_MANY_PUBLISHERS, INVALID_PUBLISHER_MEMBERS | — |
GET /api/communities/{communityId}/member-posts/quota/meWhat the composer renders for the current member: whether they may post, how many posts remain in the current Riyadh day, why not when they may not, and whether they may mark a post «مرئي للزوار» (canSetGuestVisible, true for admins and moderators whatever the setting says). Answers for every case rather than refusing — a guest or a non-member gets allowed=false, never a 403.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The member's eligibility and remaining quota | — |
GET /api/communities/{communityId}/member-posts/settingsAuth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | Settings, repaired on read so a malformed blob never 500s | — |
PUT /api/communities/{communityId}/member-posts/settingsAccepts a partial patch of enabled, audience, defaultDailyQuota, allowGuestVisible and notifyOnMemberPost. A quota of 0 is refused with its own code: zero posts per day is a ban, and a real ban records a reason and a duration. Setting allowGuestVisible to false ALSO clears isGuestVisible on every member-authored home post that carried it, in the same transaction — posts by admins and moderators are untouched. Switching it back on does not restore them: each member chooses again.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | path | yes | string | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | The saved settings | — |
400 | INVALID_ENABLED, INVALID_AUDIENCE, ZERO_DAILY_QUOTA, INVALID_ALLOW_GUEST_VISIBLE, INVALID_NOTIFY_ON_MEMBER_POST, … | — |