Targeted Notifications
Author(s)
- Amarnath Garai
Last Updated Date
2026-03-23
Version History
| Version | Date | Changes | Author |
|---|---|---|---|
| 1.0 | 2026-03-18 | Initial documentation for targeted notifications | Amarnath Garai |
| 1.1 | 2026-03-23 | Notification History Response Model Modify (Sender Details Added) | Amarnath Garai |
| 1.2 | 2026-03-26 | Scheduled Notification Support added Modified Request and Response Models | Amarnath Garai |
Feature Overview
Objective:
Send notifications to selected users (technicians/customers) based on search suggestions and chosen user IDs, and keep an audit trail of all targeted notifications sent by admins.
Scope:
- Search and list target users
- Send notifications to selected users
Dependencies:
- Notification delivery (Push/Email)
- User directory (Technicians/Customers)
Design Specifications
PostgreSQL Script:
CREATE TABLE targetednotifications (
notificationid UUID PRIMARY KEY,
senderid UUID NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
imageurl TEXT NULL,
categoey VARCHAR(50),
ispush BOOLEAN NOT NULL,
isemail BOOLEAN NOT NULL,
isscheduled BOOLEAN NOT NULL DEFAULT FALSE,
scheduledat TIMESTAMPTZ NULL,
status TEXT NOT NULL,
createdat TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE targetednotificationrecipients (
id UUID PRIMARY KEY,
notificationid UUID NOT NULL REFERENCES targetednotifications(notificationid) ON DELETE CASCADE,
recipientid UUID NOT NULL,
status TEXT NOT NULL,
sentat TIMESTAMPTZ NULL,
error TEXT NULL
);
Data Models
public enum NotificationScheduledStatus{
Pending = 1,
Sent
}
public enum NotificationStatus{
Pending = 1,
Sent,
Failed
}
public enum UserType
{
Admin = 1,
Technician,
Customer
}
public record UserSuggestionsFilter
{
public string? SearchKeyword { get; init; }
public UserType? User { get; init; } // User can be only Customer or Technician , if User is not given then it will be consider as both
}
public record UserSuggestionsResponse
{
public List<TechnicianBasicInfo>? Technicians { get; init; }
public List<CustomerBasicInfo>? Customers { get; init; }
}
public record SendTargetedNotificationRequest
{
public required string Title { get; init; }
public required string Body { get; init; }
public string? ImageUrl { get; init; }
public bool SendPush { get; init; } = true; // default true
public bool SendEmail { get; init; } = true; // default true
public NotificationCategory Category { get; init; }
public bool IsScheduled { get; init; } = false;
public DateTime? ScheduledAt { get; init; } // ScheduledAt is required when isScheduled is true
public required IEnumerable<Guid> UserIds { get; init; }
}
public record TargetedNotificationHistoryFilter
{
public Guid? SenderId { get; init; }
public Guid? UserId { get; init; }
public DateTime? DateFrom { get; init; }
public DateTime? DateTo { get; init; }
public bool? IsPush { get; init; }
public bool? IsEmail { get; init; }
public bool? IsScheduled { get; init; }
public NotificationScheduledStatus? Status { get; init; }
public int PageNumber { get; init; } = 1;
public int RowsPerPage { get; init; } = 10;
}
public record TargetedNotificationHistoryItem
{
public Guid NotificationId { get; init; }
public Guid SenderId { get; init; }
public required UserBasicInfo SenderDetails { get; init; }
public string Title { get; init; } = string.Empty;
public string Body { get; init; } = string.Empty;
public string? ImageUrl { get; init; }
public bool IsPush { get; init; }
public bool IsEmail { get; init; }
public NotificationScheduledStatus Status { get; init; }
public bool IsScheduled { get; init; }
public DateTime? ScheduledAt { get; init; }
public DateTime CreatedAt { get; init; }
public required List<NotificationRecipientDetails> Users { get; init; }
}
public record NotificationRecipientDetails : UserBasicInfo
{
public DateTime? SentAt { get; init; }
public NotificationStatus NotificationStatus { get; init; }
}
public class ServerPaginatedData<T>
{
public List<T> Data { get; set; } = [];
public int TotalNumber { get; set; }
public bool HasPreviousPage { get; set; }
public bool HasNextPage { get; set; }
public int TotalPages { get; set; }
public int PageNumber { get; set; }
public int RowsPerPage { get; set; }
}
public record TechnicianBasicInfo
{
public Guid? UserId { get; set; }
public Guid TechnicianId { get; init; }
public string TechnicianCode { get; init; } = string.Empty;
public TechnicianAvailabilityStatus? AvailabilityStatus { get; init; }
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string? Email { get; init; }
public string? PhoneNumber { get; init; }
public string? ProfileImageUrl { get; set; }
}
public record CustomerBasicInfo
{
public Guid UserId { get; init; }
// Customer identification
public Guid CustomerId { get; init; }
public string CustomerCode { get; init; } = string.Empty;
// User information
public string Email { get; init; } = string.Empty;
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string PhoneNumber { get; init; } = string.Empty;
public string? ProfileImageUrl { get; set; }
public Gender? Gender { get; init; }
}
public record UserBasicInfo
{
public Guid UserId { get; init; }
public string Email { get; init; } = string.Empty;
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string PhoneNumber { get; init; } = string.Empty;
public UserType UserType { get; init; }
public Guid EntityId { get; init; }
public string? ProfileImageUrl { get; init; }
}
API Endpoints
Summary of all endpoints:
| Method | Endpoint | Purpose | Request | Response |
|---|---|---|---|---|
GET | /user-suggestions | Get Users Suggestions for sending targeted notifications | UserSuggestionsFilter | UserSuggestionsResponse |
POST | /notification/send | Send Notifications To Targeted Users | SendTargetedNotificationRequest | CommonResponse |
GET | /targeted-notifications | Get Targeted Notifications History | TargetedNotificationHistoryFilter | ServerPaginatedData<TargetedNotificationHistoryItem> |
1. Get Users Suggestions for Targeted Notifications
Endpoint: GET /user-suggestions
Auth: Required (Bearer Token)
Request (Query Parameters):
{
"searchKeyword": "john",
"user": 2
}
Note: user is a UserType enum (Admin = 1, Technician = 2, Customer = 3). For this endpoint, use only Technician or Customer. If user is omitted, both are considered.
Success Response (UserSuggestionsResponse):
{
"technicians": [
{
"userId": "9a0d3f9a-1b7f-4d5c-9a1c-8b7b3a6f1f11",
"technicianId": "0b7a2d6b-3a19-4d2c-9c3a-0b2b9c2f4a10",
"technicianCode": "TECH-1023",
"availabilityStatus": 1,
"firstName": "John",
"lastName": "Carter",
"email": "john.carter@example.com",
"phoneNumber": "+1-555-0100",
"profileImageUrl": "https://cdn.example.com/profiles/tech-1023.png"
}
],
"customers": [
{
"userId": "e3c9a5ab-2f3a-4f91-9d4d-3c8f8b2a7b21",
"customerId": "7c2a1d4b-8e61-4c3f-9d13-2a7b9d9c1f55",
"customerCode": "CUST-4501",
"email": "john.wilson@example.com",
"firstName": "John",
"lastName": "Wilson",
"phoneNumber": "+1-555-0199",
"profileImageUrl": "https://cdn.example.com/profiles/cust-4501.png",
"gender": 1
}
]
}
Error Responses:
-20009: Invalid input parameters-20012: Unauthorized
2. Send Notifications To Targeted Users
Endpoint: POST /notification/send
Auth: Required (Bearer Token)
Request (SendTargetedNotificationRequest):
{
"title": "Service Request Update",
"body": "Your service request #SR-1023 has been assigned.",
"imageUrl": "https://cdn.example.com/notifications/sr-1023.png",
"sendPush": true,
"sendEmail": true,
"isScheduled": true,
"scheduledAt": "2026-03-20T09:30:00Z",
"userIds": [
"9a0d3f9a-1b7f-4d5c-9a1c-8b7b3a6f1f11",
"e3c9a5ab-2f3a-4f91-9d4d-3c8f8b2a7b21"
]
}
Success Response (CommonResponse):
{
"status": 0,
"message": "Notification sent successfully"
}
Error Responses:
-20009: Invalid input parameters-20012: Unauthorized
3. Get Targeted Notifications History
Endpoint: GET /targeted-notifications
Auth: Required (Bearer Token)
Request (Query Parameters):
{
"senderId": "a1111111-2222-3333-4444-555555555555",
"userId": "e3c9a5ab-2f3a-4f91-9d4d-3c8f8b2a7b21",
"dateFrom": "2026-03-01T00:00:00Z",
"dateTo": "2026-03-18T23:59:59Z",
"isPush": true,
"isEmail": false,
"isScheduled": true,
"pageNumber": 1,
"rowsPerPage": 10
}
Success Response (ServerPaginatedData<TargetedNotificationHistoryItem>):
{
"data": [
{
"notificationId": "2a6b5c9d-7f2d-4d2a-9c1f-0f1b2a3c4d5e",
"senderId": "a1111111-2222-3333-4444-555555555555",
"senderDetails": {
"userId": "a1111111-2222-3333-4444-555555555555",
"email": "admin@example.com",
"firstName": "System",
"lastName": "Admin",
"phoneNumber": "+1-555-0001",
"userType": 1,
"entityId": "1a2b3c4d-5e6f-7g8h-9i0j-0k1l2m3n4o5p",
"profileImageUrl": "https://cdn.example.com/profiles/admin-001.png"
},
"title": "Service Request Update",
"body": "Your service request #SR-1023 has been assigned.",
"imageUrl": "https://cdn.example.com/notifications/sr-1023.png",
"isPush": true,
"isEmail": false,
"status": 1,
"isScheduled": true,
"scheduledAt": "2026-03-20T09:30:00Z",
"createdAt": "2026-03-12T10:15:30Z",
"users": [
{
"userId": "e3c9a5ab-2f3a-4f91-9d4d-3c8f8b2a7b21",
"email": "john.wilson@example.com",
"firstName": "John",
"lastName": "Wilson",
"phoneNumber": "+1-555-0199",
"userType": 3,
"entityId": "7c2a1d4b-8e61-4c3f-9d13-2a7b9d9c1f55",
"profileImageUrl": "https://cdn.example.com/profiles/cust-4501.png",
"sentAt": null,
"notificationStatus": 1
},
{
"userId": "9a0d3f9a-1b7f-4d5c-9a1c-8b7b3a6f1f11",
"email": "john.carter@example.com",
"firstName": "John",
"lastName": "Carter",
"phoneNumber": "+1-555-0100",
"userType": 2,
"entityId": "0b7a2d6b-3a19-4d2c-9c3a-0b2b9c2f4a10",
"profileImageUrl": "https://cdn.example.com/profiles/tech-1023.png",
"sentAt": null,
"notificationStatus": 1
}
]
},
{
"notificationId": "8f7e6d5c-4b3a-2c1d-0e9f-8a7b6c5d4e3f",
"senderId": "a1111111-2222-3333-4444-555555555555",
"senderDetails": {
"userId": "a1111111-2222-3333-4444-555555555555",
"email": "admin@example.com",
"firstName": "System",
"lastName": "Admin",
"phoneNumber": "+1-555-0001",
"userType": 1,
"entityId": "1a2b3c4d-5e6f-7g8h-9i0j-0k1l2m3n4o5p",
"profileImageUrl": "https://cdn.example.com/profiles/admin-001.png"
},
"title": "Work Completed",
"body": "Technician has completed the work for request #SR-1019.",
"imageUrl": null,
"isPush": true,
"isEmail": true,
"status": 2,
"isScheduled": false,
"scheduledAt": null,
"createdAt": "2026-03-05T08:40:00Z",
"users": [
{
"userId": "e3c9a5ab-2f3a-4f91-9d4d-3c8f8b2a7b21",
"email": "john.wilson@example.com",
"firstName": "John",
"lastName": "Wilson",
"phoneNumber": "+1-555-0199",
"userType": 3,
"entityId": "7c2a1d4b-8e61-4c3f-9d13-2a7b9d9c1f55",
"profileImageUrl": "https://cdn.example.com/profiles/cust-4501.png",
"sentAt": "2026-03-05T08:40:10Z",
"notificationStatus": 2
}
]
}
],
"totalNumber": 42,
"hasPreviousPage": false,
"hasNextPage": true,
"totalPages": 5,
"pageNumber": 1,
"rowsPerPage": 10
}
Error Responses:
-20009: Invalid input parameters-20012: Unauthorized