Messaging & Email

Transactional email and notifications powered by pluggable mailers, channels, templates, and async job processing.

Bosca includes a messaging layer for transactional communications—email verification, password resets, organization invites, and workflow notifications. It is built around a pluggable architecture with asynchronous delivery, rich content types, and template support.

Core Concepts

Messages

A Message is the central unit of the messaging system. Each message specifies:

  • channels — how the message is delivered (EMAIL or PUSH)
  • subject — the message subject line
  • sender — an optional profile ID of the sender
  • recipients — a list of profile IDs to receive the message
  • content — one or more content parts with different types
# Simplified representation of the Message model
Message {
    channels: [EMAIL]
    subject: String
    sender: UUID        # profile ID (optional)
    recipients: [UUID]  # profile IDs
    content: [MessageContent]
}

Content Types

Messages support rich, multi-part content. Each content part has a type and a body:

TypeDescription
TEXTPlain text content
HTMLHTML-formatted content
IMAGEImage references
VIDEOVideo references
AUDIOAudio references
FILEFile attachments
PRAYERPrayer request content
ACTIVITYActivity-related content

Content parts can also carry optional JSON attributes for additional metadata.

A typical email includes both HTML and TEXT content parts so recipients see a rich version or a plain-text fallback.

Channels

A Channel represents a configured delivery pathway with a unique key, name, and JSON configuration. Channels allow organizations to set up and manage different delivery endpoints.

Message Templates

MessageTemplates provide reusable message structures identified by a unique key. Each template includes a title and JSON attributes that define the template's content and layout. Templates are used internally by the security module to generate verification and password-reset emails through the EmailTemplate interface.

How Delivery Works

Asynchronous Processing

When you call MessageService.send(message), Bosca does not send the message immediately. Instead, it:

  1. Validates the message (at least one channel and one recipient)
  2. Enqueues the message as a job on the message queue
  3. The EmailJob worker picks up the job asynchronously
  4. The worker calls sendNow() to deliver through the appropriate mailer

This design keeps your application responsive—message delivery happens in the background without blocking the calling code.

For cases that need immediate delivery, MessageService.sendNow(message) bypasses the queue and sends directly.

Recipient Resolution

Messages address recipients by profile ID, not by email address. During delivery, Bosca:

  1. Looks up each recipient's profile
  2. Fetches profile attributes to find the recipient's name (bosca.profiles.name) and email (bosca.profiles.email)
  3. Constructs the email recipient list from these attributes
  4. Skips any recipients that lack a valid name or email

This means your code never needs to manage email addresses directly—just reference profiles and Bosca resolves the rest.

Pluggable Mailers

The Mailer interface abstracts email delivery, making it easy to swap providers:

interface Mailer {
    suspend fun newEmail(name: String, email: String): Email
    suspend fun newContent(type: ContentType, content: String): Content
    suspend fun newMessage(from: Email, to: List<Email>, subject: String, content: List<Content>): EmailMessage
    suspend fun send(message: EmailMessage)
}

SendGrid (Default)

Bosca ships with a SendGrid mailer implementation. Configuration requires:

  • A SendGrid API token (configured as sendgrid.token)
  • A default sender (configured in the mailer.from section with name and email)
  • The mailer type set to sendgrid

The SendGrid mailer constructs personalized requests using SendGrid's API format, including support for multiple recipients and mixed text/HTML content.

Adding a New Provider

To add a new email provider:

  1. Implement the Mailer interface
  2. Add a new entry to the MailerType enum
  3. Wire the new type in MessageServiceImpl alongside the existing SENDGRID case

Integration with Security

The messaging system is tightly integrated with Identity Management for authentication-related emails:

  • Email Verification — after signup, an EmailVerificationMessage is sent to confirm the user's email address
  • Forgot Password — a ForgotPasswordMessage delivers a password-reset token
  • Password Reset Confirmation — a PasswordResetMessage confirms the password was changed

These use the EmailTemplate interface, which provides:

  • initialize(profileId) — loads context for the recipient
  • getSubject() — returns the email subject
  • getHtml() / getText() — returns the email body in both formats

Push Notifications

Bosca also supports push notifications as a messaging channel. When combined with Device Registration and Campaigns, you can deliver push notifications to registered devices across iOS, Android, web, and desktop platforms.

Push delivery integrates with the campaign system for targeted audience delivery, while the underlying message infrastructure handles token resolution and platform-specific formatting.

Typical Uses

  • Organization Invites — automatically send emails when you invite new members
  • Security Flows — handle email verification, passwordless login links, and password resets
  • Workflow Notifications — alert teams when content is ready for review or has been published
  • Campaign Delivery — send targeted emails and push notifications to segmented audiences
  • Custom Communications — send any transactional email by constructing a Message with the appropriate channels and content

For Developers

The messaging system spans two modules:

  • backend/framework/core-messages — models (Message, MessageContent, MessageTemplate, Channel) and the MessageService interface
  • backend/framework/messages — implementation (MessageServiceImpl, EmailJob, SendGridMailer), mailer configuration, and provider adapters

To send a message programmatically:

val message = Message(
    channels = listOf(MessageChannel.EMAIL),
    subject = "Welcome to Bosca",
    recipients = listOf(profileId),
    content = listOf(
        MessageContent(type = MessageContentType.HTML, content = "<h1>Welcome!</h1>"),
        MessageContent(type = MessageContentType.TEXT, content = "Welcome!")
    )
)
messageService.send(message) // async via job queue

Related: