AppView
The Colibri AppView serves an assembled view of communities that live in permissioned spaces. It is the only party holding a community’s credentials, so community-authored writes go through it, and it is the party the community’s PDS consults before issuing anyone a credential.
This page is the interface. For how it is put together, see AppView Architecture.
Requests are meant to be proxied through the user’s own PDS. Address this service with:
did:web:spaces-api.colibri.social#colibri_appviewPush registration targets the notification service instead:
did:web:spaces-api.colibri.social#colibri_notifsBoth resolve to the same endpoint today. Using #colibri_notifs for push
registration keeps the declared service boundary correct if notifications are
ever split onto their own deployment.
Every method lives under /xrpc/.
Authentication
Section titled “Authentication”Authenticated methods need an
atproto service auth
token: a JWT signed by the caller’s repo signing key, with aud set to this
AppView’s DID and lxm set to the method being called.
The usual way to get one is to let the PDS mint it. Send the request to the
user’s PDS with an atproto-proxy: did:web:spaces-api.colibri.social#colibri_appview
header, and the PDS attaches Authorization: Bearer <jwt> on the proxied
request.
Both WebSockets accept the same token, either in an Authorization: Bearer
header or, for browsers that cannot set headers on a WebSocket, as an auth
query parameter.
What a client writes for itself
Section titled “What a client writes for itself”Messages, reactions and labels are written by the client, straight to the user’s
own PDS with com.atproto.space.createRecord, naming the channel’s space. There
is no AppView method for sending a message.
Community-authored records go through the procedures on this page, because only the AppView holds the community’s credentials.
Identity resolution is the client’s own job too. This AppView proxies no
com.atproto.identity or com.atproto.sync method, and permissioned records
are read from the space rather than from a record endpoint.
Errors
Section titled “Errors”Every method declares its errors in its lexicon, and the AppView returns them as the standard XRPC error shape:
{ "error": "InsufficientPermissions", "message": "you do not have member.ban" }Four appear on most authenticated methods and are not repeated in the tables below unless a method treats them specially:
| Error | Meaning |
|---|---|
AuthRequired |
No token, or a token this service will not accept |
NotAMember |
You are not a member of the community |
InsufficientPermissions |
You are a member, but lack the permission or the rank |
CommunityNotFound |
No such community, or one you cannot see |
Community
Section titled “Community”A community’s structural records, such as its profile, its settings, its roles, and its moderation log, live in spaces that the community’s own DID owns. Only the AppView holds that DID’s credentials, because a community account exists solely to authorize its spaces and no person ever logs into it directly. A client can’t write these records itself: it calls one of the procedures below, and the AppView authenticates as the community and makes the write on the caller’s behalf after checking that the caller holds the required permission. See Communities for how a community’s spaces fit together.
Creation and settings
Section titled “Creation and settings”social.colibri.beta.community.create
Section titled “social.colibri.beta.community.create”Provisions a new community: an account on the AppView’s PDS under a handle the server assigns, its four spaces, an owner role held by the caller, and a starter layout of two categories holding one text channel and one voice channel between them. The call can run long enough that a client should also watch social.colibri.beta.sync.subscribeEvents for progress.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
AlreadyExists |
A community already exists with the requested handle. |
PdsUnavailable |
The AppView’s PDS could not be reached to provision the account. |
UpstreamFailure |
A PDS or third-party call the AppView depends on failed. |
social.colibri.beta.community.update
Section titled “social.colibri.beta.community.update”Updates a community’s name, description, and join and labeling settings. Any field left out of the request keeps its current value.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks the community.manage permission. |
CommunityNotFound |
No community exists with that DID. |
CredentialsUnavailable |
The AppView’s stored credentials for this community are missing or unusable. |
UpstreamFailure |
A PDS or third-party call the AppView depends on failed. |
social.colibri.beta.community.delete
Section titled “social.colibri.beta.community.delete”Deletes a community’s spaces and its account. Members’ own records stay in their own repos, since the community never held them, and stop being readable once the spaces that granted access to them are gone.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks the community.delete permission. |
CommunityNotFound |
No community exists with that DID. |
CredentialsUnavailable |
The AppView’s stored credentials for this community are missing or unusable. |
UpstreamFailure |
A PDS or third-party call the AppView depends on failed. |
social.colibri.beta.community.migrate
Section titled “social.colibri.beta.community.migrate”Migrates a repo-backed community onto spaces, recreating its structure in new spaces and stamping them with migratedFrom. Message history stays in the legacy public repos and is served alongside the migrated community, because messages live in their authors’ own repos and only their authors can move them.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks permission to migrate this community. |
CommunityNotFound |
No community exists at the legacy record’s DID. |
AlreadyExists |
This legacy community has already been migrated. |
CredentialsUnavailable |
The AppView’s stored credentials for this community are missing or unusable. |
UpstreamFailure |
A PDS or third-party call the AppView depends on failed. |
social.colibri.beta.community.registerCredentials
Section titled “social.colibri.beta.community.registerCredentials”Hands the AppView credentials for a community hosted on a PDS it doesn’t administer, so it can act as that community without having created it. The password is stored encrypted at rest. Once credentials are already registered, only an existing administrator of the community may replace them.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks permission to register credentials for this community. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
CredentialsRejected |
The PDS refused the identifier and password. |
UpstreamFailure |
A PDS or third-party call the AppView depends on failed. |
social.colibri.beta.community.reorderCategories
Section titled “social.colibri.beta.community.reorderCategories”Sets the complete display order of a community’s categories. The supplied list must name exactly the community’s existing categories, just in a new order.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks the category.update permission. |
CommunityNotFound |
No community exists with that DID. |
InvalidRequest |
The supplied categories do not match the community’s existing set. |
CredentialsUnavailable |
The AppView’s stored credentials for this community are missing or unusable. |
Membership and applications
Section titled “Membership and applications”social.colibri.beta.community.join
Section titled “social.colibri.beta.community.join”Admits the requesting user to a community, or files an application if the community requires approval. Joining is an AppView procedure rather than a record the user writes, because admission is what makes the community’s spaces readable to the joining user in the first place.
status is joined or pending. member is present only when status is joined.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
CommunityNotFound |
No community exists with that DID. |
InvitationNotFound |
No invitation exists with that code. |
AlreadyMember |
The requesting user already holds a member record in this community. |
Banned |
The requesting user is banned from this community. |
CredentialsUnavailable |
The AppView’s stored credentials for this community are missing or unusable. |
social.colibri.beta.community.leave
Section titled “social.colibri.beta.community.leave”Removes the requesting user’s own membership in a community.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
CommunityNotFound |
No community exists with that DID. |
MemberNotFound |
The requesting user does not hold a member record in this community. |
SoleOwner |
The requesting user is the last holder of a protected role and cannot leave. |
CredentialsUnavailable |
The AppView’s stored credentials for this community are missing or unusable. |
social.colibri.beta.community.kick
Section titled “social.colibri.beta.community.kick”Removes another member from a community. Unlike a ban, a kicked member may rejoin, unless the community requires approval to join.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the member.kick permission. |
CommunityNotFound |
No community exists at the given DID. |
MemberNotFound |
The subject does not hold a member record in this community. |
RoleHierarchy |
The subject holds a role at or above the caller’s highest role position. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.community.setMemberRoles
Section titled “social.colibri.beta.community.setMemberRoles”Sets the complete set of roles a member holds, replacing whatever they held before.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks the role.manage permission. |
CommunityNotFound |
No community exists with that DID. |
MemberNotFound |
The subject does not hold a member record in this community. |
RoleNotFound |
One of the requested roles does not exist. |
RoleHierarchy |
The requesting user cannot grant or revoke a role at or above their own highest position. |
CredentialsUnavailable |
The AppView’s stored credentials for this community are missing or unusable. |
social.colibri.beta.community.approveApplication
Section titled “social.colibri.beta.community.approveApplication”Approves a pending application, admitting the applicant as a member.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the approval.manage permission. |
CommunityNotFound |
No community exists at the given DID. |
ApplicationNotFound |
No pending application exists for the subject in this community. |
AlreadyMember |
The subject already holds a member record in this community. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.community.dismissApplication
Section titled “social.colibri.beta.community.dismissApplication”Hides a pending application from the active queue without approving or refusing it. This is an AppView-local change: it is never written to the community’s repo, so it doesn’t need the community’s credentials.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the approval.manage permission. |
CommunityNotFound |
No community exists at the given DID. |
ApplicationNotFound |
No pending application exists for the subject in this community. |
social.colibri.beta.community.undismissApplication
Section titled “social.colibri.beta.community.undismissApplication”Restores a dismissed application to the active queue. Like dismissing it, this is an AppView-local change that is never written to the community’s repo.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the approval.manage permission. |
CommunityNotFound |
No community exists at the given DID. |
ApplicationNotFound |
No pending application exists for the subject in this community. |
social.colibri.beta.community.listApplications
Section titled “social.colibri.beta.community.listApplications”Lists the pending requests to join a community that requires approval.
includeDismissed defaults to false. limit defaults to 50 and cannot exceed 100.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the approval.manage permission. |
CommunityNotFound |
No community exists at the given DID. |
social.colibri.beta.community.listMembers
Section titled “social.colibri.beta.community.listMembers”Lists a community’s admitted members, optionally filtered to those holding a given role.
limit defaults to 50 and cannot exceed 100.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks permission to list this community’s members. |
CommunityNotFound |
No community exists with that DID. |
Invitations
Section titled “Invitations”social.colibri.beta.community.createInvitation
Section titled “social.colibri.beta.community.createInvitation”Creates an invitation code for a community, optionally limited to a number of uses or an expiry time.
maxUses, when given, must be at least 1.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks the invitation.create permission. |
CommunityNotFound |
No community exists with that DID. |
social.colibri.beta.community.deleteInvitation
Section titled “social.colibri.beta.community.deleteInvitation”Deletes an invitation code, so it can no longer be redeemed.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks the invitation.delete permission. |
CommunityNotFound |
No community exists with that DID. |
InvitationNotFound |
No invitation exists with that code. |
social.colibri.beta.community.getInvitation
Section titled “social.colibri.beta.community.getInvitation”Resolves an invitation code to the community it leads to, so a client can show what a code invites the user into before they accept it. This is the one invitation endpoint that doesn’t require membership or even authentication, since the whole point is to preview an invitation before joining.
| Error | Description |
|---|---|
InvitationNotFound |
No invitation exists with that code. |
social.colibri.beta.community.listInvitations
Section titled “social.colibri.beta.community.listInvitations”Lists a community’s invitation codes.
limit defaults to 50 and cannot exceed 100.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks the invitation.create permission. |
CommunityNotFound |
No community exists with that DID. |
Moderation
Section titled “Moderation”social.colibri.beta.community.ban
Section titled “social.colibri.beta.community.ban”Bans a member from a community. This removes their member record and stops the community issuing them further space credentials, so they can no longer read the community’s spaces. Their existing content isn’t deleted: it stays in their own repo, where it can still be labelled or filtered like anyone else’s.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the member.ban permission. |
CommunityNotFound |
No community exists at the given DID. |
AlreadyBanned |
The subject is already banned from this community. |
RoleHierarchy |
The subject holds a role at or above the caller’s highest role position. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.community.unban
Section titled “social.colibri.beta.community.unban”Lifts a ban, letting the community issue the subject space credentials again.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the member.unban permission. |
CommunityNotFound |
No community exists at the given DID. |
NotBanned |
The subject is not currently banned from this community. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.community.listBans
Section titled “social.colibri.beta.community.listBans”Lists the members currently banned from a community, derived from the moderation log’s ban and unban entries.
limit defaults to 50 and cannot exceed 100.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the member.ban permission. |
CommunityNotFound |
No community exists at the given DID. |
social.colibri.beta.community.listModerationLog
Section titled “social.colibri.beta.community.listModerationLog”Lists a community’s moderation audit log, most recent entry first.
action is ban, unban, or kick. limit defaults to 50 and cannot exceed 100.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the moderation.viewLog permission. |
CommunityNotFound |
No community exists at the given DID. |
social.colibri.beta.community.applyLabel
Section titled “social.colibri.beta.community.applyLabel”Applies a label to a record in one of the community’s spaces. This is how content gets hidden, marked as a spoiler, or has its link previews suppressed. The record itself is left untouched, since it lives in its author’s own repo, but the effect differs by label value. A hidden label stops the AppView serving the record at all: it is withheld from reads, from notifications and from the events socket, and replies to it see a deletedMessageView in place of their parent. Moderators holding label.apply and the record’s own author still receive it. spoiler and embeds-suppressed are display hints, forwarded on messageView.labels for the client to honour. Which labelers count is the labelers list in the community’s settings, defaulting to the community itself.
val is one of hidden, spoiler, or embeds-suppressed. scope narrows the label to specific URIs inside the subject record. Leave it out to label the whole record.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the label.apply permission. |
CommunityNotFound |
No community exists at the given DID. |
SpaceNotFound |
No space exists at the given reference. |
InvalidRequest |
The scope or val is not valid for the subject record. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.community.negateLabel
Section titled “social.colibri.beta.community.negateLabel”Retracts a label by writing a negating one, rather than deleting the original label record.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the label.apply permission. |
CommunityNotFound |
No community exists at the given DID. |
SpaceNotFound |
No space exists at the given reference. |
LabelNotFound |
No active label matching the subject and value exists to negate. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.community.getCommunity
Section titled “social.colibri.beta.community.getCommunity”Gets a community by DID or handle.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
CommunityNotFound |
No community exists at that identifier. |
social.colibri.beta.community.listCategories
Section titled “social.colibri.beta.community.listCategories”Gets a community’s category layout, with each category’s channels already nested inside it, so a client can draw the whole sidebar from one call.
type is social.colibri.beta.channel.text or social.colibri.beta.channel.voice.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks permission to view this community’s categories. |
CommunityNotFound |
No community exists with that DID. |
social.colibri.beta.community.listChannels
Section titled “social.colibri.beta.community.listChannels”Lists a community’s channels as a flat list, for clients that don’t need the category grouping.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks permission to view this community’s channels. |
CommunityNotFound |
No community exists with that DID. |
social.colibri.beta.community.listRoles
Section titled “social.colibri.beta.community.listRoles”Lists a community’s roles.
position is the role’s place in the hierarchy: a higher number outranks a lower one, and that ordering is what RoleHierarchy errors elsewhere enforce.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The requesting user lacks permission to view this community’s roles. |
CommunityNotFound |
No community exists with that DID. |
Categories
Section titled “Categories”Categories group a community’s channels for display. Ordering lives outside both records: the community’s social.colibri.beta.community.settings record holds categoryOrder for the categories themselves, and each category record holds channelOrder for the channels inside it.
social.colibri.beta.category.create
Section titled “social.colibri.beta.category.create”Creates a category in a community and appends it to the community’s categoryOrder.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the category.create permission. |
CommunityNotFound |
No community exists at the given DID. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.category.update
Section titled “social.colibri.beta.category.update”Renames a category.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the category.update permission. |
CommunityNotFound |
No community exists at the given DID. |
CategoryNotFound |
No category exists at the given record key. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.category.delete
Section titled “social.colibri.beta.category.delete”Deletes a category and removes it from categoryOrder. Its channels are not deleted: they become uncategorised until moved to another category.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the category.delete permission. |
CommunityNotFound |
No community exists at the given DID. |
CategoryNotFound |
No category exists at the given record key. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
Channels
Section titled “Channels”A channel is a permissioned space in its own right, not a record inside the community. The space key is the channel’s identity, and the channel’s own configuration is the social.colibri.beta.channel record at key self inside that space. That is also why the channel record carries no community or category field: channel.create creates the space first and then writes the configuration record into it, and channel.delete deletes the space outright.
allowedRoles and allowedMembers gate who may post. visibleToRoles and visibleToMembers gate who may read, so a read-only announcement channel can restrict posting while staying readable to the whole community. Non-empty visibility lists make the channel private, and the community’s PDS then refuses space credentials for it to anyone outside those lists.
A channel you may not read is left out of every listing it would otherwise appear in. listChannels omits it, listCategories omits it from its category’s channels, and getChannel and listMessages answer Forbidden. The AppView never names it, so a client can treat the channels it receives as the whole of what the user can see.
social.colibri.beta.channel.create
Section titled “social.colibri.beta.channel.create”Creates a channel: first the space, then the social.colibri.beta.channel record at key self inside it, and finally the channel’s entry at the end of its category’s channelOrder.
type must be one of social.colibri.beta.channel.text or social.colibri.beta.channel.voice.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user lacks the channel.create permission. |
CommunityNotFound |
No community matches the given DID. |
CategoryNotFound |
No category matches the given record key. |
RoleHierarchy |
The requesting user cannot grant a role that outranks their own. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
PdsUnavailable |
The community’s PDS is unreachable or the AppView has no administrative access to it. |
UpstreamFailure |
The community’s PDS failed while creating the channel’s space. |
social.colibri.beta.channel.update
Section titled “social.colibri.beta.channel.update”Updates a channel’s configuration. A field left absent from the request keeps its current value rather than being cleared.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user lacks the channel.update permission. |
ChannelNotFound |
No channel matches the given space reference. |
CategoryNotFound |
No category matches the given record key. |
RoleHierarchy |
The requesting user cannot grant a role that outranks their own. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.channel.delete
Section titled “social.colibri.beta.channel.delete”Deletes a channel’s space. Messages members wrote in it stay in their own repos: deleting the channel makes them unreadable to everyone but their authors, it does not delete them.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user lacks the channel.delete permission. |
ChannelNotFound |
No channel matches the given space reference. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
PdsUnavailable |
The community’s PDS is unreachable or the AppView has no administrative access to it. |
UpstreamFailure |
The community’s PDS failed while deleting the channel’s space. |
social.colibri.beta.channel.getChannel
Section titled “social.colibri.beta.channel.getChannel”Gets one channel.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user may not read this channel. |
ChannelNotFound |
No channel matches the given space reference. |
Messages carrying a hidden label from a labeler the community honours are
withheld, so a page can come back shorter than limit. That is not the end of
the results: keep following cursor until it is absent, because the cursor is
keyed on the record key of the last row examined, not the last row returned.
A reply whose parent is withheld or deleted gets a deletedMessageView in
parent instead of a messageView, so parent is a union and has to be
discriminated on $type. Moderators holding label.apply, and a message’s own
author, receive hidden messages in full with the label attached.
social.colibri.beta.channel.listMessages
Section titled “social.colibri.beta.channel.listMessages”Lists a channel’s messages, newest first by default. Messages from the repo-backed community a channel was migrated from are included in the same list and marked legacy.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user may not read this channel. |
ChannelNotFound |
No channel matches the given space reference. |
social.colibri.beta.channel.listReactions
Section titled “social.colibri.beta.channel.listReactions”Lists everyone who reacted to one message, for a reaction hover card.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user may not read this channel. |
ChannelNotFound |
No channel matches the given space reference. |
MessageNotFound |
No message matches the given author and record key in that channel. |
social.colibri.beta.channel.listUnreadStatus
Section titled “social.colibri.beta.channel.listUnreadStatus”Lists per-channel unread state for the requesting user, optionally scoped to one community.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.channel.putReadCursors
Section titled “social.colibri.beta.channel.putReadCursors”Pushes the requesting user’s read cursors for one community to the AppView, so unread counts update immediately instead of waiting for the next sync pass. The client writes its own social.colibri.beta.channel.read record to its personal space first, then calls this. Clients should debounce this call rather than sending it on every message.
The cursors array is a complete replacement of the community’s read state, not a merge with what the AppView already has.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
CommunityNotFound |
No community matches the given DID. |
social.colibri.beta.channel.reorder
Section titled “social.colibri.beta.channel.reorder”Sets the complete channel order within one category.
channels must contain exactly the category’s current set of channels, reordered. Adding, dropping, or duplicating a channel space key fails the request.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user lacks the channel.update permission. |
CommunityNotFound |
No community matches the given DID. |
CategoryNotFound |
No category matches the given record key. |
InvalidRequest |
The given channels do not match the category’s current set of channels. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
A role carries a base permission list plus optional per-channel allow and deny overrides. Evaluating a permission in a channel checks the channel deny list first, which beats everything, then the channel allow list, which grants the permission even without a base grant, and only then falls back to the role’s base permissions.
Every action taken against another member is guarded by hierarchy: the actor must strictly outrank the target, comparing highest role position to highest role position. A tie does not outrank. This applies the same way to granting a role, editing a role’s position, or deleting a role, so nobody can act on a role at or above their own.
social.colibri.beta.role.create
Section titled “social.colibri.beta.role.create”Creates a role in a community.
The caller can only grant a position strictly below their own highest role, and can only grant permissions they hold themselves.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the role.manage permission. |
CommunityNotFound |
No community exists at the given DID. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
RoleHierarchy |
The requested position is at or above the caller’s highest role position, or the permissions include one the caller does not hold. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.role.update
Section titled “social.colibri.beta.role.update”Updates a role’s settings, including its per-channel overrides.
A role marked protected, which is the role created alongside the community, cannot be updated at all. Any allow entry added through channelOverrides is held to the same hierarchy check as permissions: the caller must hold that permission themselves.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the role.manage permission. |
CommunityNotFound |
No community exists at the given DID. |
RoleNotFound |
No role exists at the given record key. |
RoleProtected |
The role is marked protected and cannot be modified. |
RoleHierarchy |
The change would place the role at or above the caller’s highest role position, or grants a permission the caller does not hold. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
social.colibri.beta.role.delete
Section titled “social.colibri.beta.role.delete”Deletes a role and strips it from every member who holds it.
A protected role cannot be deleted, and the role being deleted must be strictly below the caller’s highest role position.
| Error | Description |
|---|---|
AuthRequired |
The request is missing, malformed, or unverifiable service auth. |
Forbidden |
The caller lacks the role.manage permission. |
CommunityNotFound |
No community exists at the given DID. |
RoleNotFound |
No role exists at the given record key. |
RoleProtected |
The role is marked protected and cannot be deleted. |
RoleHierarchy |
The role is at or above the caller’s highest role position. |
CredentialsUnavailable |
The AppView cannot act as the community because its stored credentials are missing or unusable. |
Actors and preferences
Section titled “Actors and preferences”Every Colibri user gets exactly one permissioned space of type social.colibri.beta.actor.preferences, anchored on their own DID, holding their mutes, settings, and read cursors. Its authority is the user’s own DID, and its policy is an empty member-list. The space manager authorizes the space’s own authority before it consults the policy, so the owner always gets in and an empty list means nobody else ever does. A delegation token for this space is single-use and lives 60 seconds, so the AppView cannot renew its own access without an active client. grantSpaceAccess is how the client hands one over, and when no client is online the AppView keeps serving its last synced copy rather than failing.
Beyond preferences, this group covers a user’s public profile, off-protocol presence, community list, and account deletion.
social.colibri.beta.actor.getProfile
Section titled “social.colibri.beta.actor.getProfile”Gets a user’s profile: their identity, resolved display fields, and any live presence the AppView is tracking for them.
avatar and banner are URLs served by this AppView’s blob proxy, not raw blob references. theme always comes from the user’s own social.colibri.beta.actor.profile record, even when syncBluesky is set and the other display fields come from app.bsky.actor.profile instead.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
ActorNotFound |
No user matches the given DID or handle. |
social.colibri.beta.actor.getPreferences
Section titled “social.colibri.beta.actor.getPreferences”Gets the requesting user’s own settings, as the AppView currently holds them. This is the AppView’s synced copy, not a live read of the user’s personal space, so it can lag behind a write the user just made until putSettings or putMutes catches it up or a sync notification arrives.
notificationLevel is one of all or mentionsAndReplies.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.actor.listCommunities
Section titled “social.colibri.beta.actor.listCommunities”Gets the requesting user’s communities, ordered the way the user prefers them in their sidebar.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.actor.putSettings
Section titled “social.colibri.beta.actor.putSettings”Pushes the requesting user’s social.colibri.beta.actor.settings record to the AppView so the change takes effect immediately, instead of waiting for the AppView to notice the write on its own sync pass. The client writes the record to its own personal space first, then calls this. Any field left out of the request leaves the AppView’s current value for it unchanged.
notificationLevel accepts all or mentionsAndReplies. The response shape is the same preferences object getPreferences returns, after the update.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
InvalidRequest |
A field is present but does not hold an allowed value. |
social.colibri.beta.actor.putMutes
Section titled “social.colibri.beta.actor.putMutes”Replaces the AppView’s whole copy of the requesting user’s mute list, so a mute or unmute takes effect immediately. As with putSettings, the client writes the underlying social.colibri.beta.actor.mute records to its own personal space first, then calls this with the complete, resulting list.
The response shape is the same preferences object getPreferences returns, after the update.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.actor.setStatus
Section titled “social.colibri.beta.actor.setStatus”Sets the requesting user’s off-protocol presence: their status line and derived online state. Any field left out of the request leaves the AppView’s current value for it unchanged.
onlineState accepts online, away, dnd, or offline. text is capped at 32 characters.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
InvalidRequest |
A field is present but does not hold an allowed value. |
social.colibri.beta.actor.grantSpaceAccess
Section titled “social.colibri.beta.actor.grantSpaceAccess”Hands the AppView a delegation token for one of the requesting user’s own personal spaces, most often their social.colibri.beta.actor.preferences space. The AppView exchanges it for a space credential and syncs the space, as described above.
Call this again before expiresAt to keep access current. A user may only grant access to a space they are themselves the authority of.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
InvalidDelegationToken |
The delegation token is malformed, expired, or already used. |
SpaceNotFound |
No space matches the given space reference. |
NotAuthorized |
The delegation token does not grant access to the given space. |
UpstreamFailure |
The user’s PDS failed while the AppView exchanged the delegation token or synced the space. |
social.colibri.beta.actor.getDeletionStatus
Section titled “social.colibri.beta.actor.getDeletionStatus”Gets what would happen if the requesting user deleted their account right now, so a client can show them the consequences before they confirm.
soleOwnedCommunities lists communities the user solely owns, which block deletion until they are transferred or deleted.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.actor.deleteAccount
Section titled “social.colibri.beta.actor.deleteAccount”Erases the requesting user’s Colibri data held by this AppView: their indexed records, notifications, mutes, settings, and cached profile. This never touches the user’s own repos, since those stay under the user’s own control regardless of what this AppView indexes.
A user who solely owns a community must transfer or delete it first. See getDeletionStatus.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
SoleOwnerOfCommunity |
The user solely owns at least one community and must transfer or delete it before deleting their account. |
Notifications
Section titled “Notifications”A notification is raised for a mention, a reply, or a plain message in a channel the user is watching, and it always carries who caused it and where it happened. The AppView tracks two separate marks on a user’s notifications: unread, cleared in bulk by timestamp with updateSeen, and unseen per channel, used to drive a channel’s unread badge. Push delivery is a separate concern, registered per device through registerPush and unregisterPush.
social.colibri.beta.notification.listNotifications
Section titled “social.colibri.beta.notification.listNotifications”Gets the requesting user’s notifications, newest first.
limit defaults to 50 and accepts 1 through 100. kind is one of mention, reply, or message. author has the same shape getProfile returns. message is only present when the message it was raised for is still readable, and mentionRole is only set for a role mention.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.notification.getUnreadCount
Section titled “social.colibri.beta.notification.getUnreadCount”Gets how many of the requesting user’s notifications are unread, for a badge count that does not need the notifications themselves.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.notification.getUnseen
Section titled “social.colibri.beta.notification.getUnseen”Gets the requesting user’s unseen notifications for one channel, used to render that channel’s unread badge detail rather than a global count.
limit defaults to 50 and accepts 1 through 100.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
ChannelNotFound |
No channel matches the given space reference. |
social.colibri.beta.notification.updateSeen
Section titled “social.colibri.beta.notification.updateSeen”Marks every notification raised at or before a point in time seen, the bulk action behind “mark all as read”.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
social.colibri.beta.notification.updateSeenForMessage
Section titled “social.colibri.beta.notification.updateSeenForMessage”Marks the notifications raised by one message seen, for when a user reads a single message rather than a whole channel.
message addresses the message by its author’s DID and its record key, since a record key is only unique per repo and a channel space aggregates many authors’ repos.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
ChannelNotFound |
No channel matches the given space reference. |
MessageNotFound |
No message matches the given record reference in that channel. |
social.colibri.beta.notification.registerPush
Section titled “social.colibri.beta.notification.registerPush”Registers a push endpoint for the requesting user’s device.
provider is webpush or fcm, and platform is web, ios, or android. Web Push registration requires endpoint, p256dh, and auth. FCM registration requires token.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
InvalidRequest |
The fields required for the given provider are missing or malformed. |
PushNotConfigured |
This AppView has no keypair for the requested provider. |
social.colibri.beta.notification.unregisterPush
Section titled “social.colibri.beta.notification.unregisterPush”Removes a previously registered push endpoint.
provider is webpush or fcm. Unregistering a Web Push endpoint needs endpoint, and unregistering an FCM registration needs token.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
Embeds and GIFs
Section titled “Embeds and GIFs”Pasting a link into a message gets a preview through getMetadata, and the GIF picker is backed by searchGifs, trendingGifs, and gifCategories. The AppView fetches every link on the client’s behalf behind an SSRF guard, so a client’s own address is never exposed to the site being previewed, and a hostname that resolves to a private, loopback, or otherwise blocked address is refused even if the URL itself looks public.
social.colibri.beta.embed.getMetadata
Section titled “social.colibri.beta.embed.getMetadata”Fetches and parses a link preview: page title, description, site name, and a preview image or video where the page declares one.
image.url and video.url are served by this AppView’s own proxy rather than pointing at the original site. video.mimeType is always video/mp4 or video/webm, since those are the only formats considered playable.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
NotFetchable |
The target refused the request, timed out, or resolves to an address the AppView will not fetch. |
UpstreamFailure |
The target site failed while the AppView fetched or parsed it. |
social.colibri.beta.embed.searchGifs
Section titled “social.colibri.beta.embed.searchGifs”Searches the GIF picker’s provider.
limit defaults to 50 and accepts 1 through 100. id is stable enough to save as a favorite in putSettings’ gifFavorites.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
GifsNotConfigured |
This AppView has no GIF provider key configured. |
UpstreamFailure |
The GIF provider failed. |
social.colibri.beta.embed.trendingGifs
Section titled “social.colibri.beta.embed.trendingGifs”Gets trending GIFs from the GIF picker’s provider, for the picker’s default view before a user types a query.
limit defaults to 50 and accepts 1 through 100.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
GifsNotConfigured |
This AppView has no GIF provider key configured. |
UpstreamFailure |
The GIF provider failed. |
social.colibri.beta.embed.gifCategories
Section titled “social.colibri.beta.embed.gifCategories”Lists the categories shown in the GIF picker.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
GifsNotConfigured |
This AppView has no GIF provider key configured. |
UpstreamFailure |
The GIF provider failed. |
Joining a voice channel, publishing and consuming audio, and reacting to who else is in the room all happen over the voice signalling WebSocket, described in the voice signalling socket. social.colibri.beta.voice.moderate is the one voice action that exists as an XRPC method instead: muting, deafening, or disconnecting someone else in a voice channel.
social.colibri.beta.voice.moderate
Section titled “social.colibri.beta.voice.moderate”Acts on someone in a voice channel: forcing their microphone muted, forcing them deafened, or disconnecting them outright. Any boolean field left out of the request leaves the AppView’s current value for it unchanged, except disconnect, which only ever acts when set.
The caller needs the voice.moderate permission for the channel, and the subject must currently be connected to it.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user lacks the voice.moderate permission. |
ChannelNotFound |
No channel matches the given space reference. |
NotInVoice |
The subject is not currently connected to the channel. |
VoiceUnavailable |
This AppView has no voice SFU running. |
social.colibri.beta.blob.get replaced the old public blob proxy. It now serves blobs from permissioned spaces by holding a space credential a browser client never gets, fetching through com.atproto.space.getBlob rather than the public com.atproto.sync.getBlob. Do not confuse the two: space.getBlob requires the AppView’s own credential for that space, while sync.getBlob only ever works for a blob on a fully public repo. Every byte this endpoint serves is verified against the requested CID before it reaches the caller, and the content type is sniffed from the bytes rather than trusted from whatever the upstream declared.
social.colibri.beta.blob.get
Section titled “social.colibri.beta.blob.get”Serves a blob from a permissioned space through the AppView, or a blob from a public repo directly. The response is the raw blob bytes, not a JSON body, and it honours HTTP range requests.
space is required for a blob in a permissioned space, and omitted for a blob on a public repo. variant selects a rendition of a resizable image: thumbnail, avatar, banner, or full for the untouched original. filename, when set, sets the downloaded file’s name.
| Error | Description |
|---|---|
AuthRequired |
The request has no valid service auth. |
Forbidden |
The requesting user may not read the space holding the blob. |
BlobNotFound |
No blob matches the given DID and CID. |
InvalidRequest |
The arguments are inconsistent or malformed beyond schema validation. |
UpstreamFailure |
The repo’s PDS failed while the AppView fetched the blob. |
Server
Section titled “Server”social.colibri.beta.server.describeServer is what a client probes to decide whether a host is a Colibri AppView at all, and which of its optional features are turned on.
social.colibri.beta.server.describeServer
Section titled “social.colibri.beta.server.describeServer”Gets information about this AppView: its own identity, the software version it runs, and the space types and optional features it understands.
handleDomain is the domain community handles are minted under, and pds is the PDS communities are created on. features lists which of voice, push, gifs, and embeds this deployment has turned on. spaceTypes lists the space type NSIDs this AppView understands, which is how a client can tell a deployment apart that predates a given space type from one that just has no instances of it yet.
The events socket
Section titled “The events socket”social.colibri.beta.sync.subscribeEvents is a WebSocket at
/xrpc/social.colibri.beta.sync.subscribeEvents. It carries everything that happens
while a client is connected: messages, reactions, structure changes, presence,
typing, notifications and voice participation.
Frames are JSON in both directions, and each one is tagged with a $type naming
a definition in social.colibri.beta.sync.defs:
{ "$type": "social.colibri.beta.sync.defs#messageEvent", "…": "…" }Subscription is explicit
Section titled “Subscription is explicit”A connection is sent nothing it did not ask for.
Send a subscribe frame naming the communities and channels you want. The
server keeps a topic index and answers with subscribed, confirming what the
connection is now receiving. Anything you are not entitled to read is silently
dropped from that confirmation rather than refused, so compare what you asked
for against what came back.
Subscribing to a community also subscribes you to every channel in it that you
may read, so asking for the community is enough to receive its messages,
reactions, labels and typing. The set is kept current: when your access changes,
the server revises your channel topics and sends a fresh subscribed naming
what you now hold.
unsubscribe removes topics again, and dropping a community drops its channels
with it. Events for the connected user personally, such as notifications, arrive
without any subscription.
Client frames
Section titled “Client frames”| Frame | What it does |
|---|---|
subscribe |
Adds communities and channels to what this connection receives |
unsubscribe |
Removes communities and channels |
heartbeat |
Keeps the connection alive and reports the user is present. Answered with ack |
typing |
Reports that the user is typing in a channel |
viewChannel |
Reports which channel the user is looking at, which drives read state and away detection |
setPresence |
Updates the user’s own presence |
Server frames
Section titled “Server frames”| Frame | Sent when |
|---|---|
ack |
Answering a heartbeat |
error |
Something the client sent could not be acted on |
subscribed |
After a subscribe or unsubscribe, confirming what this connection now receives |
messageEvent |
A message was written, edited or deleted in a subscribed channel. create and update carry the full messageView in message, and delete carries only the subject reference. A message already hidden when it is indexed is not published |
reactionEvent |
A reaction was added or removed in a subscribed channel |
channelEvent |
A channel was created, changed or deleted. Only the channel’s readers are told. Gaining access arrives as a create and losing it as a delete, so a client can add and drop entries from this frame alone |
categoryEvent |
A category was created, changed or deleted |
roleEvent |
A role was created, changed or deleted |
memberEvent |
Someone joined, left or had their roles changed. A join also goes to the member who was admitted, who is not yet subscribed to the community they just entered |
communityEvent |
A community’s profile or settings changed |
applicationEvent |
The pending-application queue changed. Only sent to members who can manage approvals |
labelEvent |
A label was applied or retracted on a record in a subscribed space. A create of hidden means the message is not served any more and should be taken off screen, and a negate of hidden is followed by a messageEvent re-publishing it |
moderationEvent |
A moderation action was logged. Only sent to members who can read the log |
notificationEvent |
A notification was raised for the connected user |
seenEvent |
The user marked notifications seen elsewhere, so this client should clear the same badges |
presenceEvent |
Someone’s presence changed |
typingEvent |
Someone is typing in a subscribed channel |
voiceEvent |
A voice channel’s participants changed |
communityProgressEvent |
Progress while a community is being created, which takes several PDS round trips |
The server pings every 30 seconds and drops connections that stop answering.
The voice socket
Section titled “The voice socket”Voice is a second WebSocket at /xrpc/social.colibri.beta.voice.subscribeSignals,
separate from the events socket, and it speaks
mediasoup client signalling. Frames are tagged from
social.colibri.beta.voice.defs.
Client frames
Section titled “Client frames”| Frame | What it does |
|---|---|
join |
Joins a voice channel. Required first |
leave |
Leaves the channel this connection is in |
getRtpCapabilities |
Requests the router’s RTP capabilities for the joined channel |
createTransport |
Requests a new WebRTC transport |
connectTransport |
Completes DTLS negotiation for a transport this connection created |
produce |
Starts producing media on a send transport |
closeProducer |
Closes a producer this connection owns |
consume |
Starts consuming another peer’s producer on a receive transport |
resumeConsumer |
Resumes a consumer, which is created paused |
setSelfState |
Updates this connection’s own mute or deafen state. An absent field is left unchanged |
heartbeat |
Keeps the connection alive. Answered with ack |
Server frames
Section titled “Server frames”| Frame | Sent when |
|---|---|
ack |
Answering a heartbeat |
error |
Something the client sent could not be acted on |
joined |
Confirming this connection joined a channel |
rtpCapabilities |
Answering getRtpCapabilities |
transportOptions |
Answering createTransport |
producerInfo |
A producer is available to consume |
consumerOptions |
Answering consume |
producerRemoved |
A peer’s producer closed |
peerJoined, peerLeft |
A peer joined or left the channel |
speakingUpdate |
Someone started or stopped speaking |
moderationChanged |
A peer’s server-enforced mute or deafen state changed |
There are no correlation identifiers on these frames. A reply is matched to a request by its type and by the order it arrives in, so do not have two requests of the same kind outstanding on one connection.
Server-enforced mute is distinct from self-mute. A moderator mute arrives as
moderationChanged, and setSelfState cannot lift it.
Protocol callbacks
Section titled “Protocol callbacks”Three inbound methods are called by a community’s PDS rather than by a client. They authenticate with service auth from the space authority, and each one verifies that the caller is the authority for the space it is talking about.
com.atproto.simplespace.checkUserAccess
Section titled “com.atproto.simplespace.checkUserAccess”Answers whether a user may hold a credential for a space. The community’s PDS calls this before minting anything, so this is where bans, membership and channel visibility are enforced.
It answers from local projections with an in-memory cache and makes no outbound
call of its own. It fails closed: an unrecognised space type, an unknown
community or a caller that is not the space’s authority all return
authorized: false.
com.atproto.space.notifyWrite
Section titled “com.atproto.space.notifyWrite”Tells the AppView that a repo in a space has been written to, which enqueues a pull of that repo. Notifications are best effort, and the periodic sweep is what guarantees correctness, so a missed one costs latency rather than data. See Sync.
com.atproto.space.notifySpaceDeleted
Section titled “com.atproto.space.notifySpaceDeleted”Tells the AppView a space no longer exists, so it drops its copy and forgets the credential. Ignored unless the caller is that space’s authority.
See also
Section titled “See also”- AppView Architecture: what implements this.
- Lexicons: the record and method schemas themselves.
- Facets: how rich text is encoded.