Skip to content

Dynamic Call Window Developer Guide ​

Overview ​

The Dynamic Call Window feature allows administrators to configure per-user calling time restrictions. This extends the existing TRAI-mandated 9 AM - 9 PM IST calling window with customizable windows for specific users.


Feature Summary ​

ScenarioBehavior
User with no custom config and no off_hours_campaign featureRestricted to 9 AM - 9 PM IST (default TRAI window)
User with off_hours_campaign feature enabledAllowed to call 24/7 (legacy behavior)
User with custom call window enabledAllowed to call only within their configured window (e.g., 6 AM - 10 PM IST)

Priority order:

  1. Custom call window (if enabled) → takes precedence
  2. off_hours_campaign feature → allows 24/7
  3. Default → 9 AM - 9 PM IST restriction

All Call Types Covered ​

The off-hours validation applies to all call types:

Call TypeWhere ValidatedFunction Used
Campaign callscalling.service.ts → fillCallSlots()isCallingAllowedForCampaign()
CRM API callscrmApi.service.ts → triggerCall()isCallingAllowedForUser()
Human calls (browser-to-phone)crm.controller.ts → initiateHumanCallFromCrm()isCallingAllowedForUser()
Phone-to-phone callscrm.controller.ts → initiatePhoneToPhoneCallFromCrm()isCallingAllowedForUser()

Data Model ​

User Schema Update ​

Added callWindowConfig field to the User model:

typescript
interface ICallWindowConfig {
  enabled: boolean;        // Whether custom window is active
  startTimeIST: string;    // Start time in "HH:MM" format (24-hour, IST)
  endTimeIST: string;      // End time in "HH:MM" format (24-hour, IST)
}

// Default values
const DEFAULT_CALL_WINDOW_CONFIG: ICallWindowConfig = {
  enabled: false,
  startTimeIST: "09:00",
  endTimeIST: "21:00",
};

MongoDB Schema (models/User.ts):

javascript
callWindowConfig: {
  type: {
    enabled: { type: Boolean, default: false },
    startTimeIST: { 
      type: String, 
      default: "09:00",
      validate: {
        validator: (value) => /^([01]\d|2[0-3]):([0-5]\d)$/.test(value),
        message: "startTimeIST must be in HH:MM format (24-hour)",
      },
    },
    endTimeIST: { 
      type: String, 
      default: "21:00",
      validate: {
        validator: (value) => /^([01]\d|2[0-3]):([0-5]\d)$/.test(value),
        message: "endTimeIST must be in HH:MM format (24-hour)",
      },
    },
  },
  default: () => ({ ...DEFAULT_CALL_WINDOW_CONFIG }),
}

Overnight Window Support ​

The system supports overnight windows (e.g., 10 PM - 6 AM).

Example configurations:

ConfigMeaning
startTimeIST: "06:00", endTimeIST: "21:00"6 AM to 9 PM (same day)
startTimeIST: "22:00", endTimeIST: "06:00"10 PM to 6 AM (overnight)
startTimeIST: "00:00", endTimeIST: "23:59"Effectively 24 hours

Logic (in isTimeInWindow()):

typescript
// If start > end, it's an overnight window
if (startMinutes > endMinutes) {
  // Allowed if: current >= start OR current < end
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
} else {
  // Normal daytime window: current >= start AND current < end
  return currentMinutes >= startMinutes && currentMinutes < endMinutes;
}

API Reference ​

Update Customer Call Window ​

Endpoint: PUT /api/admin/customers/:customerId

Auth: Admin JWT

Body:

json
{
  "callWindowConfig": {
    "enabled": true,
    "startTimeIST": "06:00",
    "endTimeIST": "22:00"
  }
}

Validation Rules:

  • enabled must be a boolean
  • When enabled: true, both startTimeIST and endTimeIST are required
  • Times must be in HH:MM format (24-hour)
  • Start and end times cannot be the same
  • To reset to default, send "callWindowConfig": null

Response (200):

json
{
  "success": true,
  "message": "Customer updated successfully",
  "data": { /* customer object with updated callWindowConfig */ }
}

Error Responses:

CodeWhen
400Invalid time format or missing required fields
404Customer not found
403Customer belongs to different org

Service Functions ​

isCallingAllowedForUser(userUID, orgUID?) ​

Used for CRM API calls, human calls, and phone-to-phone calls.

Location: services/shared.service.ts

Parameters:

  • userUID: Customer/user UID
  • orgUID: Optional org UID (will be looked up from user if not provided)

Returns:

typescript
{
  allowed: boolean;
  reason: string;
  windowStatus: {
    isWithinDefaultWindow: boolean;
    isWithinCustomWindow: boolean;
    customWindowEnabled: boolean;
    customWindowStart: string | null;
    customWindowEnd: string | null;
    currentTimeIST: string;
    defaultWindowStart: string;
    defaultWindowEnd: string;
  };
}

Example usage:

typescript
const check = await isCallingAllowedForUser(customerUID, orgUID);
if (!check.allowed) {
  return res.status(403).json({
    success: false,
    code: "OUTSIDE_CALLING_WINDOW",
    message: check.reason,
  });
}

isCallingAllowedForCampaign(campaignUID) ​

Used for campaign calls (via fillCallSlots()).

Location: services/shared.service.ts

Returns: Promise<boolean>

Check order:

  1. Within standard 9 AM - 9 PM IST? → allowed
  2. User has custom window enabled and within window? → allowed
  3. User has off_hours_campaign feature? → allowed
  4. Otherwise → blocked

getCallingWindowStatusForUser(callWindowConfig?) ​

Returns detailed window status for UI display.

Location: services/shared.service.ts

Returns:

typescript
{
  isWithinDefaultWindow: boolean;
  isWithinCustomWindow: boolean;
  customWindowEnabled: boolean;
  customWindowStart: string | null;
  customWindowEnd: string | null;
  currentTimeIST: string;        // e.g., "14:35"
  defaultWindowStart: string;    // "09:00"
  defaultWindowEnd: string;      // "21:00"
}

shouldAutoStopCampaign(orgUID, userUID) ​

Determines if a campaign should be auto-stopped at 9 PM IST.

Location: services/shared.service.ts

Returns: Promise<boolean>

  • true → auto-stop the campaign
  • false → keep running (user has extended window or off-hours feature)

Used by: scheduler.service.ts → TRAI auto-pause cron job


Frontend Integration ​

Customer Management Page ​

Location: app/src/pages/admin/CustomerManagementPage.tsx

Changes:

  1. Added "Call Window" column to customer table
  2. Added call window configuration UI in edit dialog
  3. Validates times when enabling custom window

UI Display in Table:

User StateDisplay
Custom window enabled06:00-22:00 IST
off_hours_campaign feature24/7 (green)
Default9:00-21:00 IST (muted)

Backward Compatibility ​

Existing Users ​

No migration required. Existing users without callWindowConfig will:

  • Have undefined callWindowConfig (or default when accessed)
  • Follow existing behavior:
    • With off_hours_campaign feature → 24/7 access
    • Without → 9 AM - 9 PM IST restriction

The code safely handles missing callWindowConfig:

typescript
const userCallWindowConfig = (user as any).callWindowConfig as ICallWindowConfig | undefined;
if (userCallWindowConfig?.enabled) {
  // Custom window logic
}

Feature Flag ​

The off_hours_campaign feature in the org's plan is still required to:

  1. Enable the custom call window UI in customer management
  2. Allow the admin to configure custom windows for customers

Error Messages ​

CodeMessageWhen
OUTSIDE_CALLING_WINDOW"Outside calling window (9AM-9PM IST) and no off-hours access configured"Default restriction
OUTSIDE_CALLING_WINDOW"Outside custom calling window (HH:MM-HH:MM IST)"Custom window violation
INVALID_CALL_WINDOW_CONFIG"callWindowConfig.enabled must be a boolean"Invalid enabled field
INVALID_CALL_WINDOW_CONFIG"When enabling custom call window, both startTimeIST and endTimeIST are required"Missing times
INVALID_CALL_WINDOW_CONFIG"startTimeIST must be in HH:MM format (24-hour, e.g., '06:00')"Invalid format
INVALID_CALL_WINDOW_CONFIG"startTimeIST and endTimeIST cannot be the same"Same times

Files Changed ​

Backend ​

FileChanges
types/index.tsAdded ICallWindowConfig, DEFAULT_CALL_WINDOW_CONFIG
models/User.tsAdded callWindowConfig field with validation
services/shared.service.tsAdded getISTTime(), parseTimeToMinutes(), isTimeInWindow(), isWithinCustomCallWindow(), getCallingWindowStatusForUser(), isCallingAllowedForUser(); Updated isCallingAllowedForCampaign(), shouldAutoStopCampaign()
services/crmApi.service.tsAdded off-hours check in triggerCall()
controllers/admin.controller.tsAdded callWindowConfig validation in updateCustomer()
controllers/crm.controller.tsAdded off-hours checks in initiateHumanCallFromCrm(), initiatePhoneToPhoneCallFromCrm()

Frontend ​

FileChanges
types/auth.tsAdded CallWindowConfig interface, DEFAULT_CALL_WINDOW_CONFIG
pages/admin/CustomerManagementPage.tsxAdded call window column and edit UI

Testing ​

Manual Testing Checklist ​

  1. Default restriction test:

    • Create user without off_hours_campaign feature
    • Try API call outside 9 AM - 9 PM IST
    • Expected: 403 with "Outside calling window" message
  2. Legacy feature test:

    • Enable off_hours_campaign feature for user
    • Try API call at any time
    • Expected: Call proceeds
  3. Custom window test:

    • Set callWindowConfig: { enabled: true, startTimeIST: "06:00", endTimeIST: "22:00" }
    • Try API call at 7 AM IST
    • Expected: Call proceeds
  4. Outside custom window test:

    • Same config as above
    • Try API call at 5 AM IST
    • Expected: 403 with "Outside custom calling window (06:00-22:00 IST)"
  5. Overnight window test:

    • Set callWindowConfig: { enabled: true, startTimeIST: "22:00", endTimeIST: "06:00" }
    • Try API call at 11 PM IST
    • Expected: Call proceeds
    • Try API call at 10 AM IST
    • Expected: 403 blocked
  6. Campaign auto-stop with custom window:

    • User with custom window ending at 22:00
    • Start campaign at 8 PM
    • At 9 PM: campaign should NOT auto-stop (custom window extends to 10 PM)
    • At 10 PM: campaign should auto-stop

Mermaid Diagram Call Permission Flow ​

Last updated: