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; everything here reflects that public profile and the space's public site configuration, never its private membership or activity.
Two endpoints cover spaces:
GET /v1/spaces— list public spaces (a compact discovery card each).GET /v1/spaces/{space}— fetch one space's full public profile.
The list and the single fetch return different shapes: the listing gives a
compact card (identity, avatar, description, links, member summary), while the
single fetch adds the full site configuration (joinMode, siteMode,
subscribeEnabled, header image, application questions, creation time). Fetch a
space directly when you need those fields.
Identifying a space
GET /v1/spaces/{space} accepts three forms of identifier:
- Subdomain, e.g.
field-notes— the space's public host label. - Entity id, e.g.
space_7gx4k2m9. - Protocol DID, e.g.
did:dfos:93n4d6936c3ct6r3k7477dn4z89c39h— the samedidvalue the API returns for the space.
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 protocol DID when you need a durable reference; use the subdomain only for display and links. (This mirrors how user handles work.)
The domain field the API returns is the space's effective subdomain — its
custom domain if it has set one, otherwise the normalized space-{id} form.
Legacy internal identifiers are not accepted and return 404 Not Found.
Listing spaces
GET /v1/spaces returns public spaces, cursor-paginated (see
Conventions for the pagination contract). Each item carries
the space id, did, effective domain, displayName, description,
avatarUrl, its profile links, and a memberCountSummary.
Pass joinMode to filter the listing to spaces with a given join mode:
curl 'https://api.dfos.com/v1/spaces?joinMode=open&limit=20'
The join modes are closed, open, application, paid, and allowlist.
Treat this as an open enum: new modes may be added over time, so handle an
unrecognized value gracefully rather than assuming a closed set (see the
compatibility contract).
Profile links
Both the listing card and the full space carry a links array — the space's
ordered profile links, which is empty when the space has set none. Each link
has:
url— the destination, exactly as the owner entered it.label— an optional owner-curated display label, ornull(fall back totitlewhen it is null).title— the resolved Open Graph title of the linked page, ornull.description— an optional owner-written blurb, ornull.imageUrl— a cached public CDN URL for the linked page's Open Graph preview image, ornull.
Any of the optional fields can be null; render defensively. The title and
imageUrl are resolved by DFOS from the linked page and may be absent for a
page with no Open Graph metadata.
Site configuration (single fetch only)
GET /v1/spaces/{space} adds the fields that describe how the space presents
itself publicly:
joinMode— how people join (the open enum above).siteMode— what the space's public URL presents:join(a join / landing page) orposts(a posts front page). Also an open enum; tolerate unknown values.subscribeEnabled— whether the space's public email-subscribe form is on.memberCountSummary— a worded member-count summary, e.g."a few dozen members". Public surfaces deliberately never expose exact member counts; this string is the only member-count signal, and it is intentionally fuzzy. Render it as-is; do not try to parse a number out of it.privatePostCountSummary— a worded count of the space's private posts, e.g."a few dozen private posts". The exact complement of what the posts listing serves you: a space with no private posts says so explicitly ("no private posts"), it does not omit the field.chatMessageCountSummary— a worded count of the messages across all the space's channels, including its private, member-only ones. Direct messages between members are never included.eventCountSummary— a worded count of the space's published events. A recurring series counts once.mediaUploadCountSummary— a worded count of everything uploaded to the space, including images it serves publicly (the avatar and header above are themselves uploads, and are counted).headerUrl— a public CDN URL for the space's header / wallpaper image, ornull.createdAt— when the space was created (ISO 8601 UTC).
The four …CountSummary fields work like memberCountSummary: an intentionally
fuzzy sense of scale, never an exact number. Render each as-is; do not try to
parse a number out of it. The strings are an open set — new phrasings can appear
without notice, and a zero is always spelled out ("no events"), never omitted.
Application questions
applicationQuestions is present only for a public application-mode space
(joinMode: "application") that has configured at least one question. It is
absent for every other space — do not treat its absence as an error. Each
question carries its question text and whether answering it isRequired to
apply. The list is viewer-independent — 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. The API never lets an anonymous caller tell a
private space from a missing one — see Content visibility
for the full model.
See the interactive reference for the exact response schema of every field, and Posts for a space's public posts.