๐ Pulses
๐ Overviewโ
The Pulses service provides a high-priority communication channel for urgent client needs and status updates. Unlike regular tasks, pulses are designed for immediate attention items such as new subscription inquiries, cancellation requests, or critical account issues that require direct account manager intervention.
Source Files:
- Service:
internal/api/v1/projects/services/pulses.service.js - Controller:
internal/api/v1/projects/controllers/pulses.controller.js
Key Capabilities:
- Create high-priority pulse notifications for urgent matters
- Support multiple pulse types (subscription inquiries, cancellations, issues)
- Enable threaded communication via Communications system
- Restrict updates to authorized account managers and admins
- Track preferred contact methods for follow-up
๐๏ธ Collections Usedโ
๐ Full Schema: See Database Collections Documentation
projects.pulseโ
- Operations: Create, Read, Update
- Model:
shared/models/projects-pulse.js - Usage Context: Primary storage for pulse notifications and metadata
Key Fields:
{
_id: ObjectId,
parent_account: ObjectId, // Agency account
account_id: ObjectId, // Client account
type: String, // Pulse category
priority: String, // 'high' by default
metadata: {
pulse_type: 'account',
way_to_contact: String, // Preferred contact method
remarks: {
body: String, // Initial message
attachments: Array, // File attachments
sent_by: ObjectId, // User who created pulse
createdAt: Date
},
communications: Array // Follow-up message IDs
},
createdAt: Date,
updatedAt: Date
}
communicationsโ
- Operations: Create (follow-up messages)
- Model:
shared/models/communication.js - Usage Context: Stores threaded replies to pulses from account managers
_accountsโ
- Operations: Read (parent account lookup)
- Usage Context: Determines parent account from user's authentication context
๐ Data Flowโ
Pulse Creation Flowโ
flowchart TD
A[๐ฏ Client Submits Urgent Request] --> B{Determine Account Type}
B -->|Main Account| C[parent_account = account_id]
B -->|Sub-Account| D[parent_account = account.parent_account]
C --> E[Create Pulse Document]
D --> E
E --> F[Store Initial Remarks]
F --> G[Set Priority: high]
G --> H[Set Way to Contact]
H --> I[Pulse Created โ]
I --> J[Notify Account Manager]
style A fill:#e1f5ff
style I fill:#e1ffe1
style J fill:#fff3cd
Pulse Update Flow (Follow-up)โ
sequenceDiagram
participant AM as Account Manager
participant API as Pulses API
participant DB as MongoDB
participant Comms as Communications
AM->>API: POST /pulses/:id (reply)
API->>API: Check Role (Admin/Manager only)
API->>Comms: Create communication doc
Comms-->>API: Return communication_id
API->>DB: Push communication_id to pulse
DB-->>API: Pulse updated
API-->>AM: Success response
๐ง Business Logic & Functionsโ
Service Layerโ
createPulse(options)โ
Purpose: Creates a new high-priority pulse notification for urgent client needs. Determines the correct parent account hierarchy and stores initial remarks with attachments.
Source: services/pulses.service.js
Parameters:
type(String, required) - Pulse category/type- Common values:
'new_subscription_inquiry','cancellation_request','urgent_issue'
- Common values:
remarks(String, required) - Initial message/description of the issuefileInfo(Array, optional) - Array of file attachment objectsuserId(ObjectId, required) - User creating the pulseaccount_id(ObjectId, required) - Target client account IDway_to_contact(String, required) - Preferred contact method- Values:
'email','phone','text','any'
- Values:
auth(Object, required) - Authentication contextauth.account(Object) - Account informationauth.account.id(ObjectId) - Current account IDauth.account.main(Boolean) - Is main account flagauth.account.parent_account(ObjectId) - Parent account ID
Returns: Promise<Object> (Created pulse document)
{
_id: ObjectId,
parent_account: ObjectId,
account_id: ObjectId,
type: String,
priority: 'high',
metadata: {
pulse_type: 'account',
way_to_contact: String,
remarks: {
body: String,
attachments: Array,
sent_by: ObjectId,
createdAt: Date
},
communications: []
},
createdAt: Date,
updatedAt: Date
}
Business Logic Flow:
-
Parent Account Determination
Determines the agency parent account based on account type:
let parent_account;
if (auth.account.main) {
parent_account = auth.account.id; // Main account is its own parent
} else {
parent_account = auth.account.parent_account; // Sub-account has parent
}This ensures pulses are routed to the correct agency for follow-up.
-
Parent Account Validation
if (!parent_account) {
throw custom('Parent account not found', 'PARENT_ACCOUNT_MISSING', 422);
}Fails if parent account cannot be determined (data integrity issue).
-
Pulse Document Creation
Creates pulse with structured metadata:
const Pulse = await ProjectsPulse.create({
parent_account,
account_id,
metadata: {
communications: [], // Empty initially, populated on replies
way_to_contact: way_to_contact,
pulse_type: 'account',
remarks: {
body: remarks,
attachments: fileInfo,
sent_by: userId,
createdAt: moment().toDate(),
},
},
type,
priority: 'high', // Always high priority
}); -
Return Created Document
Returns the full pulse document for confirmation.
Key Business Rules:
- โ
Always High Priority: All pulses are marked as
priority: 'high'by default - โ Main Account Logic: Main accounts are their own parent for pulse routing
- โ Timestamp Capture: Initial remarks include creation timestamp
- โ Communication Array: Initialized empty for future follow-ups
Error Handling:
422 Unprocessable Entity: Parent account cannot be determined- Propagates database errors via
Promise.reject(error)
Example Usage:
const pulse = await createPulse({
type: 'new_subscription_inquiry',
remarks: 'Client wants to add Google Ads Plus package',
fileInfo: [],
userId: clientUserId,
account_id: clientAccountId,
way_to_contact: 'email',
auth: req.auth,
});
console.log(pulse.priority); // 'high'
console.log(pulse.metadata.communications.length); // 0 (no replies yet)
Side Effects:
- โ๏ธ Writes: Creates pulse document in
projects.pulse - ๐ Notifications: May trigger account manager notifications (external to this function)
updatePulse(options)โ
Purpose: Adds a follow-up communication to an existing pulse. Restricted to account managers and admins only to ensure responses come from authorized personnel.
Source: services/pulses.service.js
Parameters:
remarks(String, required) - Follow-up message bodyfileInfo(Array, optional) - Attachments for the follow-uppulse_id(ObjectId, required) - Target pulse IDdashclicks(Object, required) - User's DashClicks permissionsdashclicks.projects.role(String) - Project role
userId(ObjectId, required) - User adding the follow-upauth(Object, required) - Authentication context
Returns: Promise<Object> (Updated pulse document)
{
_id: ObjectId,
// ... other fields
metadata: {
communications: [ObjectId, ObjectId, ...], // New communication ID added
// ... other metadata
}
}
Business Logic Flow:
-
Role Authorization Check
Critical security check - only admins and account managers can respond:
if (![PROJECT_ROLES.ADMIN, PROJECT_ROLES.ACCOUNT_MANAGER].includes(dashclicks?.projects?.role)) {
throw forbidden('You are not allowed to access this resource');
}This ensures clients cannot reply to their own pulses (maintains proper communication flow).
-
Parent Account Determination
Same logic as
createPulse:let parent_account;
if (auth.account.main) {
parent_account = auth.account.id;
} else {
parent_account = auth.account.parent_account;
}
if (!parent_account) {
throw custom('Parent account not found', 'PARENT_ACCOUNT_MISSING', 422);
} -
Pulse Existence Validation
const pulse = await ProjectsPulse.findById(pulse_id);
if (!pulse) {
throw custom('Pulse not found', 'PULSE_NOT_FOUND', 404);
} -
Communication Document Creation
Creates a communication record for the follow-up:
const { _id: communicationId } = await Communications.create({
origin: 'projects',
type: 'INCOMING', // From account manager to client
body: remarks,
task_id: pulse_id, // Links to pulse
sent_by: userId,
use_credit: false,
account_id: parent_account,
module: 'pulses',
message_type: 'text',
attachments: fileInfo,
});Note:
type: 'INCOMING'indicates direction (account manager โ client). -
Update Pulse with Communication ID
Adds the new communication to the pulse's communication array:
const updatedPulse = await ProjectsPulse.findByIdAndUpdate(
pulse_id,
{ $push: { 'metadata.communications': communicationId } },
{ new: true }, // Return updated document
);Uses
$pushto append to array without replacing existing communications.
Key Business Rules:
- ๐ Restricted Access: Only
ADMINandACCOUNT_MANAGERroles can reply - ๐จ Direction: All replies are
INCOMING(account manager โ client) - ๐ Communication Linking: Communication IDs stored in pulse metadata for retrieval
- โ
Preservation: Existing communications preserved via
$push
Error Handling:
403 Forbidden: User doesn't have required role404 Not Found: Pulse ID doesn't exist422 Unprocessable Entity: Parent account missing- Propagates database errors
Example Usage:
const updatedPulse = await updatePulse({
remarks: "I've scheduled a call for tomorrow at 2 PM to discuss the Google Ads package.",
fileInfo: [
{
url: 'https://...',
name: 'google_ads_proposal.pdf',
},
],
pulse_id: pulseId,
dashclicks: {
projects: {
role: 'account_manager',
},
},
userId: accountManagerId,
auth: req.auth,
});
console.log(updatedPulse.metadata.communications.length); // Increased by 1
Side Effects:
- โ๏ธ Writes: Creates communication document, updates pulse document
- ๐ง Notifications: May trigger client email notification (external)
- ๐ Real-time: May emit socket event (external)
Controller Layerโ
createPulse(req, res)โ
Purpose: HTTP endpoint handler for pulse creation. Formats request, calls service, and provides user-friendly success messages based on pulse type.
Source: controllers/pulses.controller.js
Route: POST /api/v1/projects/pulses
Request:
- Body:
{
type: String, // Pulse type
remarks: String, // Initial message
fileInfo: Array, // Attachments
account_id: ObjectId, // Client account
way_to_contact: String // Contact preference
}
Response:
- Success (200):
{
success: true,
message: String, // Dynamic based on pulse type
result: Object // Created pulse document
}
Logic:
-
Extract Request Data
const { type, remarks, fileInfo, account_id, way_to_contact } = req.body; -
Call Service Layer
const result = await pulsesService.createPulse({
type,
remarks,
fileInfo,
account_id,
way_to_contact,
userId: req.auth.user?.id,
auth: req.auth,
}); -
Dynamic Success Message
Generates user-friendly message based on pulse type:
let message = `Pulse for ${type
.replace(/_/g, ' ')
.replace(/\b\w/g, char => char.toUpperCase())} created successfully`;
if (type == 'new_subscription_inquiry') {
message = `Subscription inquiry has been submitted successfully to your account manager`;
}Special handling for subscription inquiries provides clearer user feedback.
Example Request:
POST /api/v1/projects/pulses
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"type": "new_subscription_inquiry",
"remarks": "Interested in adding SEO Plus package for my dental practice",
"account_id": "507f1f77bcf86cd799439011",
"way_to_contact": "email",
"fileInfo": []
}
Example Response:
{
"success": true,
"message": "Subscription inquiry has been submitted successfully to your account manager",
"result": {
"_id": "507f1f77bcf86cd799439012",
"parent_account": "507f1f77bcf86cd799439010",
"account_id": "507f1f77bcf86cd799439011",
"type": "new_subscription_inquiry",
"priority": "high",
"metadata": {
"way_to_contact": "email",
"pulse_type": "account",
"remarks": {
"body": "Interested in adding SEO Plus package...",
"sent_by": "507f1f77bcf86cd799439013",
"createdAt": "2025-10-08T10:30:00Z"
},
"communications": []
}
}
}
updatePulse(req, res)โ
Purpose: HTTP endpoint handler for adding follow-up communications to pulses. Validates authorization and calls service layer.
Source: controllers/pulses.controller.js
Route: POST /api/v1/projects/pulses/:id
Request:
- URL Parameters:
id(ObjectId) - Pulse ID
- Body:
{
remarks: String, // Follow-up message
fileInfo: Array // Optional attachments
}
Response:
- Success (200):
{
success: true,
message: 'Pulse updated successfully',
result: Object // Updated pulse document
} - Error (403): Forbidden - User lacks required role
- Error (404): Not Found - Pulse doesn't exist
Logic:
-
Extract Parameters
const { id } = req.params;
const { remarks, fileInfo } = req.body; -
Call Service Layer
const result = await pulsesService.updatePulse({
remarks,
fileInfo,
pulse_id: id,
dashclicks: req.auth.user.dashclicks,
userId: req.auth.user?.id,
auth: req.auth,
});Service layer handles role validation.
Example Request:
POST /api/v1/projects/pulses/507f1f77bcf86cd799439012
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"remarks": "I've reviewed your inquiry. Let's schedule a call to discuss.",
"fileInfo": []
}
Example Response:
{
"success": true,
"message": "Pulse updated successfully",
"result": {
"_id": "507f1f77bcf86cd799439012",
"metadata": {
"communications": ["507f1f77bcf86cd799439014"]
}
}
}
๐ Integration Pointsโ
Internal Dependenciesโ
PROJECT_ROLES(utilities/admin-filters.js) - Role constants for authorizationcatchAsync()(utilities/catch-async.js) - Error handling wrappercustom(),forbidden(),notFound()(utilities/catch-errors.js) - Error constructors- Communications Module - Threaded message storage
- Accounts Module - Parent account hierarchy
External Servicesโ
- Email Queue - Account manager notifications (external to pulses module)
- Socket.IO - Real-time pulse notifications (external)
๐งช Edge Cases & Special Handlingโ
Case: Main Account Creating Pulseโ
Condition: User belongs to main account (not a sub-account)
Handling:
if (auth.account.main) {
parent_account = auth.account.id;
}
Main account is its own parent for pulse routing purposes.
Case: Missing Parent Accountโ
Condition: Account hierarchy is broken or missing
Handling:
if (!parent_account) {
throw custom('Parent account not found', 'PARENT_ACCOUNT_MISSING', 422);
}
Returns 422 status with custom error code for client handling.
Case: Non-Manager Attempting Replyโ
Condition: Regular user tries to call updatePulse
Handling:
if (![PROJECT_ROLES.ADMIN, PROJECT_ROLES.ACCOUNT_MANAGER].includes(role)) {
throw forbidden('You are not allowed to access this resource');
}
Returns 403 Forbidden - ensures only authorized personnel respond.
Case: Reply to Non-Existent Pulseโ
Condition: Pulse ID doesn't exist in database
Handling:
const pulse = await ProjectsPulse.findById(pulse_id);
if (!pulse) {
throw custom('Pulse not found', 'PULSE_NOT_FOUND', 404);
}
Returns 404 with custom error code.
โ ๏ธ Important Notesโ
- ๐ Role Restrictions: Only admins and account managers can reply to pulses - enforces proper support workflow
- โก High Priority Default: All pulses are created with
priority: 'high'- no low-priority option - ๐จ One-Way Initial: Clients create pulses, managers respond - maintains clear communication hierarchy
- ๐ Communication Linking: Follow-ups stored separately in Communications collection, linked via IDs
- ๐ Module Identifier: Communications use
module: 'pulses'for filtering and categorization - ๐ก Contact Preference:
way_to_contactcaptured but not enforced by system - relies on account manager to honor
๐ Related Documentationโ
- Task Management - Similar communication system for regular tasks
- Activity - Activity logging for pulse events
- Projects Module Overview - Parent module architecture
Last Updated: 2025-10-08 Service Files:
services/pulses.service.js,controllers/pulses.controller.js> Primary Functions: 2 service functions, 2 controller endpoints