Skip to main content

SendNotificationCommand

Category: Communication Commands
Added: v2.2 (January 2026)
Resilient: ✅ Yes (Offline support with message queuing)

Send real-time notifications to users via SignalR with automatic fallback for offline users.


Overview​

SendNotificationCommand enables instant push notifications to logged-in users through SignalR. Unlike email or SMS which have delays, SignalR notifications appear immediately in the user's browser or app.

Key Features​

  • 🚀 Instant Delivery - Sub-second delivery to online users
  • 💾 Offline Resilience - Auto-queues messages for offline users
  • đŸ‘Ĩ Multi-User Support - Send to current user, specific user, or multiple users
  • đŸŽ¯ Targeted Delivery - Send to individual users or user groups
  • đŸ“Ļ Rich Payloads - Include custom data, actions, and metadata
  • 🔔 Priority Levels - High, normal, and low priority notifications
  • âąī¸ Expiration Control - Auto-expire old notifications (default: 24 hours)

When to Use​

✅ Use SendNotificationCommand For:❌ Don't Use For:
Transaction status updatesLong-term archival messages
Approval/rejection alertsRegulatory/compliance notices
Real-time warningsMarketing campaigns
Process completion notificationsBulk announcements (use email)
User-triggered eventsLegal documents
Dashboard updatesFinancial statements

Parameters​

Required Parameters​

ParameterTypeDescription
titlestringNotification title/heading (required)
messagestringNotification body/content (required)

Target Parameters (Choose One)​

ParameterTypeDefaultDescription
targetstring"current"Who receives: "current", "user", or "users"
userIdstringnullSingle user ID (when target="user")
userIdsarray<string>nullMultiple user IDs (when target="users")

Optional Parameters​

ParameterTypeDefaultDescription
notificationTypestring"info"Type: "info", "success", "warning", "error", "notification"
prioritystring"normal"Priority: "high", "normal", "low"
actionstringnullAction when clicked (e.g., "navigate:/transactions/123")
dataobjectnullAdditional custom data payload
persistForOfflineUsersbooleantrueStore for offline users and deliver later
expirationMinutesnumber1440Minutes before notification expires (default: 1440 = 24 hours)

Target Modes​

1. Current User (target: "current")​

Send notification to the user executing the workflow (most common).

var result = doCmd('SendNotificationCommand', {
Data: {
target: 'current',
title: 'Loan Approved!',
message: 'Your loan application #' + context.loanId + ' has been approved.',
notificationType: 'success'
}
});

Use Case: User submits a form, workflow processes it, user gets notified.


2. Specific User (target: "user")​

Send notification to one specific user by their ID.

var result = doCmd('SendNotificationCommand', {
Data: {
target: 'user',
userId: context.approverId, // Specific user ID
title: 'Pending Approval',
message: 'Loan application from ' + context.customerName + ' needs your approval.',
notificationType: 'warning',
priority: 'high',
action: 'navigate:/approvals/' + context.loanId
}
});

Use Case: Workflow assigns task to specific approver.


3. Multiple Users (target: "users")​

Send same notification to multiple users simultaneously.

var approvers = ['user123', 'user456', 'user789'];

var result = doCmd('SendNotificationCommand', {
Data: {
target: 'users',
userIds: approvers,
title: 'Team Notification',
message: 'Large deposit transaction requires team review.',
notificationType: 'warning',
priority: 'high'
}
});

Use Case: Notify all members of approval group.


Notification Types & Icons​

TypeIconUse For
infoâ„šī¸General information, updates
success✅Successful operations, approvals
warningâš ī¸Warnings, pending actions
error❌Errors, rejections, failures
notification🔔Generic notifications

Resilient Delivery (Offline Support)​

When persistForOfflineUsers: true (default), notifications are automatically stored for offline users and delivered when they reconnect.

How It Works​

1. User is offline → Notification stored in database
2. User comes online → SignalR connects
3. All pending notifications delivered automatically
4. Notifications marked as "Delivered"
5. Expired notifications (>24h by default) auto-cleaned

Example with Custom Expiration​

var result = doCmd('SendNotificationCommand', {
Data: {
target: 'user',
userId: context.customerId,
title: 'Urgent: Account Locked',
message: 'Multiple failed login attempts detected.',
notificationType: 'error',
priority: 'high',
persistForOfflineUsers: true,
expirationMinutes: 2880 // Keep for 2 days (48 hours)
}
});

Action Handling​

The action parameter tells the client what to do when notification is clicked.

Supported Action Patterns​

// Navigate to specific page
action: 'navigate:/transactions/123'

// Open modal/dialog
action: 'modal:transaction-details?id=123'

// Execute client-side function
action: 'callback:refreshBalance'

// External link
action: 'url:https://banklingo.app/help'

// Custom JSON action
action: '{"type":"refresh","target":"dashboard","params":{"tab":"loans"}}'

Example with Navigation​

var result = doCmd('SendNotificationCommand', {
Data: {
target: 'current',
title: 'Transaction Completed',
message: 'Your transfer of $5,000 was successful.',
notificationType: 'success',
action: 'navigate:/transactions/' + context.transactionId,
data: {
transactionId: context.transactionId,
amount: 5000,
currency: 'USD'
}
}
});

Custom Data Payloads​

Include additional structured data for client-side processing.

var result = doCmd('SendNotificationCommand', {
Data: {
target: 'current',
title: 'Loan Disbursed',
message: 'Your loan has been disbursed to account ' + context.accountNumber,
notificationType: 'success',
data: {
loanId: context.loanId,
amount: context.loanAmount,
currency: 'NGN',
disbursementDate: new Date().toISOString(),
accountNumber: context.accountNumber,
tenor: context.tenor,
interestRate: context.interestRate
}
}
});

Frontend handling:

signalRConnection.on("ReceiveNotification", (notification) => {
console.log('Title:', notification.Title);
console.log('Message:', notification.Message);
console.log('Custom Data:', notification.Data);

// Access custom data
if (notification.Data) {
const loanId = notification.Data.loanId;
const amount = notification.Data.amount;
// ... process custom data
}
});

Priority Levels​

PriorityBehaviorUse For
highProminent display, sound/vibrationUrgent approvals, errors, security alerts
normalStandard notificationGeneral updates, info messages
lowSubtle display, no soundBackground updates, non-critical info

Complete Workflow Example​

Loan Approval Notification Workflow​

// In your BPMN workflow script

// Step 1: Notify customer of submission
doCmd('SendNotificationCommand', {
Data: {
target: 'current',
title: 'Application Received',
message: 'Your loan application is being reviewed.',
notificationType: 'info'
}
});

// Step 2: Notify approver
var approverId = context.approver_id;
doCmd('SendNotificationCommand', {
Data: {
target: 'user',
userId: approverId,
title: 'Approval Required',
message: 'Loan application #' + context.loan_id + ' needs approval.',
notificationType: 'warning',
priority: 'high',
action: 'navigate:/approvals/' + context.loan_id,
data: {
loanId: context.loan_id,
customerName: context.customer_name,
amount: context.loan_amount
}
}
});

// Step 3: After approval, notify customer
if (context.approval_status === 'Approved') {
doCmd('SendNotificationCommand', {
Data: {
target: 'user',
userId: context.customer_id,
title: 'Loan Approved! 🎉',
message: 'Congratulations! Your loan of â‚Ļ' +
formatMoney(context.loan_amount) + ' has been approved.',
notificationType: 'success',
priority: 'high',
action: 'navigate:/loans/' + context.loan_id,
persistForOfflineUsers: true,
expirationMinutes: 2880 // 48 hours
}
});
}

Best Practices​

✅ Do's​

  1. Use Descriptive Titles: Clear, concise, action-oriented

    ✅ title: 'Loan Approved'
    ❌ title: 'Notification'
  2. Include Context: Add relevant IDs and data

    data: {
    loanId: context.loanId,
    amount: context.amount
    }
  3. Set Appropriate Priority: Reserve high for urgent items

    priority: 'high'  // Only for time-sensitive actions
  4. Add Actions: Make notifications actionable

    action: 'navigate:/approvals/' + loanId
  5. Use Persistence: Enable for important notifications

    persistForOfflineUsers: true

❌ Don'ts​

  1. Don't Spam: Avoid excessive notifications

    ❌ Sending notification for every minor update
    ✅ Batch updates or use summary notifications
  2. Don't Include Sensitive Data: PINs, passwords, full account numbers

    ❌ message: 'Your PIN is 1234'
    ✅ message: 'Your PIN has been updated'
  3. Don't Use Long Messages: Keep under 200 characters

    ❌ message: 'Very long paragraph with lots of details...'
    ✅ message: 'Loan approved. View details for more info.'
  4. Don't Overuse High Priority: Reduces effectiveness

    ❌ priority: 'high' // For everything
    ✅ priority: 'normal' // For most notifications

Frontend Integration​

JavaScript SignalR Client​

// Connect to SignalR hub
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hub/notification")
.withAutomaticReconnect()
.build();

// Listen for notifications
connection.on("ReceiveNotification", (notification) => {
// Display notification
showToast({
title: notification.Title,
message: notification.Message,
type: notification.Type,
priority: notification.Priority
});

// Handle action
if (notification.Action) {
handleNotificationAction(notification.Action, notification.Data);
}

// Log for debugging
console.log('Notification received:', notification);
});

// Start connection
connection.start()
.then(() => console.log('SignalR connected'))
.catch(err => console.error('SignalR connection error:', err));

// Handle actions
function handleNotificationAction(action, data) {
if (action.startsWith('navigate:')) {
const path = action.replace('navigate:', '');
window.location.href = path;
}
else if (action.startsWith('modal:')) {
const modal = action.replace('modal:', '');
openModal(modal, data);
}
// ... handle other action types
}

Error Handling​

try {
var result = doCmd('SendNotificationCommand', {
Data: {
target: 'user',
userId: context.userId,
title: 'Important Update',
message: 'Your transaction is complete'
}
});

if (result.IsSuccessful) {
logger.info('Notification sent successfully');
} else {
logger.error('Notification failed: ' + result.Message);
}
} catch (error) {
logger.error('Error sending notification: ' + error.message);

// Fallback: Send email instead
doCmd('SendMailCommand', {
Data: {
email: [context.userEmail],
subject: 'Important Update',
message: 'Your transaction is complete'
}
});
}

Performance Considerations​

Scalability​

  • ✅ Lightweight: ~1KB per notification
  • ✅ Fast: <100ms delivery to online users
  • ✅ Efficient: Database queuing for offline users only

Limits​

MetricLimitNotes
Max title length500 charsEnforced by database
Max message lengthUnlimitedConsider UX for display
Max users per call100For target="users"
Max data payload10KBKeep payloads small
Expiration range1-720 hours1 hour to 30 days

Database Schema​

CREATE TABLE [dbo].[PendingNotification] (
[Id] BIGINT PRIMARY KEY IDENTITY,
[NotificationId] NVARCHAR(100) NOT NULL,
[UserId] NVARCHAR(100) NOT NULL,
[TenantId] NVARCHAR(100) NOT NULL,
[Title] NVARCHAR(500) NOT NULL,
[Message] NVARCHAR(MAX) NOT NULL,
[NotificationType] NVARCHAR(50),
[Priority] NVARCHAR(50),
[Action] NVARCHAR(500),
[DataJson] NVARCHAR(MAX),
[CreatedAt] DATETIME2 NOT NULL,
[ExpiresAt] DATETIME2 NOT NULL,
[Status] NVARCHAR(50) NOT NULL,
[DeliveryAttempts] INT DEFAULT 0,
[DeliveredAt] DATETIME2 NULL,
[LastAttemptAt] DATETIME2 NULL,
[ErrorMessage] NVARCHAR(MAX) NULL,
INDEX IX_PendingNotification_UserId_Status (UserId, Status),
INDEX IX_PendingNotification_ExpiresAt (ExpiresAt)
);

Comparison with Other Commands​

FeatureSendNotificationCommandSendMailCommandSendSMSCommand
Delivery SpeedInstant (<1s)Minutes (5-30s)Seconds (3-10s)
Offline Support✅ Yes (queued)✅ Yes (email queues)❌ No
CostFreeLowModerate-High
Interactive✅ Yes (actions)❌ No❌ No
Rich Content✅ Yes (HTML/JSON)✅ Yes (HTML)❌ No (plain text)
User Must BeLogged inHave emailHave phone
Best ForReal-time updatesFormal communicationsOTPs, alerts


Support & Troubleshooting​

Common Issues​

1. Notifications not received

  • Check user is logged in and SignalR connected
  • Verify userId is correct
  • Check browser console for SignalR errors

2. Offline notifications not delivered

  • Ensure persistForOfflineUsers: true
  • Check PendingNotification table for stored messages
  • Verify expiration hasn't passed

3. Action not working

  • Verify action format: "navigate:/path" or "modal:modalName"
  • Check client-side action handler implementation

Version History​

  • v2.2 (Jan 2026): Initial release with offline support
    • SignalR real-time delivery
    • Database persistence for offline users
    • Multi-user targeting
    • Rich data payloads
    • Automatic expiration cleanup

Multi-Channel Strategy

For critical notifications, use layered approach:

// 1. Try instant notification first
doCmd('SendNotificationCommand', { Data: { ... } });

// 2. Always send email backup
doCmd('SendMailCommand', { Data: { ... } });

// 3. SMS for high-priority items
if (priority === 'high') {
doCmd('SendSMSCommand', { Data: { ... } });
}

This ensures users receive the message through at least one channel.