Skip to main content

๐Ÿ“™ 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'
  • remarks (String, required) - Initial message/description of the issue
  • fileInfo (Array, optional) - Array of file attachment objects
  • userId (ObjectId, required) - User creating the pulse
  • account_id (ObjectId, required) - Target client account ID
  • way_to_contact (String, required) - Preferred contact method
    • Values: 'email', 'phone', 'text', 'any'
  • auth (Object, required) - Authentication context
    • auth.account (Object) - Account information
      • auth.account.id (ObjectId) - Current account ID
      • auth.account.main (Boolean) - Is main account flag
      • auth.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:

  1. 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.

  2. 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).

  3. 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
    });
  4. 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 body
  • fileInfo (Array, optional) - Attachments for the follow-up
  • pulse_id (ObjectId, required) - Target pulse ID
  • dashclicks (Object, required) - User's DashClicks permissions
    • dashclicks.projects.role (String) - Project role
  • userId (ObjectId, required) - User adding the follow-up
  • auth (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:

  1. 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).

  2. 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);
    }
  3. Pulse Existence Validation

    const pulse = await ProjectsPulse.findById(pulse_id);
    if (!pulse) {
    throw custom('Pulse not found', 'PULSE_NOT_FOUND', 404);
    }
  4. 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).

  5. 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 $push to append to array without replacing existing communications.

Key Business Rules:

  • ๐Ÿ”’ Restricted Access: Only ADMIN and ACCOUNT_MANAGER roles 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 role
  • 404 Not Found: Pulse ID doesn't exist
  • 422 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:

  1. Extract Request Data

    const { type, remarks, fileInfo, account_id, way_to_contact } = req.body;
  2. Call Service Layer

    const result = await pulsesService.createPulse({
    type,
    remarks,
    fileInfo,
    account_id,
    way_to_contact,
    userId: req.auth.user?.id,
    auth: req.auth,
    });
  3. 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:

  1. Extract Parameters

    const { id } = req.params;
    const { remarks, fileInfo } = req.body;
  2. 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 authorization
  • catchAsync() (utilities/catch-async.js) - Error handling wrapper
  • custom(), 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_contact captured but not enforced by system - relies on account manager to honor


Last Updated: 2025-10-08 Service Files: services/pulses.service.js, controllers/pulses.controller.js > Primary Functions: 2 service functions, 2 controller endpoints

๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM