Skip to main content

Recipient Groups Module

Overview​

The Recipient Groups module manages custom groups of recipients for the multi-channel communication system. Businesses create groups to organize contacts (users, emails, phones, WhatsApp numbers) for targeted communications.

Architecture​

Follows hexagonal architecture:

recipient-groups/
├── domain/
│ └── recipient-groups-repository.domain.ts # Repository port (interface)
├── application/
│ └── recipient-groups.service.ts # Business logic / use cases
├── infrastructure/
│ └── recipient-groups.repository.ts # Kysely DB adapter
├── interfaces/
│ ├── recipient-groups.controller.ts # HTTP REST controller
│ └── dtos/
│ ├── create-recipient-group.dto.ts
│ ├── update-recipient-group.dto.ts
│ └── add-group-member.dto.ts
├── validators/
│ ├── email.validator.ts # Email validation decorators & utilities
│ └── phone.validator.ts # E.164 phone validation decorators & utilities
└── recipient-groups.module.ts

Domain Concepts​

Recipient Group​

A named container scoped to a business, used to group communication recipients. Has a unique name per business, optional description, and active/inactive state.

Table: communication_recipient_group

Group Member​

A member of a group. Polymorphic by memberType:

Member TypeRequired FieldDescription
business_userbusinessUserIdLinks to an existing business user account
emailemailAddressStandalone email recipient
phonephoneNumberSMS recipient (E.164 format)
whatsappwhatsappNumberWhatsApp recipient (E.164 format)

Table: communication_group_member

Members are soft-deleted (marked isActive = false with removedAt timestamp).

API Endpoints​

All endpoints require Bearer token authentication.

MethodPathDescription
POST/recipient-groupsCreate a new group
GET/recipient-groups/business/:businessIdList all active groups for a business
GET/recipient-groups/:idGet a specific group
PATCH/recipient-groups/:idUpdate a group
DELETE/recipient-groups/:idDelete a group (cascades to members)
POST/recipient-groups/:id/membersAdd a member to a group
GET/recipient-groups/:id/membersList all active members of a group
DELETE/recipient-groups/:id/members/:memberIdRemove (soft-delete) a member

Example: Create Group​

POST /recipient-groups
{
"businessId": "550e8400-e29b-41d4-a716-446655440000",
"createdBy": "550e8400-e29b-41d4-a716-446655440001",
"name": "VIP Customers",
"description": "High-value customers for special promotions",
"isActive": true
}

Example: Add Email Member​

POST /recipient-groups/:id/members
{
"businessId": "550e8400-e29b-41d4-a716-446655440000",
"createdBy": "550e8400-e29b-41d4-a716-446655440001",
"memberType": "email",
"emailAddress": "supplier@example.com",
"displayName": "Supplier Contact"
}

Example: Add Phone Member​

POST /recipient-groups/:id/members
{
"businessId": "550e8400-e29b-41d4-a716-446655440000",
"createdBy": "550e8400-e29b-41d4-a716-446655440001",
"memberType": "phone",
"phoneNumber": "+14155552671",
"displayName": "John Doe"
}

Key Business Rules​

  1. Group name uniqueness: Enforced at DB level per business (UNIQUE constraint on businessId + name).
  2. Duplicate prevention: A group cannot contain duplicate contacts. Checked via targeted DB queries per member type.
  3. Contact normalization: Emails are lowercased/trimmed. Phone numbers are normalized to E.164 format before storage.
  4. Multi-tenancy: All group queries are scoped by businessId.
  5. Soft deletes: Members are marked inactive rather than hard-deleted, preserving audit trail.

Integration Points​

  • Recipient Rules (apps/backend/src/recipient-rules/): Rules reference groups by ID for communication targeting.
  • Communications (apps/backend/src/communications/): The RecipientResolverService resolves group members when sending communications.
  • Frontend PWA (apps/frontend-pwa/): UI for managing groups and members via recipientGroupsService.ts and React hooks.

Error Responses​

StatusCondition
400Invalid DTO fields, missing required contact field for member type
401Missing or invalid authentication token
404Group or member not found
409Duplicate member (same email/phone/user already in group)