Launch Rail Logo
Launch Rail
Core Documentation

Notifications Service

The ultimate communication orchestration layer. Decouple business logic from delivery concerns with a policy-driven engine for Email, SMS, Push, In-App, and more.

System Overview

In a modern microservices architecture, communication is the most fragile domain. The Launch Rail Notifications Service is a policy-driven orchestration engine that sits between your business logic and delivery providers. It handles the "Last Mile" of communication—ensuring messages are localized, legal, and delivered at the perfect moment.

Decoupled Producers

Backend services emit business intents (e.g. "order.shipped"), not formatting. The service handles HTML/Push rendering, translations, and templates.

Regulatory Guardrails

Natively enforces unsubscribe links, opt-out headers, quiet hours, and global rate limits to protect your sender reputation and compliance status.

The Orchestration Pipeline

Step 1: Ingest

Contract Validation

The ConnectRPC layer validates the event payload against the Catalog's JSON Schema. Immediate rejection on mismatch.

Step 2: Policy

Rules & Preferences

Fetches user categories, quiet hours, and topic mutes. Determines if the event is "Critical" enough to bypass filters.

Step 3: Render

Localized Copy

Resolves the best template for the user's locale and resolves data variables using high-performance Go templates.

Step 4: Dispatch

Multi-Worker Out

Asynchronous workers hand off to SMTP, FCM, or Twilio. Records the provider message ID for tracking.

The Events Catalog

The catalog is the runtime configuration that defines your notification taxonomy. Every event must be registered here before it can be triggered.

Comprehensive YAML Structure

events:
  - name: "billing.invoice_reminder"
    category: "billing"
    priority: "HIGH" # LOW, NORMAL, HIGH, CRITICAL
    bypass_user_preferences: false
    supports_topic_mute: true
    allowed_channels: ["EMAIL", "PUSH", "IN_APP"]
    default_channels: ["EMAIL", "IN_APP"]
    allowed_override_fields: ["locale", "priority_override"]
    default_locale: "en-US"
    fallback_locales: ["en", "uk-UA"]
    default_ttl: "48h" # Drop if not delivered in 2 days
    deduplication_window: "24h" # Prevent duplicate reminders
    supports_digest: true
    default_digest_policy:
      aggregation_key: "invoice_id"
      window: "24h"
    rate_limit:
      scope: "RECIPIENT_AND_TENANT"
      max_events: 5
      window: "24h"
    payload_schema:
      type: "object"
      required: ["invoice_id", "due_date"]
      properties:
        invoice_id: { type: "string" }
        due_date: { type: "string", format: "date" }
Deduplication logic

If an event with the same Recipient + EventType + Category arrives within the deduplication_window, the service returns INGEST_STATUS_DUPLICATE and prevents a second delivery. This is perfect for noisy monitoring alerts or repeated user actions.

Throttling Policy

Rate limits are evaluated at the ingestion point. A RECIPIENT_AND_TENANT scope ensures a single workspace doesn't flood a single user, even across multiple projects.

Backend Producer SDK

Trigger & Batch RPCs

The service provides three primary ways to emit alerts:

Single Trigger

Synchronous validation for one recipient. Best for transactional actions.

Multi-Batch

Highly parallelized ingest for concrete recipient lists (up to 1,000 items).

Audience Job

Fires an asynchronous job to target large segments (e.g. "active-enterprise-users").

Advanced Scheduling Logic

Absolute UTC Time

The event fires at a specific global moment. 2 PM UTC is 2 PM UTC for everyone. Best for webinars or maintenance windows.

ScheduleAt: timestamppb.New(utcTime),

Recipient Local Wall-Time

The service uses the recipient's resolved IANA TimeZone to determine the dispatch moment. "9 AM Local" means users in Tokyo receive it 8 hours before London.

LocalWallTime: "09:00:00",

The Idempotency Guard

Producers should derive stable keys from business entities. If your payment service retries a request, use the payment_id as the key.

Key: "invoice:123:paid"
Status: OK (Accepted)
Key: "invoice:123:paid"
Status: DUPLICATE (Safe)

Delivery State Machine

Once an event is accepted, it transitions through several states. Monitoring these via the Admin API or Webhooks allows for high-granularity support observability.

PENDING

Accepted for orchestration.

SCHEDULED

Waiting for local-time window.

DISPATCHED

Handed to Provider (Email/SMS API).

DELIVERED

Provider confirmed delivery.

FAILED

Permanent failure (Max retries hit).

BOUNCED

Provider reported inbox rejection.

DROPPED

Suppressed by policy or TTL.

CANCELED

Canceled via CancelScheduledEvent.

RATE_LIMITED

Dropped to protect tenant throughput.

Frontend Integration

Patching Preferences with FieldMasks

Developer Warning: The preference API uses standard Google FieldMasks. If you only want to update the time zone, you must specify the field in the mask. Providing an empty mask may accidentally reset other fields in a partial update.
// Correct way to update only Quiet Hours
await prefClient.updateUserPreferences({
  preferences: {
    quietHours: {
      enabled: true,
      startHour: 22,
      endHour: 8
    }
  },
  updateMask: { paths: ["quiet_hours"] } // Explicitly targets this field
});

Push Device Lifecycle

Tokens are volatile. The system expects regular idempotent refreshes. Register the token on every app launch to ensure the server-side TTL never expires.

Automatic Expiry

Inactive devices are automatically marked EXPIRED after 90 days of silence, protecting you from FCM "ghost" send costs.

Multi-Device Routing

The service manages a set of active tokens per user. One "PUSH" trigger fans out to all active devices simultaneously.

Admin Operations

Provider Compatibility Matrix

ChannelTechnical DriverService Type
EMAILSMTP / SendGrid API / AWS SESTransactional
SMSTwilio / Vonage / Amazon SNSLegacy / Direct
PUSHFCM (Android/Web) / APNS (Apple)App Native
SLACKOAuth App / Incoming WebhooksCollaboration
WEBHOOKHTTPS (HMAC Signed)Enterprise

Webhook HMAC Security

The service signs every outbound webhook with a SHA-256 HMAC signature. Consumers must verify the X-LaunchRail-Signature header against their shared secret.

// Verifying signature in Go
expectedMAC := hmac.New(sha256.New, secret)
expectedMAC.Write(bodyBytes)
expectedSignature := hex.EncodeToString(expectedMAC.Sum(nil))

if !hmac.Equal([]byte(receivedSignature), []byte(expectedSignature)) {
    return status.Error(codes.Unauthenticated, "invalid signature")
}

Compliance & GDPR Flow

One-Click Unsubscribe

Transactional emails automatically include List-Unsubscribe headers. The system exposes an unauthenticated compliance endpoint that uses signed tokens to record opt-outs without friction.

The "Right to be Forgotten"

The DeleteUserData RPC scrubs all delivery history, archived in-app items, and preference records across all providers instantly.

Technical Mastery Awaits.

The Notifications Service is built on Go 1.25, using ConnectRPC for networking and NATS for massive asynchronous fan-out. It is designed to scale to millions of alerts per minute.