Edit a user's profile (self or moderator)
PATCH /users/{ref}
Self-edit always allowed; editing other users requires the
users.profile.edit.any capability (Admin by default).
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Request Body required
Section titled “Request Body required ”PATCH semantics — only present fields are updated, but the schema treats each field as the new full value (no JSON Merge Patch nested ops). Clearing a field is sending an empty string (for text) or null (for nullable URLs).
object
object
BCP47 tag, or empty string to reset to “system default”. Validated only loosely server-side — frontend constrains the picker to a known list.
system follows the OS. Empty string CLEARS the account’s
preference, which is not the same thing — a cleared account
leaves each device on the app default, an account set to
system makes every device follow its own OS.
Owner opt-out from anonymous profile exposure (ADR 0070 → ADR 0024). Default false. When true, the owner’s public profile page (/users/by-username, /users/by-ref) returns 404 to anonymous viewers even with public mode on. Authenticated viewers and the content-visibility gating are unaffected.
Responses
Section titled “ Responses ”Updated profile
Merged user + profile view. display_name is the resolved
display string, first non-empty of:
authenticated: profile.display_name → user.fullname → username anonymous: profile.display_name → username
The anonymous ladder skips fullname, and has since #478 —
real name is authenticated-only (ADR 0070 §3), so a fallback
that reached it would defeat that rule silently, for exactly
the users who never set a display name. This sentence used to
describe only the authenticated ladder, which made the schema
the stale source; the rule itself is one Go expression
(users.ResolveDisplayName), shared with PostAuthor.
avatar_url is null for users without an uploaded avatar; the
frontend renders a generated initials disc in that case.
object
The raw user.fullname value, for the rare case a caller needs the unresolved string (account settings page). Most UI surfaces should use display_name instead.
Resolved display string ready to render — never empty.
Free-form platform → handle map. Frontend renders known platforms (twitter, instagram, artstation, …) as icon links; unknown keys are ignored.
object
IETF BCP47 language tag (e.g. “en”, “es-AR”). Empty string = “use system / browser default”. Drives the i18n switcher in the user menu.
UI theme preference. system follows the OS; empty string
= no stored preference. Persisted alongside the profile so
it follows the user across browsers (#677).
Total live posts authored by this user.
Set on profiles mirroring a remote site’s user. NULL = locally-owned account.
Malformed request
object
Human-readable error summary
Example
the request could not be completedAuthentication required, missing, or invalid
object
Human-readable error summary
Example
the request could not be completedExample
{ "error": "authentication required: sign in and retry with a valid session or API token"}Authenticated but missing required capabilities
object
Human-readable error summary
Example
the request could not be completedResource not found
object
Human-readable error summary
Example
the request could not be completedOperator has disabled self-edit for the
field carried in field. The frontend renders an
inline “locked by your administrator” tooltip without
blocking other fields.