---
title: "ServerService API reference"
description: "Every ServerService call of the Sblurp community server: what it does, who may call it, and its request and response fields."
nav: "ServerService"
doc_version: 1
last_updated: 2026-10-18
---

<!-- Generated by auth/cmd/apidocs from proto/sblurp/v1/sblurp.proto. Do not edit; run go generate in auth/cmd/auth. -->

# ServerService

Each call is `POST {base}/sblurp.v1.ServerService/<Call>` with a Connect, gRPC or gRPC-Web body. See [Protocol and transport](/docs/api) for the basics. Field types in links are on the [Types](/docs/api/types) page. Tools can read every call and type from [schema.json](/docs/api/schema.json).

## Server info (public, no auth)

### GetServerInfo

Request `GetServerInfoRequest`:

No fields.

Response `GetServerInfoResponse`:

| Field | Type | Notes |
|---|---|---|
| `name` | `string` |  |
| `description` | `string` | Markdown, shown in the sidebar |
| `icon_url` | `string` |  |
| `protocol_version` | `int32` | currently 1 |
| `trusted_issuers` | repeated `string` | auth issuers this server accepts, e.g. "https://sblurp.chat" |
| `base_url` | `string` | canonical URL; also the JWT audience |
| `member_count` | `int32` |  |
| `accent_color` | `string` | optional "#rrggbb" for the rail icon |
| `invite_only` | `bool` | Join needs a valid invite_code |
| `join_questions` | repeated `string` | Questions a plugin asks people who join; show each with a text field and send the answers in JoinRequest.answers, in the same order. |
| `banner` | [Attachment](/docs/api/types#attachment) | optional image above the name in the sidebar |
| `retention_days` | `int32` | Message retention. 0 = keep forever. Otherwise the server deletes messages (with their thread replies, reactions and files) older than this many days, and ListMessages stops returning them. Clients hide loaded messages older than now minus this many days; no per-message events. |
| `needs_setup` | `bool` | A brand-new community whose owner has not finished or skipped the setup wizard yet. Clients show the owner the wizard; others ignore it. |

## Joining

Authentication: every call except GetServerInfo carries

```text
Authorization: Bearer <token>
```

The token is either

```text
(a) a JWT from a trusted issuer with aud = this server's base_url, or
(b) a bot token ("sbb_..."), created by an admin.
```

A human token for a person who has not joined yet is accepted only by Join and GetServerInfo; other calls return PERMISSION_DENIED.

### Join

Request `JoinRequest`:

| Field | Type | Notes |
|---|---|---|
| `display_name` | `string` | Profile fields the user agreed to share with this community. |
| `avatar_url` | `string` |  |
| `bio` | `string` |  |
| `invite_code` | `string` | checked only when the server is invite_only; case-insensitive |
| `answers` | repeated `string` | to GetServerInfoResponse.join_questions, in order |

Response `JoinResponse`:

| Field | Type | Notes |
|---|---|---|
| `member` | [Member](/docs/api/types#member) |  |
| `created` | `bool` | false if already a member |

### GetMe

Request `GetMeRequest`:

No fields.

Response `GetMeResponse`:

| Field | Type | Notes |
|---|---|---|
| `member` | [Member](/docs/api/types#member) |  |
| `server_version` | `string` | The next three are for admins and the owner only; empty for others. "1.2.3", "1.3.0-beta.1", "nightly-20261016-abc1234" or "dev" |
| `server_track` | `string` | "stable", "beta" or "nightly" |
| `update_available` | `string` | a newer version on server_track, or "" |

### UpdateProfile

Request `UpdateProfileRequest`:

| Field | Type | Notes |
|---|---|---|
| `display_name` | optional `string` |  |
| `avatar_url` | optional `string` |  |
| `bio` | optional `string` |  |
| `status_text` | optional `string` |  |
| `status_emoji` | optional `string` |  |

Response `UpdateProfileResponse`:

| Field | Type | Notes |
|---|---|---|
| `member` | [Member](/docs/api/types#member) |  |

## Sync

### GetState

Everything a client needs to render the sidebar after connecting.

Request `GetStateRequest`:

No fields.

Response `GetStateResponse`:

| Field | Type | Notes |
|---|---|---|
| `me` | [Member](/docs/api/types#member) |  |
| `channels` | repeated [Channel](/docs/api/types#channel) | channels I am in (my sidebar), incl. DMs |
| `members` | repeated [Member](/docs/api/types#member) |  |
| `read_states` | repeated [ReadState](/docs/api/types#readstate) |  |
| `seq` | `int64` | server seq at snapshot time; Subscribe from here |
| `roles` | repeated [Role](/docs/api/types#role) | sorted by position |
| `channel_mode` | [ChannelMode](/docs/api/types#channelmode) |  |
| `blocked_member_ids` | repeated `string` | members I blocked, see BlockMember |
| `muted_member_ids` | repeated `string` | members I muted, see MuteMember |

### Subscribe

Server stream. Browsers support server streams over Connect; there is deliberately no bidirectional stream. Clients send with unary calls.

Server stream of [Event](/docs/api/types#event) messages.

Request `SubscribeRequest`:

| Field | Type | Notes |
|---|---|---|
| `after_seq` | `int64` | Last seq the client has applied. The server first replays every event after it (that the caller may see), then streams live events. If the gap is too large the server sends Event.resync and the client must call GetState again. |

## Membership and moderation

Rank: owner > admin > moderator > member. Nobody acts on a member of equal or higher rank.

### SetMemberRole

Admin or owner only. The new role must be MEMBER, MODERATOR or ADMIN and lower than the caller's, and the target must rank lower than the caller.

Request `SetMemberRoleRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |
| `role` | [MemberRole](/docs/api/types#memberrole) |  |

Response `SetMemberRoleResponse`:

| Field | Type | Notes |
|---|---|---|
| `member` | [Member](/docs/api/types#member) |  |

### LeaveCommunity

Leave this community. The owner cannot leave. You may Join again later. Others get member_left.

Request `LeaveCommunityRequest`:

No fields.

Response `LeaveCommunityResponse`:

No fields.

### KickMember

Moderator or higher, and a higher rank than the target. The target leaves the community and its private channels but may Join again. Not for bots (use BotService.DeleteBot).

Request `KickMemberRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |

Response `KickMemberResponse`:

No fields.

### BanMember

Like KickMember, and the person's login (iss, sub) cannot Join again.

Request `BanMemberRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |

Response `BanMemberResponse`:

No fields.

### ListBans

Moderator or higher. Newest first.

Request `ListBansRequest`:

No fields.

Response `ListBansResponse`:

| Field | Type | Notes |
|---|---|---|
| `bans` | repeated [Ban](/docs/api/types#ban) |  |

### UnbanMember

Moderator or higher. The login may Join again (with an invite if the community is invite-only) and comes back with the same member id.

Request `UnbanMemberRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |

Response `UnbanMemberResponse`:

No fields.

## Invites and settings (admin or owner)

An open community lets anyone with a valid login Join. An invite-only one needs JoinRequest.invite_code, except for the first person (the owner) and people who are already members. A banned login cannot join either way.

Roles (admins only, except that everyone sees them in GetState).

### CreateInvite

Request `CreateInviteRequest`:

| Field | Type | Notes |
|---|---|---|
| `expires_in_seconds` | `int64` | 0 = never; max one year |
| `max_uses` | `int32` | 0 = no limit |

Response `CreateInviteResponse`:

| Field | Type | Notes |
|---|---|---|
| `invite` | [Invite](/docs/api/types#invite) |  |

### ListInvites

Every invite that was not revoked, newest first, including expired and used-up ones (compare expires_at_ms and uses to show their state).

Request `ListInvitesRequest`:

No fields.

Response `ListInvitesResponse`:

| Field | Type | Notes |
|---|---|---|
| `invites` | repeated [Invite](/docs/api/types#invite) |  |

### RevokeInvite

Request `RevokeInviteRequest`:

| Field | Type | Notes |
|---|---|---|
| `code` | `string` |  |

Response `RevokeInviteResponse`:

No fields.

### UpdateServerSettings

Request `UpdateServerSettingsRequest`:

| Field | Type | Notes |
|---|---|---|
| `invite_only` | optional `bool` |  |
| `channel_mode` | [ChannelMode](/docs/api/types#channelmode) | UNSPECIFIED = leave as is |
| `description` | optional `string` | Markdown; "" falls back to the server's -description |
| `banner_id` | optional `string` | an uploaded image's id (POST /upload); "" removes it |
| `icon_id` | optional `string` | rail icon, an uploaded image's id; "" falls back to -icon-url |
| `retention_days` | optional `int32` | 0 = keep forever, else 1 to 3650 |
| `setup_done` | `bool` | true clears GetServerInfoResponse.needs_setup |

Response `UpdateServerSettingsResponse`:

| Field | Type | Notes |
|---|---|---|
| `invite_only` | `bool` |  |
| `channel_mode` | [ChannelMode](/docs/api/types#channelmode) |  |
| `description` | `string` |  |
| `banner` | [Attachment](/docs/api/types#attachment) |  |
| `icon_url` | `string` |  |
| `retention_days` | `int32` |  |

### CreateRole

Request `CreateRoleRequest`:

| Field | Type | Notes |
|---|---|---|
| `name` | `string` |  |
| `color` | `string` | "#rrggbb" or empty |

Response `CreateRoleResponse`:

| Field | Type | Notes |
|---|---|---|
| `role` | [Role](/docs/api/types#role) |  |

### UpdateRole

Request `UpdateRoleRequest`:

| Field | Type | Notes |
|---|---|---|
| `role_id` | `string` |  |
| `name` | optional `string` |  |
| `color` | optional `string` |  |
| `position` | optional `int32` |  |

Response `UpdateRoleResponse`:

| Field | Type | Notes |
|---|---|---|
| `role` | [Role](/docs/api/types#role) |  |

### DeleteRole

Fails with FAILED_PRECONDITION while the role still limits a channel, so deleting a role never opens a channel to everyone.

Request `DeleteRoleRequest`:

| Field | Type | Notes |
|---|---|---|
| `role_id` | `string` |  |

Response `DeleteRoleResponse`:

No fields.

### SetMemberRoles

Replaces the member's roles. Works for bots too.

Request `SetMemberRolesRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |
| `role_ids` | repeated `string` |  |

Response `SetMemberRolesResponse`:

| Field | Type | Notes |
|---|---|---|
| `member` | [Member](/docs/api/types#member) |  |

## Custom emoji

Messages and reactions use a custom emoji as ":name:". Unknown names are plain text. The list and the uploads are plain HTTP, see server/README.md:

```text
GET  {base}/emoji      ListEmojiResponse as protojson, with an ETag.
                       Send If-None-Match; 304 means nothing changed.
POST {base}/emoji      multipart "file" parts (images or .zip files);
                       moderator or higher. Reply: UploadEmojiResponse.
GET  {base}/emoji.zip  every emoji as name.ext in one zip.
GET  {base}/emoji/{id} the image, public, cached forever.
```

After a change every member gets Event.emoji_changed.

### DeleteEmoji

Moderator or higher.

Request `DeleteEmojiRequest`:

| Field | Type | Notes |
|---|---|---|
| `name` | `string` |  |

Response `DeleteEmojiResponse`:

No fields.

## Blocking

### BlockMember

Any member, for themselves only. Blocking hides nothing on the server except DMs: the two cannot open a DM with each other or send in a shared DM. Clients collapse a blocked member's messages. The blocker's devices get Event.member_blocked or member_unblocked. Blocking twice is fine.

Request `BlockMemberRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |

Response `BlockMemberResponse`:

No fields.

### UnblockMember

Request `UnblockMemberRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |

Response `UnblockMemberResponse`:

No fields.

### MuteMember

Any member, for themselves only. Muting is quieter than blocking and does not stop DMs: a muted member's messages do not count toward my unread or mention counts, and clients collapse them, skip their notifications and hide their typing. Blocking does the same. Only the muter's devices see the mute list (GetState.muted_member_ids, Event.member_muted or member_unmuted). Muting twice is fine.

Request `MuteMemberRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |

Response `MuteMemberResponse`:

No fields.

### UnmuteMember

Request `UnmuteMemberRequest`:

| Field | Type | Notes |
|---|---|---|
| `member_id` | `string` |  |

Response `UnmuteMemberResponse`:

No fields.

## Reports (1119)

### ReportMessage

Any member, about a message they can see that someone else wrote, with an optional note (up to 1,000 characters). The server keeps a copy of the text, so the report outlives an edit or a delete. A report of a DM message shares that one message with moderators and nothing else from the DM. Reporting the same message again replaces the note. People may send 5 reports, then 1 every 10 minutes (RESOURCE_EXHAUSTED). Nobody is told who reported, and the reporter cannot list reports.

Request `ReportMessageRequest`:

| Field | Type | Notes |
|---|---|---|
| `message_id` | `string` |  |
| `note` | `string` |  |

Response `ReportMessageResponse`:

No fields.

### ListReports

Moderator or higher. Open reports, newest first, about members the caller outranks (the owner sees reports about everyone, the owner included).

Request `ListReportsRequest`:

No fields.

Response `ListReportsResponse`:

| Field | Type | Notes |
|---|---|---|
| `reports` | repeated [Report](/docs/api/types#report) |  |

### ResolveReport

Moderator or higher, for a report ListReports shows them. Resolving deletes every open report of that message, copies included.

Request `ResolveReportRequest`:

| Field | Type | Notes |
|---|---|---|
| `report_id` | `string` |  |

Response `ResolveReportResponse`:

No fields.

## Sitemap

- [Sblurp: chat for communities](https://sblurp.chat/index.md): Sblurp is group chat with channels, threads and roles. Each community runs on its own server, and one Sblurp account signs you in to all of them.
- [About Sblurp](https://sblurp.chat/about.md): Why Sblurp exists: chat for communities that each run their own server, with no ads, no unasked-for AI and an honest word on encryption.
- [Privacy at Sblurp](https://sblurp.chat/privacy.md): What sblurp.chat stores about you, what it never stores, what it shares with communities you join, and how to get a copy or delete it.
- [Terms of use](https://sblurp.chat/terms.md): The terms for a sblurp.chat account: who can sign up, what you must not do, and what we do and do not promise. A first draft.
- [Create your Sblurp account](https://sblurp.chat/signup.md): Create a Sblurp account: one account for every Sblurp community you join. You need an email, a username, a password and to be 13 or older.
- [Sign in to Sblurp](https://sblurp.chat/login.md): Sign in to your Sblurp account with your email or username and password, or with a passkey. One account works in every Sblurp community.
- [Reset your password](https://sblurp.chat/forgot.md): Forgot your Sblurp password? Type the email you signed up with and we send a link to choose a new one. The link works once, for one hour.
- [Sblurp docs](https://sblurp.chat/docs.md): Sblurp documentation: how accounts and communities work, how to run a community server, how to add bots, and the full API reference.
- [Accounts and communities](https://sblurp.chat/docs/accounts.md): How a Sblurp account relates to the communities you join: what sblurp.chat keeps, what each community server sees, and what stays private.
- [Join a community](https://sblurp.chat/docs/join.md): Join a Sblurp community: add it by address or invite link, answer its questions, choose what to share, and join.
- [Sblurp glossary](https://sblurp.chat/docs/glossary.md): The words the Sblurp docs use, from account and community to issuer, track, bot token and seq, each with a link to the page that explains it.
- [Run a Sblurp community](https://sblurp.chat/docs/self-host.md): Run a Sblurp community on your own server: one small program and one data folder. What you need, how to start it, and what it stores.
- [Run a server](https://sblurp.chat/docs/create.md): For developers: start your own Sblurp community. Run the open-source server, join it first to become the owner, then set it up and invite people.
- [Set up a server with AI](https://sblurp.chat/docs/ai.md): Not a developer? Ask an AI assistant such as Claude to set up your Sblurp community server.
- [Sblurp server sizes and costs](https://sblurp.chat/docs/hosting.md): Which server size to rent for a Sblurp community and what it costs each month: measured limits per size and prices at Fly.io, DigitalOcean, Hetzner, OVHcloud and AWS Lightsail.
- [Run Sblurp on Render](https://sblurp.chat/docs/render.md): Run a Sblurp community server on Render: one Starter web service with a persistent disk, the released image, and optional backups to Cloudflare R2.
- [Run Sblurp on Railway](https://sblurp.chat/docs/railway.md): Run a Sblurp community server on Railway: one service from the released image, a volume for /data, and optional backups to Cloudflare R2.
- [Run Sblurp on Fly.io](https://sblurp.chat/docs/fly.md): Run a Sblurp community server on Fly.io: one always-on machine with a volume, h2c streaming, and optional backups to Cloudflare R2.
- [Run Sblurp on any VPS](https://sblurp.chat/docs/vps.md): Run a Sblurp community server on a small virtual server from Hetzner, OVHcloud, Akamai Linode, Vultr, DigitalOcean or any other host, with Docker Compose.
- [Run Sblurp with Docker and Compose](https://sblurp.chat/docs/docker.md): Run a Sblurp community server with Docker Compose and Caddy: the files, the settings, where the data lives, and how to run it behind your own proxy.
- [Run Sblurp with Podman](https://sblurp.chat/docs/podman.md): Run a Sblurp community server with a Podman Quadlet file, with daily automatic updates that roll back when a new image does not start.
- [Sblurp server configuration](https://sblurp.chat/docs/configuration.md): Every flag and environment variable of the Sblurp community server, with defaults, plus the backup variables of the container image and the built-in limits.
- [TLS and reverse proxies for Sblurp](https://sblurp.chat/docs/proxies.md): Put Caddy, nginx or Cloudflare in front of a Sblurp community server without breaking live streams: h2c, no buffering, no compression.
- [Back up a Sblurp server](https://sblurp.chat/docs/backups.md): Back up a Sblurp community server: Litestream copies the database to Cloudflare R2 within 10 seconds, an encrypted backup tool copies the files, or make a plain copy. How to restore.
- [Update a Sblurp server](https://sblurp.chat/docs/updates.md): Release tracks for the Sblurp server image, the daily update check, manual and automatic updates, and how to roll back.
- [Verify a Sblurp release](https://sblurp.chat/docs/provenance.md): Check that a Sblurp server image was built by the public release workflow, with GitHub attestations or cosign.
- [Troubleshooting a Sblurp server](https://sblurp.chat/docs/troubleshooting.md): Fix common problems with a Sblurp community server: sign-in fails, messages arrive late, certificates, file links, and where the logs are.
- [Invites in Sblurp](https://sblurp.chat/docs/invites.md): Make a Sblurp community invite only, create invite links with an expiry and a use limit, and revoke them.
- [Moderation and roles in Sblurp](https://sblurp.chat/docs/moderation.md): Permissions in a Sblurp community (owner, admin, moderator, member), custom roles that limit channels, channel modes, kicks and bans.
- [Safety in Sblurp](https://sblurp.chat/docs/safety.md): Who can read your messages, how to protect your account, what to do about unwanted messages, how to report a message, what moderators can do, age rules, and how to reach sblurp.chat.
- [Your data in Sblurp](https://sblurp.chat/docs/data.md): What sblurp.chat stores about your account, what a community server stores, who controls each, how long data is kept, backups, export and deletion.
- [Sblurp bots](https://sblurp.chat/docs/bots.md): Two ways to add a bot to a Sblurp community: a JavaScript plugin on the server, or a program anywhere that uses the API with a bot token.
- [Sblurp bot tokens](https://sblurp.chat/docs/bot-tokens.md): Make a Sblurp bot with a token, run the example bot, and call the API from any language: subscribe to events and reply to mentions.
- [Connect an AI agent to Sblurp](https://sblurp.chat/docs/agents.md): How to connect a self-hosted AI agent, such as OpenClaw, NanoClaw or Hermes Agent, to a Sblurp community with a bot token. No built-in connector exists yet.
- [Use Sblurp from Claude Code and other MCP clients](https://sblurp.chat/docs/mcp.md): Add a Sblurp community to Claude Code, Claude Desktop, VS Code or another MCP client by URL, sign in with your sblurp.chat account, and let the agent read and post as you.
- [Sblurp plugins](https://sblurp.chat/docs/plugins.md): Write a Sblurp bot as one JavaScript file and install it in the app, with the API or in the plugins folder: hooks for joins, messages, slash commands and timers, replies, limits and the dice example.
- [Sblurp API: protocol and transport](https://sblurp.chat/docs/api.md): How clients and bots talk to a Sblurp community server: Connect RPC over HTTP, the proto contract, JSON and binary bodies, auth, streaming and versioning.
- [Sblurp API explorer](https://sblurp.chat/docs/api/explorer.md): Browse and try every call of the Sblurp community server API in your browser, from the OpenAPI 3.1 description in openapi.json.
- [Sblurp API: sign-in and tokens](https://sblurp.chat/docs/api/auth.md): How sign-in works across Sblurp: OAuth 2.0 with PKCE at sblurp.chat, per-server access tokens, JWT claims, refresh token rotation, JWKS, and bot tokens.
- [Sblurp API: events](https://sblurp.chat/docs/api/events.md): The Sblurp live event stream: Subscribe, sequence numbers, replay and resync, heartbeats, every event kind, who receives it, and unread counts.
- [Sblurp API: uploads and files](https://sblurp.chat/docs/api/files.md): The plain HTTP endpoints of a Sblurp community server: uploads, signed file URLs, custom emoji, invite links and the health check.
- [Sblurp API: errors and rate limits](https://sblurp.chat/docs/api/errors.md): Error codes the Sblurp community server returns, what each one means for a client, and the rate limits for people and bots.
- [ServerService API reference](https://sblurp.chat/docs/api/server-service.md): Every ServerService call of the Sblurp community server: what it does, who may call it, and its request and response fields.
- [ChatService API reference](https://sblurp.chat/docs/api/chat-service.md): Every ChatService call of the Sblurp community server: what it does, who may call it, and its request and response fields.
- [BotService API reference](https://sblurp.chat/docs/api/bot-service.md): Every BotService call of the Sblurp community server: what it does, who may call it, and its request and response fields.
- [API types](https://sblurp.chat/docs/api/types.md): The messages and enums the Sblurp community server API shares between calls, with every field.
