---
title: "Update Profile"
method: PUT
path: "/api/user/profile"
---

# Update Profile

`PUT /api/user/profile`

Partial update of the current user's profile.

Bounded-growth columns (``user_text``, ``soul_text``,
``heartbeat_text``) are routed through
:func:`backend.app.agent.markdown_registry.assert_column_within_budget`
so the dashboard editor cannot bypass the byte cap that the agent's
workspace tools and compaction paths already respect. Returns
``413 Payload Too Large`` with the registry's actual / allowed
sizes so a client-side editor can show a useful error.

## Request body

- UserProfileUpdate — Fields the client is allowed to update on the current user. ``onboarding_complete`` is deliberately not writable here. It is owned by the backend (set by ``OnboardingSubscriber`` when the LLM deletes BOOTSTRAP.md or heuristic evidence appears) so the conversational onboarding can't be short-circuited by the UI. ``data_sharing_consent`` is deliberately not writable here either: it has its own dedicated endpoint (``PUT /api/user/data-sharing-consent``) that always stamps ``data_sharing_consent_at``. Routing it through this generic patch endpoint would lose the timestamp guarantee. ``model_config`` pins ``extra="ignore"`` so unknown fields (including ``data_sharing_consent`` if a client tries to slip it through here) are silently dropped. This is the contract the dedicated-endpoint test relies on. If pydantic ever flips the global default to ``"forbid"``, this declaration keeps the contract stable.
  - `phone` string, nullable
  - `timezone` string, nullable
  - `soul_text` string, nullable
  - `user_text` string, nullable
  - `heartbeat_text` string, nullable
  - `heartbeat_opt_in` boolean, nullable
  - `heartbeat_frequency` string, nullable
  - `heartbeat_max_daily` integer, nullable

## Response `200`

Successful Response

- UserProfileResponse
  - `id` string, required
  - `user_id` string, required
  - `phone` string, required
  - `timezone` string, required
  - `soul_text` string, required
  - `user_text` string, required
  - `heartbeat_text` string, required
  - `preferred_channel` string, required
  - `channel_identifier` string, required
  - `heartbeat_opt_in` boolean, required
  - `heartbeat_frequency` string, required
  - `heartbeat_max_daily` integer
  - `onboarding_complete` boolean, required
  - `is_active` boolean, required
  - `data_sharing_consent` boolean
  - `data_sharing_consent_at` string, nullable
  - `created_at` string, required
  - `updated_at` string, required

## Other responses

- `422` — Validation Error

## Changes

- **2026-05-01** `beaacd674ec8` — 2 info
  - added the optional property `data_sharing_consent` to the response with the `200` status
  - added the optional property `data_sharing_consent_at` to the response with the `200` status
- **2026-04-18** `12185a14dc83` — 1 warning
  - removed the request property `onboarding_complete`
- **2026-03-23** `fdf71fbdd784` — 2 info
  - added the new optional request property `heartbeat_max_daily`
  - added the optional property `heartbeat_max_daily` to the response with the `200` status
- **2026-03-20** `4c528359213d` — 4 warning, 1 info
  - removed the request property `heartbeat_max_daily`
  - removed the optional property `data_sharing_consent` from the response with the `200` status
  - removed the optional property `data_sharing_consent_at` from the response with the `200` status
  - removed the optional property `heartbeat_max_daily` from the response with the `200` status
  - …1 more

[Change history](https://skmtc.dev/mozilla-ai/apis/clawbolt/changes/api/user/profile/put.md)

---

[API](https://skmtc.dev/mozilla-ai/apis/clawbolt.md) · [All operations](https://skmtc.dev/mozilla-ai/apis/clawbolt/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/mozilla-ai/clawbolt/revisions/579c110de1b8/schema)
