# profiles — customer and nurse profiles > Client seam `client/src/services/profiles/` · `USE_PROFILES_MOCK = false` (**real**) · 7 server ops > Last verified: 2026-07-30 against commit `d3ec723` and swagger.v1.json (2026-07-29). One client domain over two server controllers, because the client's profile screens are one feature with two actor variants. Identity and roles come from [auth.md](auth.md)'s `/me`, not from here. ## Endpoints | Method | Path | Verdict | | --- | --- | --- | | GET | `/api/v1/customer_profiles/me` | wired | | POST | `/api/v1/customer_profiles/upsert` | wired | | POST | `/api/v1/customer_profiles/avatar` | **unwired** — the client's avatar upload calls the nurse route only | | GET | `/api/v1/nurse_profiles/me` | wired | | POST | `/api/v1/nurse_profiles/upsert` | wired | | POST | `/api/v1/nurse_profiles/avatar` | wired | | POST | `/api/v1/nurse_profiles/set_accepting_bookings` | wired | All `[Authorize]`. No phantoms. > **`customer_profiles/avatar` exists and is unused.** The nurse avatar upload is wired; the customer one > is not, so a customer cannot set a photo even though the endpoint is live. A one-line client gap, not a > contract gap. ## Shape rules the JSON does not express - **Avatar upload is `multipart/form-data`.** This is the one place the client must **not** set `Content-Type` — `clientFetch` detects a `FormData` body and lets the browser write the multipart boundary. A manual JSON content-type there breaks the upload. See [../api-contract.md](../api-contract.md). - **`avatarUrl` is served by the object-storage seam.** `Seams:ObjectStorage:Provider` = `local` (default, writes under `RootPath`) or `s3`. In the deployment `RootPath` is a **named docker volume** — without it, uploads vanish on the next `up --build`. See [../topology.md](../topology.md). - **`isAcceptingBookings` is a real toggle with real consequences.** It is one of the four conditions the search index's `is_searchable` requires — flipping it off removes the nurse from discovery. See [search.md](search.md). - **`isVerified` on `NurseProfileDto` is derived, never writable.** It is written **only** by the verification finalize transaction when the aggregate reaches `approved`. Never set it from a profile write. See [verification.md](verification.md). - **`averageRating` / `totalReviews` / `totalCompletedBookings` are recomputed from source**, not incremented. A moderation change that hides a review recomputes the aggregate. See [reviews.md](reviews.md). - **`specializationsJson` is a string containing JSON**, like the other `*Json` fields on this wire. - `preferredLanguage` on `CustomerProfileDto` (REQ-007, delivered) alongside the name update. - `defaultEmergencyContactName`/`Phone` are the customer-level fallback used when a booking supplies none. ## Enums None of its own. `educationLevel` and `educationField` are free strings. Gender lives on the user (see [auth.md](auth.md)), not on the profile. ## Open REQs None. REQ-006 (avatar/object-storage upload route) and REQ-007 (name + preferred language) were both delivered in refinement-phase-3.