Permissioned Spaces

Invites

Invites let space authorities distribute membership tokens without knowing recipients' DIDs in advance.

Creating an invite

Only the space authority or a super admin can create invites.

const response = await fetch("https://happyview.example.com/xrpc/dev.happyview.space.createInvite", {
  method: "POST",
  headers: {
    "X-Client-Key": CLIENT_KEY,
    "Authorization": `DPoP ${ACCESS_TOKEN}`,
    "DPoP": DPOP_PROOF,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    space: "ats://did:plc:abc123/com.example.forum/main",
    access: "write",
    maxUses: 10,
    expiresAt: "2026-06-01T00:00:00Z",
  }),
});
interface CreateInviteResponse {
  inviteId: string;
  token: string;
  access: string;
  maxUses: number;
  expiresAt: string;
}
const data: CreateInviteResponse = await response.json();

Input:

FieldTypeRequiredDefaultDescription
spacestringYesThe space this invite is for
accessstringNoreadAccess level granted on acceptance (read, read_self, or write)
maxUsesintegerNounlimitedMaximum number of times the invite can be redeemed
expiresAtstring (datetime)NoneverWhen the invite expires

Response (201):

{
  "inviteId": "uuid",
  "token": "a1b2c3d4e5f6...",
  "access": "write",
  "maxUses": 10,
  "expiresAt": "2026-06-01T00:00:00Z"
}

Accepting an invite

Any authenticated user can accept an invite token to join the space.

const response = await fetch("https://happyview.example.com/xrpc/dev.happyview.space.acceptInvite", {
  method: "POST",
  headers: {
    "X-Client-Key": CLIENT_KEY,
    "Authorization": `DPoP ${ACCESS_TOKEN}`,
    "DPoP": DPOP_PROOF,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    token: "a1b2c3d4e5f6...",
  }),
});
interface AcceptInviteResponse {
  uri: string;
  access: string;
}
const data: AcceptInviteResponse = await response.json();

Response (201):

{
  "uri": "ats://did:plc:abc123/com.example.forum/main",
  "access": "write"
}

Acceptance fails if:

  • The token is invalid (no matching hash found)
  • The invite has been revoked
  • The invite has reached its maxUses
  • The invite has expired
  • The user is already a member of the space

Revoking an invite

const response = await fetch("https://happyview.example.com/xrpc/dev.happyview.space.revokeInvite", {
  method: "POST",
  headers: {
    "X-Client-Key": CLIENT_KEY,
    "Authorization": `DPoP ${ACCESS_TOKEN}`,
    "DPoP": DPOP_PROOF,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    space: "ats://did:plc:abc123/com.example.forum/main",
    inviteId: "uuid",
  }),
});

Revoking an invite prevents future redemptions but does not remove members who already redeemed it.

Listing invites

Only the space authority or a super admin can list invites.

const response = await fetch(
  "https://happyview.example.com/xrpc/dev.happyview.space.listInvites?space=ats://did:plc:abc123/com.example.forum/main",
  {
    headers: {
      "X-Client-Key": CLIENT_KEY,
      "Authorization": `DPoP ${ACCESS_TOKEN}`,
      "DPoP": DPOP_PROOF,
    },
  },
);
interface Invite {
  id: string;
  access: string;
  maxUses: number;
  uses: number;
  expiresAt: string;
  revoked: boolean;
  createdBy: string;
  createdAt: string;
}
const data: { invites: Invite[] } = await response.json();

Parameters:

FieldTypeRequiredDescription
spacestringYesThe space to list invites for

Response:

{
  "invites": [
    {
      "id": "uuid",
      "access": "write",
      "maxUses": 10,
      "uses": 3,
      "expiresAt": "2026-06-01T00:00:00Z",
      "revoked": false,
      "createdBy": "did:plc:abc123",
      "createdAt": "2026-05-09T12:00:00Z"
    }
  ]
}

The token itself is never returned in list responses — only the invite metadata.