Cherry API: chat rooms, bots and moderation
How the Cherry API works: project and bot keys, room and member endpoints, Telegram-style bot updates, in-room moderation and per-app rate limits.
The Cherry API is a REST API your backend calls to create chat rooms, invite wallets, post messages, moderate members and run bots inside Cherry. Cherry is a wallet-to-wallet messenger for crypto: you sign in with a wallet, DM any address, and join or create token-gated, NFT-gated and paid group chats, with no phone number, email or KYC. For a developer that means a room per match, order or community, addressed by wallet, with no accounts or presence to build. The API, its keys and its bots are free, and everything below is current as of 11 September 2026.
What the Cherry API does
Two surfaces, two key formats, two base URLs. A project key manages the rooms your project owns; a bot key acts as a bot identity.
| Surface | Base URL | Key format | What it does |
|---|---|---|---|
| Apps API | https://api.cherry.fun/api/v1/apps | cherry_sk_<projectId>_<secret> | Create, update and delete rooms; invite wallets; kick, ban, mute, set roles; send, read and delete messages as the project |
| Bot API | https://api.cherry.fun/api/v1/bots | cherry_bot_<botId>_<secret> | DM a wallet; post into a group; inline buttons and callbacks; edit sent messages; moderate where the bot holds a role; long-poll updates or register a webhook |
Both are server-to-server. A key is a single opaque token you copy from the portal, shown once, and it belongs on your server, not in a browser bundle.
How it works
The permission model has three parts: which key you use, which scopes it holds, and for bots, what role it has in the room.
Two keys and the /me check
GET /me on either surface returns that key’s context: the fastest way to confirm credentials and read back granted scopes.
curl https://api.cherry.fun/api/v1/apps/me \
-H "Authorization: Bearer cherry_sk_<projectId>_<secret>"
curl https://api.cherry.fun/api/v1/bots/me \
-H "Authorization: Bearer cherry_bot_<botId>_<secret>"
A project key’s response names its scopes, its rate limits ({ "perMinute": 600, "perDay": 50000 }), the keyId that authenticated the call, and the bots your project owns with their ids and wallets. Every project also has an app identity: a wallet minted on your first send, or when you create your first API key, whichever happens first, whose display label you set in the portal under Developer profile → App identity. That identity posts into rooms without joining them, so a backend that runs a room per clan or per listing does not add a bot member to each one. Both key types rotate in place: rotation issues a fresh secret for the same key, keeps its id, name and scopes, and invalidates the old secret immediately.
Scopes per endpoint
Each endpoint requires one scope, and a call without it returns 403 INSUFFICIENT_SCOPE. Scopes are keyed to their surface: granting an Apps API scope to a bot key does nothing.
| Key | Scopes | Example endpoints |
|---|---|---|
Project (cherry_sk_) | groups:create, groups:manage, members:invite, members:moderate, messages:send, messages:read, messages:delete | POST /apps/groups, POST /apps/groups/:roomId/members/:wallet/ban |
Bot (cherry_bot_) | bots:dm:send, bots:dm:read, bots:groups:send, bots:groups:moderate, bots:interactive, bots:callback:answer, bots:updates:poll, bots:webhook:manage, messages:edit | POST /bots/sendDirectMessage, GET /bots/getUpdates |
You grant these in the portal, project keys under the project’s API keys section and bot keys under the bot’s Keys section, or by PATCHing the key with the set it should hold. The signing scopes bots:sign:request and bots:tx:request need admin review first.
Rooms, members and messages
The core server flow is three calls: create a room owned by a wallet you name, seed it with participants, post into it.
const OWNER = {
Authorization: `Bearer ${process.env.CHERRY_PROJECT_KEY}`,
'Content-Type': 'application/json',
};
const { roomId } = await (
await fetch('https://api.cherry.fun/api/v1/apps/groups', {
method: 'POST',
headers: OWNER,
body: JSON.stringify({
ownerWallet: hostWallet,
title: `Match #${matchId}`,
initialMembers: [playerA, playerB],
}),
})
).json();
await fetch(`https://api.cherry.fun/api/v1/apps/groups/${roomId}/messages`, {
method: 'POST',
headers: OWNER,
body: JSON.stringify({ content: 'Match starting. Good luck.' }),
});
initialMembers invites and accepts in one step; a later POST /apps/groups/:roomId/members with autoAccept: true does the same for latecomers, and its response splits them into invited, skipped and accepted. Reading history is cursor-paginated, newest first, 50 messages a page by default and 100 at most. Moderation is one route per verb: a ban with deleteMessages: true purges that member’s messages and reports a deletedCount, while a mute leaves their old messages standing until you unmute. Rooms your project did not create are off limits: 403 ROOM_NOT_MANAGED_BY_APP.
The Bot API
A bot is a separate identity with its own wallet, name, handle and avatar. Its endpoints take a JSON body with no path parameters, and the Telegram-shaped vocabulary is deliberate: sendInteractiveMessage attaches an inline keyboard, answerCallbackQuery responds when someone presses a button, and editMessageText and editMessageReplyMarkup change what the bot already sent.
Bot moderation is gated twice. The bots:groups:moderate scope authorizes the call, and the bot’s in-room role, the same owner/admin/moderator roles a human moderator holds, authorizes the action on another member. So a moderation bot is set up in two halves: a project key creates the room, invites the bot’s wallet with autoAccept: true and promotes it with POST /apps/groups/:roomId/members/:wallet/role, then the bot key does the work.
await fetch('https://api.cherry.fun/api/v1/bots/banGroupMember', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHERRY_BOT_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ roomId, wallet, deleteMessages: true }),
});
Four invariants fall out of that second gate. A bot cannot moderate the room owner, and a moderator bot cannot act on admins. A bot cannot set roles, so none can escalate itself. A muted bot’s moderation calls are rejected, which makes muting a kill switch. And a bot acts as its own wallet: it never inherits the owner’s authority.
Updates: long-poll or webhook
A bot hears about new messages in one of two ways. GET /api/v1/bots/getUpdates long-polls for them under the bots:updates:poll scope. POST /api/v1/bots/setWebhook, under bots:webhook:manage, registers a webhook so they reach your server as they arrive, and POST /api/v1/bots/deleteWebhook removes it.
A project key reads room history: GET /api/v1/apps/groups/:roomId/messages with messages:read, newest first. A project-key poller should pick an interval that stays well inside the rate limits below, because every poll draws on the same budget as your writes.
Rate limits and errors
The budget is per app, not per key and not per IP: every request with the same app credentials draws on one pool. The defaults are 600 requests per minute and 50,000 per day as of 10 September 2026, both windows rolling, reads costing the same as writes. Exceeding one returns 429 with a Retry-After header in seconds and a body naming the window: { "error": "RATE_LIMITED", "reason": "PER_MINUTE_LIMIT_EXCEEDED", "retryAfterSec": 60 }. The other codes worth handling are 401, 403 INSUFFICIENT_SCOPE, 403 ROOM_NOT_MANAGED_BY_APP, 403 CANNOT_MODERATE_OWNER and 403 CANNOT_DELETE_ASSIGNED_ROOM. Every error body carries an error code and a readable message. For higher limits, ask the Cherry team in its public Telegram group
.
What is different from a general chat API
General chat APIs start from a user table: you create a user, store a token, and map it to your own account. The Cherry API starts from a wallet address, because a wallet is already the account: you pass ownerWallet and a list of member wallets, and Cherry resolves display names from SNS (.sol, .sns), .skr on Solana Seeker and other AllDomains names, with the avatar from the domain record.
Three consequences follow. There is no user provisioning step and no identity store to keep in sync. Membership can be checked against the chain instead of your database, which is what token-gated chat
does with a gatingRule on the room. And the room needs no second system to reach your users: an embed on your page shows it to any viewer who is an active member.
The Bot API is Telegram-shaped by design, so a bot you have written before ports over, but the identity it acts as is a wallet.
Who uses it and for what
A game backend runs a room per clan and a room per match. The clan room is long-lived and app-owned; the match room is created when the match starts with both rosters as initialMembers and deleted when the match ends. The chat for web3 games guide
walks that pattern end to end.
A marketplace opens a room per order between buyer and seller; a support desk opens one per support thread. State changes post into each as the order or the thread moves. The thread becomes the dispute record, and because the app identity posts without joining, the room stays a two-party conversation.
A community team runs moderation with a bot: it long-polls getUpdates, deletes what breaks the rules, mutes repeat offenders and escalates a ban to a human. Cherry’s server-side anti-spam layers
handle wallet-age gating and blocked words underneath, so the bot only encodes your own rules.
The fourth pattern is an AI agent in chat: a bot key, inline buttons, and the same wallet identity every member has.
Limits and honest caveats
Group chats on Cherry are not end-to-end encrypted, by design, because moderation and bots need to read them. End-to-end encryption covers DMs between people, with keys derived from the wallet.
Your project can only manage the rooms it created, and deleting a room you did not create returns 403 CANNOT_DELETE_ASSIGNED_ROOM. The three owner capabilities, allowOwnerDetach, allowOwnerDelete and allowOwnerTransfer, default to false, so set them at creation if the owner wallet should be able to detach, delete or transfer the room.
Pairing a room with the embeddable chat widget
works in single mode only: the list and external-controlled modes, and the setRoom() call with them, are documented ahead of runtime support and behave like single today.
Every wallet field in the API docs and examples is a Solana address. Legacy cha_ keys still resolve during a migration grace window, but issue new keys in the cherry_sk_ and cherry_bot_ formats.
How to start
- Sign in at portal.cherry.fun with a Solana wallet and create a project, as the developer portal guide sets out.
- In the project’s API keys section, create a project key with only the scopes you need, and copy the token: it is shown once.
- Call
GET /api/v1/apps/mewith it and check thescopesandrateLimitsthat come back. - Create your first room with
POST /api/v1/apps/groups, passingownerWalletandtitle, and store the returnedroomIdnext to your own record. - Post into it with
POST /api/v1/apps/groups/:roomId/messages, then surface it to users with the chat widget in your dApp or inside a Cherry mini app . - Add a bot under Bots when you need a named identity, give its key the scopes it needs, promote its wallet in each room it moderates, and have it long-poll
getUpdatesor register a webhook withsetWebhook.
Start at the Cherry API endpoint reference or hand your agent the OpenAPI 3.1 spec and let it generate the client.
FAQ
What is the Cherry API?
The Cherry API is a REST API your backend calls to manage Cherry chat programmatically. It has two surfaces: the Apps API at https://api.cherry.fun/api/v1/apps, with a project key, which manages rooms, members and messages your project owns; and the Bot API at https://api.cherry.fun/api/v1/bots, with a bot key, which acts as a named bot with its own wallet.
How do I send a message as a bot in a group chat?
Two ways. With a project key, POST to /api/v1/apps/groups/:roomId/messages and pass a botId in the body to speak as one of your project’s bots; leave botId out and the message comes from your project’s app identity. With a bot key, POST to /api/v1/bots/sendGroupMessage with the bots:groups:send scope. The sender is resolved on Cherry’s servers, so no caller can impersonate a user.
Does the Cherry chat API support webhooks?
Yes, for bots. A bot key with the bots:webhook:manage scope registers a webhook with POST /api/v1/bots/setWebhook, removes it with POST /api/v1/bots/deleteWebhook, and receives new messages as they arrive. Without one, the bot long-polls GET /api/v1/bots/getUpdates under bots:updates:poll, and a project key can poll GET /api/v1/apps/groups/:roomId/messages with messages:read.
Is the Cherry API free?
Yes. The Cherry API, bots, the Chat Embed SDK and Cherry Portal are free. Cherry’s only charges are a share of paid-community payments, the paid-DM fee and a referral share on in-chat swaps, and none of them apply to the developer tools.
What are the Cherry API rate limits?
600 requests per minute and 50,000 per day per app by default, as of 10 September 2026. The budget is shared by every key the app holds, the windows are rolling rather than calendar-based, and reads count the same as writes. Over the limit you get 429 with a Retry-After header and a body naming the window you hit.
Is there an OpenAPI spec for the Cherry chat API?
Yes, an OpenAPI 3.1 document at https://portal.cherry.fun/openapi.json. It covers every endpoint on both surfaces, both bearer schemes, and the scope each operation requires, so you can import it into Postman or generate a client.
Can a bot moderate a room my project does not own?
Yes, if the room’s owner or an admin invites the bot’s wallet and promotes it to moderator or admin. Bot moderation passes two independent checks: the bots:groups:moderate scope authorizes the call, and the bot’s in-room role authorizes the action on other members. A bot with the scope but no role is denied with 403 INSUFFICIENT_ROOM_ROLE.
Do my users need a Cherry account to use a room the API created?
No. Their Solana wallet is the account: initialMembers invites the wallets you pass and accepts them on creation, and an embed on your page shows them the room once they are active members.
Sources
- Cherry API authentication: key formats, /me and rotation
- Cherry API scopes
- Create rooms and attach them to embeds
- Messages and bot actions
- Members and moderation
- Rate limits and errors
- Cherry API endpoint reference
- Cherry API OpenAPI 3.1 spec
- Guide: build a moderation bot
- Guide: a room per game, match or order
- Cherry developer support on Telegram
Build on Cherry
Add chat to your dApp, ship a mini app, or run bots and rooms through the API. Self-serve at the developer portal.