Users
A DFOS user profile carries public identity only: a protocol DID, handle, display name, avatar, bio, profile links, a verified domain, and the public spaces its owner belongs to. It never exposes presence, account data, or exact numbers about a person.
GET /v1/users— list public profiles.GET /v1/users/{user}— fetch one profile.GET /v1/users/{user}/spaces— page that user's public space memberships.
Profiles are public by default. An owner can make theirs private, which drops it
from the listing and turns a direct fetch into a 404 Not Found,
indistinguishable from a profile that does not exist.
Identifying a user
All three routes accept a handle (bvalosek, case-insensitive), an identity id
(identity_3drf9nfk93na7vfdk723r6), or a protocol DID
(did:dfos:z8zt7ecn9h8n782kae3k796crva2c73). A handle is a mutable alias: it
can change or be released, and a released handle may later belong to a different
user. Store the identity id or the DID.
Verified domain
Every profile shape carries verifiedDomain: a domain this user has proven
control of, or null. 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 verified 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 user's identity chain claims the domain, and the domain publishes the user's DID back. See Protocol discovery and Origin binding.
Protocol handles
A fetched profile carries an optional protocol block: headOpCid, the current
head operation of the user's identity chain, plus a nested profile
(contentId, headOpCid) when they have a profile content chain. Absent, never
null, when the identity has no chain; profile is absent when the profile is
empty. Listing rows do not carry it. See
Protocol discovery.
Listing
GET /v1/users lists public profiles, cursor-paginated. Ordering is
deterministic and stable across pages but otherwise unspecified — do not depend
on it. Each row carries the identity fields of a fetched profile plus
pinnedSpaces: terse cards (did, domain, displayName, avatarUrl) for
the spaces the user pinned, in their chosen order, possibly empty.
A user's public spaces
GET /v1/users/{user} embeds a preview of the shelf as spaces: every pinned
space, then up to 20 more, most recently joined first. Page
GET /v1/users/{user}/spaces for the whole shelf, cursor-paginated (see
Conventions), ordered pinned spaces first in the
user's chosen order and everything else by join recency. Array order is the
whole ordering signal; there is no numeric position field.
curl 'https://api.dfos.com/v1/users/bvalosek/spaces?limit=20'
| Field | Value |
|---|---|
id, did | The space's stable identifiers. |
domain | Its effective subdomain, a mutable alias. |
displayName, avatarUrl | The space's name and avatar, or null. |
memberCountSummary | The same worded count the space itself returns, e.g. "a few dozen members". Never an exact number; render it as-is. |
pinned | Whether the user pinned this space to the top of their profile. |
pinMessage | The user's short note about the space, or null. Only ever set on a pinned space. |
pinnedAt | When they pinned it (ISO 8601 UTC), or null. |
This is a curated shelf, not a membership audit: a space appears only if it has a public profile, its owners allow memberships to show on member profiles, and the member has not hidden it. The shape carries no role, member number, or join date.
A profile that is private, missing, or not a user returns the same 404 Not Found the profile fetch returns. A public profile with nothing to show returns
an empty list.
See Conventions for the pagination contract, and Posts for how a user appears as a post author.