---
title: "Update the current user profile"
method: PATCH
path: "/users/me/profile"
tags: ["user-profile"]
---

# Update the current user profile

`PATCH /users/me/profile`

## Request body

- UpdateUserProfileDto
  - `name` string — Full name
  - `username` object — Username: 3 - 100 chars; letters, digits, underscore, and dot only
  - `companyName` object — Company name (business profiles)
  - `jobTitle` object — Job title
  - `birthday` object — Birthday
  - `phone` object — Phone number
  - `timezone` object — Timezone
  - `onboardingCompleted` boolean — Whether the onboarding is completed
  - `currency` string — Display currency, lower-case ISO 4217. Shape-checked only: the valid set is whatever GET /currency/rates publishes, and a code it stops publishing falls back to USD at format time rather than 400ing here.
  - `inputMode` 'fiat' | 'token' — Amount entry mode
  - `language` object — UI language, as a **resolved translation-bundle key** — not a raw device locale. `navigator.language` returns values like `en-US` and `zh-Hans-CN`; resolve those to a language you ship before sending, because an unresolved locale is rejected rather than truncated. Two lower-case letters (`de`); canonical case is required, so `EN` is rejected. The code is not checked against a fixed set, and one that matches no bundle renders English. Send `null` to clear the choice; a client that caches the value per user must drop that cache in the same action, or its next reconcile reads NULL and republishes the old value.
  - `sessionTimeout` 0 | 5 | 15 | 30 | 60 — App-lock idle timeout in minutes; 0 disables it
  - `appLockEnabled` boolean — Accepted and ignored since DRE-441. The app lock is server-owned: a client that could set this to `false` would disable the lock with a profile PATCH, which is the bypass server-side verification exists to close. Use `POST /auth/app-lock` and `POST /auth/app-lock/remove`. Still accepted rather than rejected because `forbidNonWhitelisted` would 400 the *entire* profile update for a tab holding older JS. The field is removed in a follow-up.

## Response `200`

User profile updated

- UserProfile
  - `id` number, required — User profile ID
  - `name` string, required — Full name
  - `username` object, nullable, required — Username
  - `type` 1 | 2, required — Profile type
  - `companyName` object, nullable, required — Company name (business profiles)
  - `jobTitle` object, nullable, required — Job title
  - `birthday` object, nullable, required — Birthday
  - `phone` object, nullable, required — Phone number
  - `timezone` object, nullable, required — Timezone
  - `avatar` object, nullable, required — Avatar
  - `userId` number, required — User ID
  - `user` User, required
    - `id` number, required — User ID
    - `privyId` string, required — Privy user ID
    - `email` object, nullable, required — Email address
    - `status` 1 | 2 | 3, required — User status
    - `lastLoginCountry` object, nullable, required — Country of the last login with a known geo (ISO 3166-1 alpha-2)
    - `lastLoginCountryAt` object, nullable, required — When lastLoginCountry was last updated
    - `created_at` string, date-time, required — Creation timestamp
    - `updated_at` string, date-time, required — Last update timestamp
    - `userProfile` UserProfile, required — recursive
  - `referralCode` ReferralCode, required
    - `id` number, required — Referral code ID
    - `wallet` string, required — Wallet address
    - `code` string, required — Referral code
    - `codeNormalized` string, required — Referral code normalized
    - `createdAt` string, date-time, required — Creation timestamp
  - `onboardingCompleted` boolean, required — Whether the onboarding is completed
  - `currency` object, nullable, required — Display currency, lower-case ISO 4217
  - `inputMode` object, nullable, required — Amount entry mode: fiat or token
  - `language` object, nullable, required — UI language, as a resolved translation-bundle key. NULL means never chosen (DRE-447).
  - `sessionTimeout` object, nullable, required — App-lock idle timeout in minutes; 0 disables it
  - `appLockEnabled` boolean, required — Whether the app lock (PIN) is set up
  - `created_at` string, date-time, required — Creation timestamp
  - `updated_at` string, date-time, required — Last update timestamp

## Other responses

- `400` — Validation error
- `401` — Unauthorized
- `403` — Region blocked (`code: REGION_BLOCKED`), beta access not enabled for this wallet (no code — check `status` on `GET /auth/session`), or a missing/invalid CSRF token on a cookie session (`code: CSRF_INVALID` — re-fetch it and retry once)
- `404` — User or profile not found
- `409` — Username already exists

---

[API](https://skmtc.dev/dre/apis/dre-api.md) · [All operations](https://skmtc.dev/dre/apis/dre-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/dre/dre-api/revisions/5d14beb9482b/schema)
