User Profile
Fetch and update your authenticated user profile. The server stores your display name as an encrypted blob it cannot read.
Two endpoints manage the authenticated user's profile. One fetches it; one updates the encrypted display name. Both require authentication.
For token setup, see Authentication.
Server-Visible vs. Client-Encrypted Fields
The server stores two types of data in your profile:
| Field | Visibility | Notes |
|---|---|---|
user_id | Server-visible | UUID, used as a stable foreign key |
email_encrypted | Opaque blob | AES-256-GCM ciphertext — server never sees the email address |
email_verified | Server-visible | Boolean flag, not the address itself |
key_version | Server-visible | Increments on credential rotation |
created_at | Server-visible | Account creation timestamp |
last_login_at | Server-visible | Last successful login timestamp |
display_name_encrypted | Opaque blob | AES-256-GCM ciphertext — server never sees the display name |
Zero-Knowledge Note: The server cannot read your display name or email address. It stores whatever encrypted blob you send and echoes it back verbatim. Decryption happens on your device using your personal master key (UMK).
GET /v1/users/me
Returns the authenticated user's profile. All encrypted fields are returned as opaque base64 blobs.
Request
curl https://api.kyndex.co/v1/users/me \
-H 'Authorization: Bearer <access_token>'Cookie auth is also accepted if you have an active session cookie.
Response
200 OK
{
"user_id": "d9b7c8a9-e0f1-4627-b3c4-d5e6f7a8b9c0",
"email_encrypted": "ZW1haWxlbmNyeXB0ZWR3aXRoVU1L",
"key_version": 0,
"email_verified": true,
"created_at": "2026-01-15T10:30:00.000Z",
"last_login_at": "2026-04-03T08:15:00.000Z",
"display_name_encrypted": "ZGlzcGxheW5hbWVlbmNyeXB0ZWQ="
}Response Fields
| Field | Type | Description |
|---|---|---|
user_id | string (UUID) | Stable identifier for the authenticated user. |
email_encrypted | string (base64) | Email address encrypted client-side with UMK. Decrypt locally for display. |
key_version | integer | Key rotation epoch. Increments on credential rotation events (e.g. recovery). |
email_verified | boolean | Whether the account's email address has been verified. |
created_at | string (ISO 8601) | Account creation timestamp. |
last_login_at | string (ISO 8601) | null | Timestamp of the most recent successful login. null if no login recorded. |
display_name_encrypted | string (base64) | null | AES-256-GCM ciphertext of the display name. null if not yet set. Decrypt with UMK. |
PATCH /v1/users/me
Replaces the display_name_encrypted field. This is a replace, not a partial merge — the server overwrites the stored blob with whatever you send.
You encrypt the new display name on your device using AES-256-GCM with your UMK before sending. The server stores the ciphertext as-is.
Request
curl -X PATCH https://api.kyndex.co/v1/users/me \
-H 'Authorization: Bearer <access_token>' \
-H 'Content-Type: application/json' \
-d '{
"display_name_encrypted": "<base64-AES-GCM-ciphertext>"
}'Request Fields
| Field | Required | Description |
|---|---|---|
display_name_encrypted | Yes | AES-256-GCM ciphertext of the new display name, base64-encoded. Encrypted client-side with your UMK before sending. |
Response
200 OK — returns the full updated profile in the same shape as the GET response.
{
"user_id": "d9b7c8a9-e0f1-4627-b3c4-d5e6f7a8b9c0",
"email_encrypted": "ZW1haWxlbmNyeXB0ZWR3aXRoVU1L",
"key_version": 0,
"email_verified": true,
"created_at": "2026-01-15T10:30:00.000Z",
"last_login_at": "2026-04-03T08:15:00.000Z",
"display_name_encrypted": "<your-new-ciphertext>"
}Encrypt the display name before sending. Sending plaintext will result in the server storing it verbatim — the server applies no encryption on your behalf. All encryption happens client-side.
Error Responses
All errors follow RFC 9457 Problem Details with Content-Type: application/problem+json.
| Status | Endpoint | When |
|---|---|---|
400 | PATCH | Request body failed validation — display_name_encrypted missing or not valid base64. |
401 | Both | Expired or invalid session — re-authenticate. |
404 | Both | No user record found for the authenticated user_id. Should not occur for active accounts. |
429 | Both | Rate limit exceeded. Back off and retry. |
500 | Both | Internal server error. Retry with exponential backoff. |
Related
- Authentication — how to obtain and refresh access tokens.
- Key Hierarchy — how the UMK protects your display name and email at rest.