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

OperationKey arguments
engageMessageskind, status, method, brandId, fromUserId, searchValue, cursor params; returns EngageMessageListResponse
engageMessageDetail / engageMessagesTotalCount_id / same filters
engageMessageCountsname!, kind, status
engageMembersisVerified, cursor params
engageReportsListpage, perPage, customerId, status, searchValue; returns EngageDeliveryReport
engageSmsDeliveriestype!, to, page, perPage
engageBroadcastTracesengageMessageId!
engageEmailPercentages, engageVerifiedEmails, engagesConfigDetail, emailSenderOptionsscope for the last
engageMessageAdd / engageMessageEdittitle, kind, method, fromEmail, fromUserId, cpId, targetType, targetIds, targetCount, isDraft, isLive, email, messenger, notification
engageMessageSetLive / SetPause / SetLiveManual_id!
engageMessageRemove / engageMessageCopy_ids / _id!
engageMessageVerifyEmail / engageMessageRemoveVerifiedEmailemail!, name, replyTo, scope / email!, scope
engageMessageSendTestEmailfrom!, to!, content!, title!
engagesUpdateConfigs / broadcastUpdateConfigsconfigsMap: JSON!
engageSendMailintegrationId, 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 }
}
OperationKey arguments
notificationsids, status (READ/UNREAD/ALL), priority (LOW/MEDIUM/HIGH/URGENT), type (INFO/SUCCESS/WARNING/ERROR), fromDate, endDate, fromUserId, module, cursor params
notificationDetail / unreadNotificationsCount_id! / none
pluginsNotificationsnone; returns every plugin's declared modules/events
notificationSettingsnone; returns the per-user event/channel matrix
emailDeliveries / emailDeliveryDetailstatus, source, provider, searchValue, date range, cursor params / _id!
emailAddresseslane, suppressionReason, searchValue, emails, cursor params
emailRampStatusnone
markNotificationAsRead / markAsReadNotifications_id! / notification filters
archiveNotification / archiveNotifications_id! / ids, archiveAll, filters
updateNotificationSettingsEvent / Channelinput: { event, enabled, channels } / input: { channel, enabled, metadata }
emailAddressRelease / emailRampReleaseemail!, 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
  }
}
Was this helpful?