Compatibility and stability contract
This page is the stability contract: what the API guarantees stays the same over time, and what can change. For how to actually talk to the API today — pagination mechanics, the identifier forms you pass, reading errors — see Conventions.
The DFOS API is in beta. During the beta window the surface may still change. At general availability (GA) the contract becomes additive-only: existing fields and behavior are preserved, and changes arrive as new fields, new values, and new endpoints.
What clients must tolerate
Write clients that keep working as the API grows:
- Unknown fields. Responses may gain new fields at any time, without a version bump. Ignore fields you do not recognize rather than failing on them.
- Unknown enum values. Fields such as
joinModeandsiteModemay gain new values at any time, without a version bump. Handle unrecognized values gracefully instead of assuming a closed set.
Cursors
Pagination cursors (nextCursor, previousCursor) are opaque tokens. Store and
return them unchanged — never parse, decode, or construct them. Their internal
structure is not part of the contract and can change.
Identifiers
- Entity ids (
space_…,post_…, and similar) and protocol DIDs are canonical and stable. They identify the same entity for its lifetime. - Subdomains are mutable aliases. A space can change its domain, and a freed domain can later belong to a different space. A subdomain you stored may silently begin resolving to a different space.
Store entity ids or protocol DIDs when you need a durable reference. Use subdomains only for display and links.
DIDs on the wire
Decentralized identifiers in API responses are protocol DIDs — did:dfos:
followed by 31 characters. Treat them as opaque, stable identifiers.
Media URLs
Media objects come back in two URL classes, and every media object tells you which it is:
- Public images (post covers, avatars, inline body images) resolve to a
permanent, unsigned CDN URL. These carry no
urlExpiresAt. - Private media (audio, video, and other file attachments) resolve to a
time-limited signed URL, returned inline on the post response. These carry
a
urlExpiresAttimestamp marking when the URL stops working, and its presence is the signal that the URL is signed and expiring.
Never persist a signed URL (any media url / playbackUrl accompanied by
urlExpiresAt). Re-fetch the post to obtain fresh URLs when you need them —
data responses are not cached (see
Rate limits and caching), so a fresh fetch always
returns fresh URLs.
Even a permanent public URL is stable only while the media remains referenced by public content: these are not archival URLs. If the underlying content is removed or the reference is dropped, the URL can stop resolving. Re-fetch media URLs from the API rather than treating any of them as durable.
Errors
Error responses are machine-readable and documented in the OpenAPI specification. Read the response body's error fields rather than relying only on the HTTP status.