Messaging & Email
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 (
EMAILorPUSH) - 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:
| Type | Description |
|---|---|
TEXT | Plain text content |
HTML | HTML-formatted content |
IMAGE | Image references |
VIDEO | Video references |
AUDIO | Audio references |
FILE | File attachments |
PRAYER | Prayer request content |
ACTIVITY | Activity-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:
- Validates the message (at least one channel and one recipient)
- Enqueues the message as a job on the message queue
- The
EmailJobworker picks up the job asynchronously - 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:
- Looks up each recipient's profile
- Fetches profile attributes to find the recipient's name (
bosca.profiles.name) and email (bosca.profiles.email) - Constructs the email recipient list from these attributes
- 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.fromsection withnameandemail) - 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:
- Implement the
Mailerinterface - Add a new entry to the
MailerTypeenum - Wire the new type in
MessageServiceImplalongside the existingSENDGRIDcase
Integration with Security
The messaging system is tightly integrated with Identity Management for authentication-related emails:
- Email Verification — after signup, an
EmailVerificationMessageis sent to confirm the user's email address - Forgot Password — a
ForgotPasswordMessagedelivers a password-reset token - Password Reset Confirmation — a
PasswordResetMessageconfirms the password was changed
These use the EmailTemplate interface, which provides:
initialize(profileId)— loads context for the recipientgetSubject()— returns the email subjectgetHtml()/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
Messagewith the appropriate channels and content
For Developers
The messaging system spans two modules:
backend/framework/core-messages— models (Message,MessageContent,MessageTemplate,Channel) and theMessageServiceinterfacebackend/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:
- Identity flows: Identity Management
- Workflow notifications: Workflows
- Campaigns: Segmentation & Campaigns
- Push notifications: Devices & Push
- Architecture overview: Architecture