Skip to main content
Was this helpful?

User API

User, Profile, and Permission Surface

Manage profile data, language, visibility preferences, and permission bootstrap

These routes back the signed-in user settings area and the helper APIs the frontend uses to hydrate the current session. They do not include admin-only user management.

JWT or user API keyMultipart uploadProfile preferencesPermission bootstrap

Source

  • Profile controller: backend-nestjs/src/controllers/user-profile.controller.ts
  • Language controller: backend-nestjs/src/controllers/user-language.controller.ts
  • Permissions controller: backend-nestjs/src/controllers/user-permissions.controller.ts
  • Users controller: backend-nestjs/src/users/users.controller.ts
  • User groups controller: backend-nestjs/src/user-groups/user-groups.controller.ts
  • DTOs: backend-nestjs/src/dto/user-profile.dto.ts, backend-nestjs/src/users/dto/list-users.query.dto.ts, backend-nestjs/src/user-groups/dto/user-group.dto.ts

Authentication and Permissions

  • All routes on this page require authentication.
  • Routes using JwtAuthGuard accept bearer JWT and, where supported, user API keys.
  • POST /api/user/profile-picture is marked with @AllowIncompleteOnboarding(), so it can be used before onboarding is complete.
  • Profile writes are scoped to the authenticated user only.
  • User-group membership writes require group owner or admin role. Deleting a group requires the owner.
  • Group invite creation returns a generic response and does not reveal whether the invited email maps to an existing PrimeCal user.

Endpoint Reference

Profile and Settings

MethodPathPurposeRequest or queryAuthSource
GET/api/user/profileRead the current user profile and settings.NoneJWT or user API keycontrollers/user-profile.controller.ts
POST/api/user/profile-pictureUpload and set a profile picture.Multipart field: fileJWT or user API keycontrollers/user-profile.controller.ts
PATCH/api/user/profileUpdate profile fields and UI preferences.Body: profile fieldsJWT or user API keycontrollers/user-profile.controller.ts
DELETE/api/user/event-labels/:labelRemove one saved event label and strip it from the user's events.Path: labelJWT or user API keycontrollers/user-profile.controller.ts
PATCH/api/user/themeUpdate theme color only.Body: themeColorJWT or user API keycontrollers/user-profile.controller.ts
PATCH/api/user/passwordChange the current user's password.Body: currentPassword,newPasswordJWT or user API keycontrollers/user-profile.controller.ts
PATCH/api/users/me/languageUpdate the preferred UI language.Body: preferredLanguageJWT or user API keycontrollers/user-language.controller.ts

Session Bootstrap and Sharing Helpers

MethodPathPurposeRequest or queryAuthSource
GET/api/users/meRead the current user entity from the users service.NoneJWT or user API keyusers/users.controller.ts
GET/api/usersSearch users for sharing flows.Query: searchJWT or user API keyusers/users.controller.ts
GET/api/user-permissionsGet the current permission snapshot.NoneJWT or user API keycontrollers/user-permissions.controller.ts
GET/api/user-permissions/accessible-organizationsList organizations accessible to the current user.NoneJWT or user API keycontrollers/user-permissions.controller.ts
GET/api/user-permissions/accessible-reservation-calendarsList reservation calendars accessible to the current user.NoneJWT or user API keycontrollers/user-permissions.controller.ts

People Groups

MethodPathPurposeRequest or queryAuthSource
POST/api/user-groupsCreate a people group for sharing.Body: name,slug,description,kindJWT or user API keyuser-groups/user-groups.controller.ts
GET/api/user-groupsList groups the current user owns or belongs to.NoneJWT or user API keyuser-groups/user-groups.controller.ts
GET/api/user-groups/:idGet group details and members.Path: idJWT or user API keyuser-groups/user-groups.controller.ts
PATCH/api/user-groups/:idUpdate group metadata.Path: id, body: partial group fieldsJWT or user API keyuser-groups/user-groups.controller.ts
DELETE/api/user-groups/:idDelete a group.Path: idJWT or user API keyuser-groups/user-groups.controller.ts
GET/api/user-groups/:id/membersList group members.Path: idJWT or user API keyuser-groups/user-groups.controller.ts
POST/api/user-groups/:id/membersAdd or update a member.Path: id, body: userId,roleJWT or user API keyuser-groups/user-groups.controller.ts
PATCH/api/user-groups/:id/members/:userIdChange a member role.Path: id,userId, body: roleJWT or user API keyuser-groups/user-groups.controller.ts
DELETE/api/user-groups/:id/members/:userIdRemove a member.Path: id,userIdJWT or user API keyuser-groups/user-groups.controller.ts
POST/api/user-groups/:id/invitesCreate an email invite record.Path: id, body: email,messageJWT or user API keyuser-groups/user-groups.controller.ts
GET/api/user-groups/:id/invitesList group invites.Path: idJWT or user API keyuser-groups/user-groups.controller.ts
POST/api/user-groups/invites/:token/acceptAccept an invite by secure token.Path: tokenJWT or user API keyuser-groups/user-groups.controller.ts
POST/api/user-groups/invites/:token/declineDecline an invite by secure token.Path: tokenJWT or user API keyuser-groups/user-groups.controller.ts

Request Shapes

Update profile

UpdateProfileDto in backend-nestjs/src/dto/user-profile.dto.ts

  • username: optional, minimum 3 chars
  • email: optional, valid email
  • firstName: optional string
  • lastName: optional string
  • profilePictureUrl: optional URL, max 2048 chars
  • weekStartDay: optional integer 0..6
  • defaultCalendarView: optional month|week
  • timezone: optional string
  • timeFormat: optional 12h|24h
  • language: optional enum en|hu|de|fr
  • preferredLanguage: optional enum en|hu|de|fr
  • hideReservationsTab: optional boolean
  • hiddenResourceIds: optional number array
  • visibleCalendarIds: optional number array
  • visibleResourceTypeIds: optional number array
  • hiddenFromLiveFocusTags: optional string array, max 64 chars each
  • eventLabels: optional string array, max 64 chars each
  • calendarLayoutMode: optional 'boxed' | 'full'
  • calendarSidebarCollapsed: optional boolean
  • calendarSidebarWidth: optional integer, clamped server-side to 200..420
  • defaultTasksCalendarId: optional number or null

Implementation behavior from the controller:

  • Username and email uniqueness are rechecked only if those fields actually changed.
  • hiddenFromLiveFocusTags and eventLabels are normalized, deduplicated, trimmed, and capped to 100 items.
  • defaultTasksCalendarId can be cleared with null.
  • Changing defaultTasksCalendarId can trigger task-to-calendar resyncs for tasks with due dates.

Profile picture upload

Rules enforced in backend-nestjs/src/controllers/user-profile.controller.ts

  • field name: file
  • allowed MIME types: image/jpeg, image/png, image/gif, image/webp
  • max file size: 2MB

Theme and password

  • UpdateThemeDto.themeColor: optional hex string #rgb or #rrggbb
  • ChangePasswordDto.currentPassword: required string
  • ChangePasswordDto.newPassword: required, minimum 6 chars

Language

  • UpdateLanguagePreferenceDto.preferredLanguage: required enum en|hu|de|fr
  • ListUsersQueryDto.search: optional safe text, max 80 chars

People groups and invites

CreateUserGroupDto, AddUserGroupMemberDto, and CreateUserGroupInviteDto in backend-nestjs/src/user-groups/dto/user-group.dto.ts

  • name: required on create, 2 to 200 chars
  • slug: optional stable key, max 120 chars
  • description: optional string, max 500 chars
  • kind: optional enum family|friends|team|resource|custom
  • role: member role enum owner|admin|member; API callers can assign admin or member
  • email: required valid email for invites, normalized server-side
  • message: optional invite message, max 500 chars

Invite storage includes token, expiresAt, emailSentAt, and emailSendAttempts so outbound SMTP can be added later without changing the API contract.

Example Calls

Update profile preferences

curl -X PATCH "$PRIMECAL_API/api/user/profile" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timezone": "Europe/Budapest",
"timeFormat": "24h",
"weekStartDay": 1,
"visibleCalendarIds": [2, 3, 5],
"hiddenFromLiveFocusTags": ["no_focus", "private"],
"defaultTasksCalendarId": 7
}'

Upload a profile picture

curl -X POST "$PRIMECAL_API/api/user/profile-picture" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@C:/tmp/avatar.webp"

Search users for sharing

curl "$PRIMECAL_API/api/users?search=justin" \
-H "Authorization: Bearer $TOKEN"

Create a people group invite

curl -X POST "$PRIMECAL_API/api/user-groups/9/invites" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "teammate@example.com",
"message": "Join the planning calendars."
}'

Bootstrap permission-aware UI

curl "$PRIMECAL_API/api/user-permissions" \
-H "Authorization: Bearer $TOKEN"

Response Notes

  • GET /api/user/profile returns the richest user-settings payload, including visibility preferences, live-focus hidden tags, event labels, onboarding state, and privacy policy acceptance info.
  • GET /api/users/me is a lighter current-user lookup from the users service.
  • PATCH /api/user/password returns a simple success message after validating the current password.
  • DELETE /api/user/event-labels/:label returns the removed label, remaining labels, and the number of events affected.
  • POST /api/user-groups/:id/invites returns generic invite metadata only. Clients should not branch UI behavior based on whether the email already belongs to a user.

Workspace layout example

GET /api/user/profile

{
"id": 42,
"username": "demo_user",
"calendarLayoutMode": "full",
"calendarSidebarCollapsed": false,
"calendarSidebarWidth": 320,
"timeFormat": "24h",
"timezone": "Europe/Budapest"
}

PATCH /api/user/profile

{
"calendarLayoutMode": "boxed",
"calendarSidebarCollapsed": true,
"calendarSidebarWidth": 240
}

The controller validates the incoming DTO, ignores missing fields, and clamps the sidebar width server-side before saving.

Best Practices

  • Use GET /api/user/profile as the primary settings bootstrap route.
  • Use GET /api/user-permissions before rendering reservations, organization settings, or role-sensitive UI.
  • Send only changed fields in PATCH /api/user/profile; the controller intentionally performs narrow uniqueness checks.
  • Keep eventLabels and hiddenFromLiveFocusTags normalized on the client too, so UI state matches the backend normalization rules.
  • Use Personal Logs API for audit history rather than overloading these settings endpoints with activity concerns.