MenuHow an app talks to a group

How an app talks to a group

The three servers involved, the names to use on the live network, and the six things an app does.

This section is a working guide to building an app against a group host: a group’s own website, an events app, anything that shows or acts on groups. It follows the requests the reference apps make.

Names on the live network

Setting Value
Host URL https://host.opensocial.fyi
Host DID did:web:host.opensocial.fyi
Service audience for group methods did:web:host.opensocial.fyi#opensocial
Namespace fyi.opensocial

Three kinds of server

Every request goes to one of three places. Getting this right is most of the work.

Server What it does for your app
The user’s own PDS Signs them in (OAuth). Mints service auth for calling the group host and delegation tokens for reading spaces. Stores every record the user writes, including their records inside a group’s spaces.
The group’s host The group’s PDS: the group DID’s #atproto_pds endpoint points here. Serves the group API (fyi.opensocial.*), exchanges delegation tokens for space credentials, lists who has written into each space, and stores records the group itself writes.
Other members’ PDSes Store their records inside the group’s spaces. To read a space you read from each writer’s PDS.

A group’s data isn’t in one place. The group’s profile, roles and events live with the group. A member’s RSVP or acceptance lives with that member.

What an app does

  1. Sign the user in with OAuth, asking for the group permission sets and the host methods you call.
  2. Find groups: from declarations on the network, the host’s public listing, or the spaces on the user’s own PDS.
  3. Read a space: delegation token, then space credential, then records from each writer.
  4. Call group methods with service auth: join, invite, admit, moderate.
  5. Write records: a member’s own records into a group space, or records as the group.
  6. Put it together: a group’s events page.

Setting up a project

Spaces, space scopes and the com.atproto.space.* methods exist only in the spaces alpha builds of the atproto packages. Pin them exactly, and override the transitive ones too. Each package also has a plain 0.0.0 release that satisfies a ^0.0.0-spaces-alpha-… range and can’t be installed.

{
  "dependencies": {
    "@atproto/oauth-client-browser": "0.0.0-spaces-alpha-20260915165437",
    "@atproto/lex": "0.0.0-spaces-alpha-20260915165437",
    "@atproto/lex-client": "0.0.0-spaces-alpha-20260915165437",
    "@atproto/jwk-jose": "^0.2.4"
  },
  "overrides": {
O0.0.0-spaces-alpha-20260915165437RIDES
  }
}

(overrides is npm’s field. With pnpm, put the same map under pnpm.overrides.) @atproto/jwk-jose has no alpha build; the regular release works.

Imports

The snippets in these guides use:

import { BrowserOAuthClient, type OAuthSession } from "@atproto/oauth-client-browser";
import { JoseKey } from "@atproto/jwk-jose";
import { Client } from "@atproto/lex";
import * as com from "./lexicons/com.js"; // generated, see below

Typed calls for com.atproto.space.*

new Client(session).call(com.atproto.space.getDelegationToken, …) needs generated code for those methods. Save the schemas you call into ./lexicons/, one JSON file per NSID (lexicons/com/atproto/space/getDelegationToken.json, …), then generate:

npx lex build --lexicons ./lexicons --out ./src/lexicons

The schemas are in llms-full.txt under “Permissioned-data methods”. Generate only the ones you call: a schema that refers to another (the calendar event refers to community.lexicon.location.*) needs that one in the folder too.

If you’d rather not generate code, call XRPC through the session directly. session.fetchHandler("/xrpc/com.atproto.space.getDelegationToken?space=…") sends an authenticated request to the user’s PDS.

Schemas

Every schema these guides mention, including the com.atproto.space.* methods, is in llms-full.txt under the names the host serves.