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; 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 same did value 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).

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, or null (fall back to title when it is null).
  • title — the resolved Open Graph title of the linked page, or null.
  • description — an optional owner-written blurb, or null.
  • imageUrl — a cached public CDN URL for the linked page's Open Graph preview image, or null.

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) or posts (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, or null.
  • 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.