Permissioned Spaces

Managing Spaces

Creating a space

const response = await fetch("https://happyview.example.com/xrpc/dev.happyview.space.createSpace", {
  method: "POST",
  headers: {
    "X-Client-Key": CLIENT_KEY,
    "Authorization": `DPoP ${ACCESS_TOKEN}`,
    "DPoP": DPOP_PROOF,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    type: "com.example.forum",
    skey: "main",
    displayName: "My Forum",
    description: "A place for discussion",
    accessMode: "default_allow",
  }),
});
interface CreateSpaceResponse {
  uri: string;
}
const data: CreateSpaceResponse = await response.json();

Input:

FieldTypeRequiredDescription
typestring (NSID)YesThe space type; describes what this space is for
skeystringYesSpace key; differentiates spaces of the same type
displayNamestringNoHuman-readable name
descriptionstringNoDescription of the space
accessModestringNodefault_allow (default) or default_deny
managingAppDidstringNoDID of the application that manages this space
configobjectNoSpace configuration (see below)

Response (201):

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

The creator is automatically added as a write member. Use dev.happyview.space.getSpace to retrieve the full space object.

Space configuration

The config object supports:

FieldTypeDefaultDescription
membershipPublicbooleanfalseWhether the member list is visible without authentication
recordsPublicbooleanfalseWhether records are readable without membership

Additional fields are preserved as-is.

Getting a space

const response = await fetch(
  "https://happyview.example.com/xrpc/dev.happyview.space.getSpace?space=ats://did:plc:abc123/com.example.forum/main",
  {
    headers: {
      "X-Client-Key": CLIENT_KEY,
      "Authorization": `DPoP ${ACCESS_TOKEN}`,
      "DPoP": DPOP_PROOF,
    },
  },
);
interface Space {
  uri: string;
  isOwner: boolean;
}
const data: Space = await response.json();

If membershipPublic is false, the caller must be authenticated and be a member (or the owner) to see the space. Non-members receive a 404 Not Found.

Listing spaces

Returns spaces where the authenticated user is a member.

const response = await fetch(
  "https://happyview.example.com/xrpc/dev.happyview.space.listSpaces?limit=20",
  {
    headers: {
      "X-Client-Key": CLIENT_KEY,
      "Authorization": `DPoP ${ACCESS_TOKEN}`,
      "DPoP": DPOP_PROOF,
    },
  },
);
interface Space {
  uri: string;
  isOwner: boolean;
}
interface ListSpacesResponse {
  spaces: Space[];
  cursor?: string;
}
const data: ListSpacesResponse = await response.json();

Parameters:

FieldTypeRequiredDefaultDescription
limitintegerNo50Max spaces to return (1-100)
cursorstringNoPagination cursor

Response:

{
  "spaces": [
    {
      "uri": "ats://did:plc:abc123/com.example.forum/main",
      "isOwner": true
    }
  ],
  "cursor": "MjAyNi0wNS0wOVQxMjowMDowMFp8YXRzOi8vZGlkOnBsYzphYmMxMjMvY29tLmV4YW1wbGUuZm9ydW0vbWFpbg"
}

Updating a space

Only the space owner or a HappView super admin can update a space.

const response = await fetch("https://happyview.example.com/xrpc/dev.happyview.space.updateSpace", {
  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",
    displayName: "Updated Forum Name",
    accessMode: "default_deny",
    appAllowlist: ["did:web:myapp.example.com"],
  }),
});

All fields except space are optional. Only provided fields are updated. To clear an optional field, pass null.

Deleting a space

Only the space owner or a HappyView super admin can delete a space.

const response = await fetch("https://happyview.example.com/xrpc/dev.happyview.space.deleteSpace", {
  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",
  }),
});