Skip to main content
OpenInsure delivers notifications across five channels from a single dispatch endpoint. All channels are optional — the system gracefully skips any channel whose credentials are absent.

Email

Microsoft Graph (O365) or Resend or SMTP

SMS

Twilio

Teams

Microsoft Teams Bot Framework

Slack

Incoming Webhook

In-App

Stored in PlanetScale, served via API

Architecture

POST /v1/notifications/dispatch

        ├─ email    → createEmailProvider()  → Graph / Resend / SMTP
        ├─ sms      → createTwilioClient()   → Twilio REST API
        ├─ teams    → createTeamsBotClient() → Bot Framework / Graph
        ├─ slack    → createSlackClient()    → Incoming Webhook
        └─ in_app   → db.insert(notifications)

               communicationsLedger (delivery audit trail)
The dispatchNotification() function in packages/notify/src/index.ts fans out to all requested channels in parallel and returns a per-channel result. Results are written to communications_ledger for audit.

Email Provider

Selecting a provider

Set EMAIL_PROVIDER in .dev.vars or wrangler.toml [vars]:
ValueProviderBest for
graphMicrosoft Graph sendMailO365 licensed mailboxes, MGA tenants
resendResend APITransactional email to external recipients
smtpSMTP relaySelf-hosted or custom relay
The Graph provider is the default in production (EMAIL_PROVIDER=graph in wrangler.toml).

Graph email secrets

wrangler secret put EMAIL_PROVIDER       # graph
wrangler secret put AZURE_TENANT_ID
wrangler secret put AZURE_CLIENT_ID
wrangler secret put AZURE_CLIENT_SECRET
wrangler secret put GRAPH_MAIL_FROM      # jd@openinsure.dev
See Microsoft 365 Integration for full setup details.

Resend email secrets

wrangler secret put EMAIL_PROVIDER    # resend
wrangler secret put RESEND_API_KEY    # re_...
wrangler secret put RESEND_FROM       # noreply@openinsure.dev

SMS (Twilio)

SMS is delivered via Twilio Programmable SMS. The client is created only when both TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN are present.
wrangler secret put TWILIO_ACCOUNT_SID
wrangler secret put TWILIO_AUTH_TOKEN
wrangler secret put TWILIO_FROM_NUMBER  # +18005551234

Microsoft Teams

Teams messages are dispatched via the Bot Framework. The Notifications Bot app registration is configured in wrangler.toml:
[vars]
TEAMS_BOT_APP_ID    = "ac81bbbd-53fe-4b8a-8755-dafded231e19"
TEAMS_BOT_TENANT_ID = "ebd58a52-c818-4230-b150-348ae1e17975"
wrangler secret put TEAMS_BOT_APP_PASSWORD
Caution: The current bot has Mail.Send permission only. ChatMessage.Send must be added and admin-consented before Teams channel messages work. See Fix Teams Messaging.

Slack

Slack notifications use an incoming webhook URL. No OAuth is required.
wrangler secret put SLACK_WEBHOOK_URL   # https://hooks.slack.com/services/...

In-App Notifications

In-app notifications are stored in PlanetScale (notifications table) and surfaced via the API. The client polls or subscribes via SSE. Endpoints:
MethodPathDescription
GET/v1/notifications?orgId=&read=falseList notifications
POST/v1/notifications/:id/readMark single read
POST/v1/notifications/read-allMark all read

Dispatch API

Request

POST /v1/notifications/dispatch
Authorization: Bearer {token}
Content-Type: application/json
{
  "orgId": "00000000-0000-4000-a000-000000000001",
  "userId": "10000000-0000-4000-a000-000000000010",
  "type": "submission_referred",
  "title": "New Submission Referred",
  "body": "A new GL submission from Acme Hardware has been referred for UW review.",
  "entityType": "submission",
  "entityId": "...",
  "template": "submission_referred",
  "templateData": {
    "submissionId": "SUB-2026-00042",
    "insuredName": "Acme Hardware",
    "line": "General Liability"
  },
  "channels": ["email", "teams", "in_app"],
  "email": { "to": "underwriter@mhcmga.com" },
  "teams": { "to": "underwriting@mhcmga.com" }
}

Available templates

TemplateTriggered by
policy_boundPolicy binds
claim_openedFNOL submitted
claim_settledClaim closed
quote_availableUW approves quote
renewal_reminder60/30/15 days before expiry
invoice_generatedBilling cycle runs
compliance_deadlineFiling due date approaching
approval_neededAuthority limit exceeded
bordereaux_submittedBordereaux sent to carrier
noc_deliveryNotice of cancellation issued
submission_referredSubmission escalated for review
endorsement_issuedMid-term endorsement processed
cancellation_completedPolicy cancelled
reinstatement_completedPolicy reinstated
audit_completedPremium audit finalized
nonrenewal_noticeNon-renewal notice issued
subrogation_updateSubrogation status change

Response

{
  "ok": true,
  "result": {
    "email": { "success": true, "providerMessageId": "msg_..." },
    "teams": { "success": false, "error": "ChatMessage.Send permission not granted" },
    "inApp": { "success": true }
  }
}

Communications Ledger

Every email and SMS dispatch is written to the communications_ledger table for compliance audit:
GET /v1/notifications/ledger?orgId=&status=failed&entityType=policy
The ledger captures: channel, provider, recipient, subject, status (sent / failed), provider message ID, and trace ID.

Adding a New Template

  1. Add the template key to the template enum in apps/api/src/routes/notifications.ts 2. Add the template rendering logic in packages/notify/src/templates/ 3. Document the trigger condition in the table above 4. Add tests in packages/notify/src/__tests__/