Broadcasts & Notifications
Send engage campaigns over email, messenger, and SMS, and deliver in-app and email notifications to team members.
For inbound mail, DKIM, and the mail worker, see Email Delivery.
Broadcasts
backend/core-api/src/modules/broadcast/:
graphql/schemas/{index,engage}.ts
graphql/resolvers/{queries,mutations,customResolvers}/
db/definitions/{engages,deliveryReports,broadcastTraces,smsRequest,log,common}.ts
db/models/{Engages,DeliveryReports,BroadcastTraces,SmsRequests,Logs,factories}.ts
worker/{broadcast,email,messenger,notification,index}.ts
trackers/{engage,sendgrid,index}.ts
routes/{index,telnyx,tracker,unsubscribe}.ts
utils/{broadcast,email,engage,outboundEmail,sender,targeting,
telnyx,transporter,worker,widget,...}.ts
trpc/{broadcast,index}.ts
An EngageMessage (engages.ts) carries kind (auto, visitorAuto, manual), method (messenger, email, notification), title, fromEmail, fromUserId, cpId, targetType/targetIds/targetCount, isDraft, isLive, channel payloads (email, messenger, notification), plus send progress (totalBatches, processedBatches, successCount, failureCount, lastUpdated) and status (sending, completed, failed).
GraphQL API
| Operation | Key arguments |
|---|---|
engageMessages | kind, status, method, brandId, fromUserId, searchValue, cursor params; returns EngageMessageListResponse |
engageMessageDetail / engageMessagesTotalCount | _id / same filters |
engageMessageCounts | name!, kind, status |
engageMembers | isVerified, cursor params |
engageReportsList | page, perPage, customerId, status, searchValue; returns EngageDeliveryReport |
engageSmsDeliveries | type!, to, page, perPage |
engageBroadcastTraces | engageMessageId! |
engageEmailPercentages, engageVerifiedEmails, engagesConfigDetail, emailSenderOptions | scope for the last |
engageMessageAdd / engageMessageEdit | title, kind, method, fromEmail, fromUserId, cpId, targetType, targetIds, targetCount, isDraft, isLive, email, messenger, notification |
engageMessageSetLive / SetPause / SetLiveManual | _id! |
engageMessageRemove / engageMessageCopy | _ids / _id! |
engageMessageVerifyEmail / engageMessageRemoveVerifiedEmail | email!, name, replyTo, scope / email!, scope |
engageMessageSendTestEmail | from!, to!, content!, title! |
engagesUpdateConfigs / broadcastUpdateConfigs | configsMap: JSON! |
engageSendMail | integrationId, conversationId, subject!, body, to: [String]!, cc, bcc, from! |
Delivery pipeline
Sending runs through worker/ batch processors (broadcast, email, messenger, notification); a broadcast is not transactional: create it, set it live, then track per-recipient state in DeliveryReports, BroadcastTraces, SmsRequests, and Logs. Open/click tracking lands via routes/tracker.ts and trackers/; Telnyx webhooks arrive on routes/telnyx.ts; unsubscribe links hit routes/unsubscribe.ts. Outbound email builds SES headers in utils/email.ts (X-SES-CONFIGURATION-SET, default config set erxes) and sends through the SES transporter in utils/transporter.ts (AWS_SES_ACCESS_KEY_ID, AWS_SES_SECRET_ACCESS_KEY).
Stuck or suppressed sends
A broadcast stuck at sending means a batch worker is down or failing: compare processedBatches to totalBatches and check the worker logs; failed batches show in failureCount and Logs. If email never leaves, check emailAddresses for suppression lanes and emailRampStatus for throttling, then release with emailAddressRelease/emailRampRelease.
Notifications
backend/core-api/src/modules/notifications/:
graphql/schema/{index,subscription}.ts
graphql/resolver/{queries,mutations,utils}.ts
graphql/customResolvers/{notification,emailAddress}.ts
db/models/{EmailSenders,EmailAddresses,EmailDeliveries,EmailRamp}.ts
trpc/{email,index}.ts
routes/{index,senderConfirm}.ts
Notification records, settings, and the outbound-email pipeline (senders, addresses, deliveries, ramp) share one module. In-app delivery uses the notificationInserted GraphQL subscription:
subscription OnNotification($id: String!) {
notificationInserted(_id: $id) { _id }
}
| Operation | Key arguments |
|---|---|
notifications | ids, status (READ/UNREAD/ALL), priority (LOW/MEDIUM/HIGH/URGENT), type (INFO/SUCCESS/WARNING/ERROR), fromDate, endDate, fromUserId, module, cursor params |
notificationDetail / unreadNotificationsCount | _id! / none |
pluginsNotifications | none; returns every plugin's declared modules/events |
notificationSettings | none; returns the per-user event/channel matrix |
emailDeliveries / emailDeliveryDetail | status, source, provider, searchValue, date range, cursor params / _id! |
emailAddresses | lane, suppressionReason, searchValue, emails, cursor params |
emailRampStatus | none |
markNotificationAsRead / markAsReadNotifications | _id! / notification filters |
archiveNotification / archiveNotifications | _id! / ids, archiveAll, filters |
updateNotificationSettingsEvent / Channel | input: { event, enabled, channels } / input: { channel, enabled, metadata } |
emailAddressRelease / emailRampRelease | email!, note! / note! |
How plugins register notifications
A plugin declares its notification modules and events in src/meta/notifications.ts:
// backend/plugins/sales_api/src/meta/notifications.ts (trimmed)
export const notifications = {
plugin: 'sales',
modules: [
{
name: 'deals',
description: 'Deals',
icon: 'IconChecklist',
events: [
{ name: 'dealAssignee', title: 'Deal assignee', description: 'Triggered when a user is assigned to a deal' },
],
},
],
};
pluginsNotifications merges these with CORE_NOTIFICATION_MODULES (modules/notifications/constants.ts) for the settings UI, where users toggle events and channels via updateNotificationSettingsEvent. To emit, a plugin calls sendNotification(subdomain, { userIds, kind, notificationType, ... }) from erxes-api-shared/core-modules; it validates the payload, creates the in-app record over tRPC (module: 'notifications', action: 'create'), then sendNotificationChannels fans out to email (sendNotificationEmail) when the user has that channel enabled. Frontend plugins render entries in their own notification widgets under frontend/libs/ui-modules / plugin NotificationsWidgets.
The email path is detailed in Email Delivery: EmailSenders (verified from-addresses, senderConfirm route), EmailAddresses (suppression lanes; release with emailAddressRelease), EmailDeliveries (per-message state), EmailRamp (sending warmup; release with emailRampRelease).
Permissions
broadcastRead (always), broadcastCreate, broadcastUpdate, broadcastDelete, broadcastConfigsManage; scope own (records in createdBy) or all. Notifications have no admin actions: users manage only their own feed and settings.
Example
mutation SendTest {
engageMessageSendTestEmail(
from: "[email protected]"
to: ["[email protected]"]
title: "Test"
content: "<p>Hello</p>"
)
}
query MyNotifications {
notifications(status: UNREAD, limit: 20) {
list { _id }
totalCount
}
}