> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myme.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Profile

> The signed-in user's account profile — username, name, bio, avatar — served via /profile/me and the OIDC userinfo endpoint

Every Marfa account has a single canonical profile. It carries the public-facing identity fields a user wants apps to see when they sign in: username, first and last name, bio, avatar. Email comes from the auth identity record and is exposed only with the `email` scope.

A profile is platform-internal — `system.profile` — but virtual. There is no items-table row per user. The profile is served over a dedicated endpoint that reads from the underlying user record.

## Fields

| Field                       | Required | Editable | Notes                                                                                                                                                                                                     |
| --------------------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `username`                  | yes      | yes      | Unique per Marfa instance. 3-32 chars, lowercase alphanumeric and hyphens. Same convention as a [publisher handle](/concepts/handles-and-publishers) — when the account publishes, this is the namespace. |
| `first_name`                | no       | yes      |                                                                                                                                                                                                           |
| `last_name`                 | no       | yes      |                                                                                                                                                                                                           |
| `bio`                       | no       | yes      | Short free-text.                                                                                                                                                                                          |
| `avatar_url`                | no       | derived  | Resolves to the user's uploaded avatar, or to a deterministic placeholder generated from `username` when none is set.                                                                                     |
| `email`                     | derived  | no       | Mirror of the auth identity record. Read-only on the profile.                                                                                                                                             |
| `created_at` / `updated_at` | derived  | no       | Standard timestamps.                                                                                                                                                                                      |

There is no `display_name` field. Apps that want one compose it from `first_name`, `last_name`, or fall back to `username`.

## Endpoints

<Steps>
  <Step title="Read the caller's own profile">
    `GET /profile/me`. Bearer-auth required. Returns the full profile shape; avatar URL is reconstructed at read time.
  </Step>

  <Step title="Update editable fields">
    `PATCH /profile/me`. Accepts any subset of `username`, `first_name`, `last_name`, `bio`. Username changes flow through the same validators as handle claims (reserved-handle and uniqueness checks); collisions return `409 conflict`, reserved values return `400 handle_reserved`.
  </Step>

  <Step title="Manage the avatar">
    `POST /profile/me/avatar` accepts an image upload (`image/png`, `image/jpeg`, `image/webp`, `image/svg+xml`). Subject to the standard blob size cap and the space's `blobs` and `storage_bytes` quotas. `DELETE /profile/me/avatar` clears the upload; the placeholder reappears.
  </Step>

  <Step title="Read via OIDC userinfo">
    `GET /oauth/userinfo` returns OIDC-shaped userinfo for an OAuth bearer. Field set is gated by the granted scopes — `profile` exposes `username` / `first_name` / `last_name` / `bio` / `avatar_url`; `email` exposes `email` and `email_verified`. With no OIDC scopes granted the response carries `sub` only. Raw API-key bearers are rejected — userinfo is OAuth-flow-bound.
  </Step>
</Steps>

## OIDC scopes

Profile data is exposed to apps through standard OIDC scopes alongside Marfa's data-plane scope grammar:

* `openid` — the canonical OIDC marker scope. Always granted on a Marfa OAuth flow that returns userinfo.
* `profile` — exposes the non-email profile fields.
* `email` — exposes the canonical email and verification status.

These scopes don't project into Marfa's type-permission grammar (`<type>:<op>`). They affect the userinfo response only — they don't grant read/write access to any items, edges, or metadata. An app that wants both profile data and item access requests the OIDC scope alongside the relevant `<type>:<op>` scopes.

## Profile is not `core.entity.person`

A user profile is auth-side identity. A `core.entity.person` is item-graph data — a person represented in a space's data model, possibly the user themselves, possibly someone else. They overlap conceptually but live in different planes:

* The **profile** is one record per account, served at `/profile/me`, governed by OAuth scopes.
* A **`core.entity.person` item** is item-table data, addressable, queryable, governed by item-level type permissions.

A user who wants to model themselves as an entity in their own data graph creates a `core.entity.person` item. The platform doesn't auto-link the two; that's app-level concern.

## Multi-space context

A user has one profile, regardless of how many spaces they own or are members of. Profile is identity. Spaces are data scopes. Switching spaces changes which items are visible; the signed-in user is still the same person with the same profile.
