Skip to main content
Was this helpful?

User Group API

People Groups and Calendar Sharing

Manage reusable people groups, invites, and group-shared calendars

User groups are people groups, not calendar sidebar groups. They let a user share one or more calendars with a stable set of members while preserving the existing calendar color, icon, visibility, and event behavior.

JWT or user API keyPrivacy-preserving invitesCalendarShare principal: groupOwner/admin controls

Source

  • User groups controller: backend-nestjs/src/user-groups/user-groups.controller.ts
  • User group calendars controller: backend-nestjs/src/calendars/user-group-calendars.controller.ts
  • User groups service: backend-nestjs/src/user-groups/user-groups.service.ts
  • Calendar share service: backend-nestjs/src/calendars/calendar-share.service.ts
  • DTOs: backend-nestjs/src/user-groups/dto/user-group.dto.ts
  • Entities: backend-nestjs/src/entities/user-group.entity.ts, backend-nestjs/src/entities/calendar.entity.ts

Authentication and Permissions

  • All endpoints require authentication.
  • Users can list groups they own or belong to.
  • Group metadata, membership, invite, and calendar-link writes require owner or admin role.
  • Deleting a group requires the group owner.
  • Calendar attachment also checks the actor's calendar rights before creating the group share.
  • Invite creation is privacy-preserving: the response shape does not reveal whether the email belongs to an existing PrimeCal user.
  • Invite accept and decline use an opaque token and authenticated user context. Invalid, expired, and already-used tokens return generic errors.

Endpoint Reference

Groups

MethodPathPurposeRequest or queryAuthSource
GET/api/user-groupsList groups owned by or shared with the current user.NoneJWT or user API keyuser-groups/user-groups.controller.ts
POST/api/user-groupsCreate a people group.Body: name,slug,description,kindJWT or user API keyuser-groups/user-groups.controller.ts
GET/api/user-groups/:idGet one group with members and shared calendar details where visible.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 people group.Path: idJWT or user API keyuser-groups/user-groups.controller.ts

Members

MethodPathPurposeRequest or queryAuthSource
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 directly by user ID.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 from the group.Path: id,userIdJWT or user API keyuser-groups/user-groups.controller.ts

Invites

MethodPathPurposeRequest or queryAuthSource
POST/api/user-groups/:id/invitesCreate an invite by email.Path: id, body: email,messageJWT or user API keyuser-groups/user-groups.controller.ts
GET/api/user-groups/:id/invitesList group invites for owner/admin review.Path: idJWT or user API keyuser-groups/user-groups.controller.ts
POST/api/user-groups/invites/:token/acceptAccept an invite by opaque token.Path: tokenJWT or user API keyuser-groups/user-groups.controller.ts
POST/api/user-groups/invites/:token/declineDecline an invite by opaque token.Path: tokenJWT or user API keyuser-groups/user-groups.controller.ts

Calendars

MethodPathPurposeRequest or queryAuthSource
POST/api/user-groups/:groupId/calendarsAttach one or more existing calendars to a people group.Path: groupId, body: calendarIds,calendarId,permissionJWT or user API keycalendars/user-group-calendars.controller.ts
POST/api/user-groups/:groupId/calendars/createCreate a calendar primarily attached to a people group.Path: groupId, body: calendar fields plus optional groupPermissionJWT or user API keycalendars/user-group-calendars.controller.ts
DELETE/api/user-groups/:groupId/calendarsDetach one or more calendars from a people group.Path: groupId, body: calendarIds or calendarIdJWT or user API keycalendars/user-group-calendars.controller.ts
DELETE/api/user-groups/:groupId/calendars/:calendarIdDetach one calendar from a people group.Path: groupId,calendarIdJWT or user API keycalendars/user-group-calendars.controller.ts

DTO Shapes and Validation

CreateUserGroupDto

  • name: required string, 2 to 200 chars.
  • slug: optional stable key, max 120 chars, pattern ^[a-z0-9][a-z0-9_-]*$.
  • description: optional string, max 500 chars.
  • kind: optional enum family|friends|team|resource|custom.

UpdateUserGroupDto

  • Same group metadata fields as create, all optional.
  • slug and description may be cleared with null where service behavior allows it.

AddUserGroupMemberDto and UpdateUserGroupMemberDto

  • userId: required integer greater than 0 for direct member additions.
  • role: enum admin|member for API callers. owner is reserved for the group owner record.

CreateUserGroupInviteDto

  • email: required email string, max 320 chars, trimmed and lowercased server-side.
  • message: optional string, max 500 chars, trimmed server-side.

Invite storage includes token, expiresAt, emailSentAt, and emailSendAttempts for future outbound email support.

GroupCalendarsDto

  • calendarIds: array of unique integers, 1 to 100 items.
  • calendarId: legacy single-calendar field, accepted when calendarIds is absent.
  • permission: optional enum read|write|admin, defaults to read for attach operations.

Example Calls

Create a group

curl -X POST "$PRIMECAL_API/api/user-groups" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Family Planning",
"slug": "family-planning",
"description": "Calendars shared with the household",
"kind": "family"
}'

Invite a user by email

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 shared planning calendars."
}'

The response is intentionally generic. Do not use it to infer whether teammate@example.com has a PrimeCal account.

Accept an invite

curl -X POST "$PRIMECAL_API/api/user-groups/invites/$INVITE_TOKEN/accept" \
-H "Authorization: Bearer $TOKEN"

Attach calendars to a group

curl -X POST "$PRIMECAL_API/api/user-groups/9/calendars" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"calendarIds": [5, 7],
"permission": "write"
}'

Response and Behavior Notes

  • Group-shared calendars are represented by CalendarShare rows with principalType: "group".
  • Direct user sharing uses the same CalendarShare model with principalType: "user".
  • Effective calendar access includes owned calendars, direct user shares, and people-group shares.
  • If multiple paths grant access to the same calendar, the highest permission applies: admin > write > read.
  • Accepted group members see group-shared calendars through GET /api/calendars without calling this API directly.
  • External email invites are recorded for future email delivery and post-signup mapping, but the current API does not send outbound email.

Best Practices

  • Use people groups when several calendars should stay aligned under one member list and permission model.
  • Keep direct user shares for one-off access.
  • Build invite UIs around generic success states.
  • Refresh both /api/user-groups and /api/calendars after accepting an invite.
  • Prefer GET /api/notifications/catalog when building rule or preference editors for invite-related events.