Skip to main content

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'
FieldValue
id, didThe space's stable identifiers.
domainIts effective subdomain, a mutable alias.
displayName, avatarUrlThe space's name and avatar, or null.
memberCountSummaryThe same worded count the space itself returns, e.g. "a few dozen members". Never an exact number; render it as-is.
pinnedWhether the user pinned this space to the top of their profile.
pinMessageThe user's short note about the space, or null. Only ever set on a pinned space.
pinnedAtWhen 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.