# Members & moderation Manage who's in a room and keep it healthy. Cherry has **two moderation models** - pick the one that matches your key: - **Project key** (`cherry_sk_`) moderates rooms **your project owns**, acting as the room owner - `/api/v1/apps/...`, scope `members:moderate`. - **Bot key** (`cherry_bot_`) moderates rooms where **the bot itself holds a role** (owner / admin / moderator) - `/api/v1/bots/...`, scope `bots:groups:moderate`. ## Invite (project key) `POST /api/v1/apps/groups/:roomId/members` - bulk-invite wallets, requires `members:invite`. With `autoAccept: true` they become active immediately; otherwise they get a pending invite. ```json { "wallets": ["walletA...", "walletB..."], "autoAccept": false } ``` ```json { "invited": ["walletA..."], "skipped": ["walletB..."], "accepted": [] } ``` `skipped` covers wallets already in the room or already invited. ## Project-key moderation Rooms your project created are moderated with your **project key**. The call is made **as the room owner**, so it always has authority. Every route here needs `members:moderate`. | Action | Endpoint | Body | |---|---|---| | Kick | `DELETE /api/v1/apps/groups/:roomId/members/:wallet` | | | Ban | `POST /api/v1/apps/groups/:roomId/members/:wallet/ban` | `{ "deleteMessages"?: boolean }` | | Unban | `POST /api/v1/apps/groups/:roomId/members/:wallet/unban` | | | Mute | `POST /api/v1/apps/groups/:roomId/members/:wallet/mute` | | | Unmute | `POST /api/v1/apps/groups/:roomId/members/:wallet/unmute` | | | Set role | `POST /api/v1/apps/groups/:roomId/members/:wallet/role` | `{ "role": "admin" \| "moderator" \| "member" }` | Notes: - **Ban** with `deleteMessages: true` also removes that member's messages; the response includes `deletedCount`. - **Mute** is a shadow-ban: the member can't post, but their existing messages stay. Mutes are indefinite: call **Unmute** to lift one. - **Roles**: `owner` cannot be assigned via the API. **Set role** is a project-key power: bots cannot change roles. - You can only act on [rooms](https://portal.cherry.fun/docs/api/rooms.md) your project manages → otherwise `403 ROOM_NOT_MANAGED_BY_APP`. ## Bot-key moderation A **bot key** moderates from inside the room, using the bot's own in-room role, not project ownership. This lets a bot moderate any room it's been promoted in, even rooms your project doesn't own. Every route needs `bots:groups:moderate`, takes a JSON body, and posts as the bot's wallet. | Action | Endpoint | Body | |---|---|---| | Ban | `POST /api/v1/bots/banGroupMember` | `{ "roomId", "wallet", "deleteMessages"?: boolean }` | | Unban | `POST /api/v1/bots/unbanGroupMember` | `{ "roomId", "wallet" }` | | Kick | `POST /api/v1/bots/kickGroupMember` | `{ "roomId", "wallet" }` | | Mute | `POST /api/v1/bots/muteGroupMember` | `{ "roomId", "wallet" }` | | Unmute | `POST /api/v1/bots/unmuteGroupMember` | `{ "roomId", "wallet" }` | | Delete message | `POST /api/v1/bots/deleteGroupMessage` | `{ "roomId", "messageId" }` | ```http POST /api/v1/bots/banGroupMember Authorization: Bearer cherry_bot__ Content-Type: application/json ``` ```json { "roomId": "room-uuid", "wallet": "7jx8aB...", "deleteMessages": true } ``` A bot gets its authority the ordinary way: a project key (or a human owner) invites the bot's wallet into the room and promotes it - see the [moderation bot guide](https://portal.cherry.fun/docs/guides/moderation-bot.md). ## The layered permission model Two independent checks gate every bot moderation call: 1. **The scope authorizes the API call.** Without `bots:groups:moderate`, the request is rejected before anything else (`403 INSUFFICIENT_SCOPE`). 2. **The bot's in-room role authorizes the action on others.** The bot must be an active, privileged member (owner / admin / moderator) of the target room, the *same* role model human moderators pass through. > **Warning:** A scope is not permission to act. A bot with `bots:groups:moderate` but only a plain-member role, or one that isn't in the room at all, is **denied** (`403 INSUFFICIENT_ROOM_ROLE`). Because in-room role is the second gate, these invariants always hold for bots: - A bot **cannot moderate the room owner**, and a moderator bot cannot act on admins. - A **muted** bot is denied: muting a misbehaving bot is a working kill-switch. - Bots **cannot set member roles**: no bot can mint admins or escalate itself. - The bot always acts as **its own wallet**; it never inherits the owner's authority. ## Next steps - [Messages & bot actions](https://portal.cherry.fun/docs/api/messages.md) · [Scopes](https://portal.cherry.fun/docs/api/scopes.md) · [Rate limits & errors](https://portal.cherry.fun/docs/api/rate-limits.md) · [Endpoint reference](https://portal.cherry.fun/docs/api/reference.md) - Build one: [Guides › Moderation bot](https://portal.cherry.fun/docs/guides/moderation-bot.md).