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
  }
}

Operate on one system.

Talk to our team about consolidating your stack onto erxes. 30-minute walkthrough, custom to your org.