Skip to main content

Spaces

A space is a group's home on DFOS. A space appears in this API only when its owners have turned on a public profile, and only its public profile and site configuration are served — never its private membership or activity.

  • GET /v1/spaces — list public spaces.
  • GET /v1/spaces/{space} — fetch one space's full public profile.

The two shapes differ: the listing gives a compact card, and the single fetch adds the site configuration below.

Identifying a space

GET /v1/spaces/{space} accepts a subdomain (home), an entity id (space_vnzfk7hth9vadc3daahd48), or a protocol DID (did:dfos:9ctvrdn9vedda7efetrhcdakfh4cr2k). The subdomain is a mutable alias: a space can change its domain, and a released domain can later belong to a different space. Store the entity id or the DID; use the subdomain for display and links. User handles work the same way.

domain is the space's effective subdomain — its custom domain when it has set one, otherwise the normalized space-{id} form.

Listing spaces

GET /v1/spaces returns public spaces ordered by recent activity, cursor-paginated (see Conventions). Each item carries id, did, domain, displayName, description, avatarUrl, verifiedDomain, links, memberCountSummary, and category when the space has filed one.

curl 'https://api.dfos.com/v1/spaces?joinMode=open&category=music&limit=20'

Four optional filters narrow it. Each is a request parameter; none is encoded in a cursor.

ParameterMeaning
joinModeclosed, open, application, or allowlist. An open enum — handle an unrecognized value gracefully (see the compatibility contract).
activeWithinDaysSpaces active within that many days. Clamped to 1–365, never rejected. A space with no recorded activity is excluded.
categoryA discovery category. Normalized (trimmed, whitespace collapsed, lowercased) before comparison, so "Music " matches music, and a value that normalizes to empty filters nothing. Pass back a category an item emitted.
optedIntrue returns only spaces that opted in to the directory, false only those that have not; omit it for both. Opting in is curation, not privacy — a public space that never opted in is still listed unless you ask for the opted-in set.

Both shapes carry links, the space's ordered profile links, empty when it has set none. Any field but url can be null; render defensively. title and imageUrl are resolved from the linked page and are absent for a page with no Open Graph metadata.

FieldValue
urlThe destination, exactly as the owner entered it.
labelOwner-curated display label, or null. Fall back to title.
titleThe resolved Open Graph title of the linked page, or null.
descriptionAn owner-written blurb, or null.
imageUrlCached public CDN URL for the page's Open Graph preview image, or null.

Verified domain

verifiedDomain is an external domain the space has proven control of, or null. Distinct from domain, the DFOS subdomain. A value proves control of that domain at the last check and nothing else — not personhood, endorsement, or notability. Render the domain itself rather than a checkmark.

It is null when no domain is claimed, the claim has not verified yet, or the domain now attests a different identity. A domain that stops responding keeps its value; one that contradicts this identity reads null until it attests it again.

The proof has two halves you can check yourself: the space's identity chain claims the domain, and the domain publishes the space's DID back. See Protocol discovery and Origin binding.

Site configuration (single fetch only)

GET /v1/spaces/{space} adds:

FieldValue
joinModeHow people join (the open enum above).
siteModeWhat the space's public URL presents: join (a landing page) or posts (a posts front page). Also an open enum.
subscribeEnabledWhether the space's public email-subscribe form is on.
memberCountSummaryA worded member count, e.g. "a few dozen members".
privatePostCountSummaryA worded count of the space's private posts — the complement of what the posts listing serves. A space with none says "no private posts".
chatMessageCountSummaryA worded count of the messages across all the space's channels, including its private, member-only ones. Direct messages between members are never included.
eventCountSummaryA worded count of the space's published events. A recurring series counts once.
mediaUploadCountSummaryA worded count of everything uploaded to the space, including the images it serves publicly — the avatar and header are counted.
categoryThe normalized discovery category the space filed itself under. Absent, never null, when it has filed none. Pass it back verbatim to the listing's category filter rather than displaying it.
headerUrlA public CDN URL for the space's header image, or null.
createdAtWhen the space was created (ISO 8601 UTC).
protocolThe space's protocol handles: headOpCid, plus a nested profile (contentId, headOpCid) when the space has a profile content chain. Not on the listing card.

protocol is absent, never null, when the space has no identity chain, and profile is absent when the profile is empty. See Protocol discovery.

Every …CountSummary is a worded sense of scale, never an exact number. Render each as-is rather than parsing a number out of it. The strings are an open set, and a zero is spelled out ("no events"), never omitted.

Application questions

applicationQuestions is present only for a public application-mode space (joinMode: "application") with at least one question configured, and absent for every other space. Each question carries its question text and whether answering it isRequired. The list is the same for every caller.

{
"applicationQuestions": [{ "question": "What draws you to this space?", "isRequired": true }]
}

When a space is not found

A space that has no public profile and a space that does not exist both return the same 404 Not Found — see Content visibility.


See the interactive reference for the exact response schema of every field, and Posts for a space's public posts.