Back to .md Directory

FreeRangeNotify — Simplification Roadmap

> **Audience**: Product, Engineering, Sales

May 2, 2026
0 downloads
1 views
ai eval
View source

FreeRangeNotify — Simplification Roadmap

Audience: Product, Engineering, Sales
Purpose: Identify every friction point a new user faces and propose concrete solutions to make FreeRangeNotify dead-simple to adopt.
Date: March 2026


Table of Contents

  1. The Problem — A Sales Perspective
  2. Current User Journey (Step-by-Step)
  3. Pain Point Analysis
  4. Proposed Solutions
  5. Priority Matrix
  6. Appendix — Current API Call Sequence

1. The Problem — A Sales Perspective

Imagine you're a developer evaluating FreeRangeNotify for your SaaS product. You sign up, and here's what you have to do before you can send a single notification:

Step 1: Register an admin account (POST /v1/auth/register)
Step 2: Log in to get a JWT token (POST /v1/auth/login)
Step 3: Create an Application (POST /v1/apps) → get an API key
Step 4: Create a User (POST /v1/users with the API key)
Step 5: Create a Template (POST /v1/templates with the API key)
Step 6: Now FINALLY send a notification (POST /v1/notifications)

That's 6 API calls and 3 different auth mechanisms (JWT for admin, API key for users/templates/notifications, and knowing which to use when) — just to see "Hello World" arrive at a webhook.

Compare this to competitors:

  • OneSignal: Install SDK → call sendNotification({ message: "Hello" }) → done.
  • the reference platform: Create trigger → send event → done.
  • Firebase Cloud Messaging: Get server key → POST to FCM → done.

FreeRangeNotify is architecturally superior to many of these (priority queues, smart delivery, template rendering, multi-channel), but the onboarding friction is killing adoption before users ever see those features.


2. Current User Journey (Step-by-Step)

Here's what a developer actually experiences today, annotated with friction points:

Step 1: Authentication Confusion

Question: "Do I use JWT or API key?"
Answer: JWT for admin/app management. API key for everything else.
But: This is not documented clearly. AUTH_QUICKSTART.md mentions JWT.
     API_DOCUMENTATION.md mentions API key. Neither explains the relationship.

Step 2: Create Application

# Need JWT first
curl -X POST http://localhost:8080/v1/auth/register \
  -d '{"username":"admin","email":"admin@x.com","password":"SecurePass123!"}'

curl -X POST http://localhost:8080/v1/auth/login \
  -d '{"email":"admin@x.com","password":"SecurePass123!"}'
# Response: JWT token

# Now create app with JWT
curl -X POST http://localhost:8080/v1/apps \
  -H "Authorization: Bearer <JWT>" \
  -d '{"app_name":"My App"}'
# Response: API key (frn_xxxxx)

Friction: Two auth flows before you even start.

Step 3: Create Users

curl -X POST http://localhost:8080/v1/users \
  -H "Authorization: Bearer frn_xxxxx" \
  -d '{
    "email": "john@example.com",
    "timezone": "UTC",          # Free text, no validation
    "language": "en",           # Free text, no validation
    "preferences": {
      "email_enabled": true,
      "push_enabled": true,
      "sms_enabled": true
    }
  }'
# Response: internal UUID (you need THIS, not the email, to send notifications)

Friction: The response gives you an internal UUID. You must save this UUID to send notifications later. There's no way to send a notification by email address.

Step 4: Create Template (The Hard Part)

curl -X POST http://localhost:8080/v1/templates \
  -H "Authorization: Bearer frn_xxxxx" \
  -d '{
    "app_id": "<app_uuid>",        # Wait, why do I need this? It is auto-set from the API key
    "name": "welcome",
    "channel": "email",            # Must match the channel you send on
    "body": "Hello {{.name}}, welcome to {{.product}}!",
    "variables": ["name", "product"],   # Must manually list every variable
    "locale": "en",                # Required even for single-language apps
    "subject": "Welcome!"
  }'

Friction:

  • app_id is required in the DTO but auto-set from context — confusing.
  • variables must be manually listed AND match the {{.name}} patterns in body — easy to desync.
  • locale is required even when you don't need i18n.
  • If you want HTML email newsletters, you paste raw HTML into a JSON string field. No preview, no WYSIWYG, no upload.
  • Templates are channel-locked — a "welcome" template for email can't be reused for push.
  • No template editing in the UI — you must delete and re-create (losing the template ID).

Step 5: Send Notification

curl -X POST http://localhost:8080/v1/notifications \
  -H "Authorization: Bearer frn_xxxxx" \
  -d '{
    "user_id": "<internal_uuid>",    # NOT the email, not the external ID
    "channel": "email",
    "priority": "normal",
    "template_id": "<template_uuid>",
    "data": {
      "name": "John",
      "product": "Acme"
    }
  }'

Friction:

  • Must know user's internal UUID (not email or external ID) — requires a lookup.
  • Must know template UUID — requires a lookup.
  • Must know which variables to pass — no discovery mechanism.
  • Channel must be specified AND must match the template's channel — redundant.
  • Even for a simple "Hello World" webhook, you need ALL of the above.

3. Pain Point Analysis

3.1 Onboarding & First Notification

IssueSeverityDetail
6-step setup for first notificationCriticalTakes 15-30 minutes for an experienced developer
Dual auth confusion (JWT + API key)HighNo clear documentation on when to use which
No "Hello World" quickstartCriticalNo script or wizard to get a working example in < 2 minutes
Migration tool is a no-opMediummigrate command prints fake success messages but does nothing
setup_data.py only sets up webhookMediumNo email, push, or SSE example data

3.2 Application Setup

IssueSeverityDetail
AppForm.tsx is outdatedLowUses raw CSS, missing webhook fields; superseded by AppDetail
Settings form is fragileMediumDeep nested state with as any casts; no validation feedback
Named webhooks are buried in OverviewLowShould be in Settings or dedicated section
No provider status visibilityMediumCan't see if SMTP/APNS/FCM is configured and healthy

3.3 User Management

IssueSeverityDetail
No lookup by email/external IDHighMust use internal UUID everywhere
No bulk importHighAdding 1000 users requires 1000 API calls
Timezone/language are free textMedium"UTC" vs "utc" vs "America/New_York" — no validation
User table shows minimal infoLowNo UUID, timezone, or preference summary visible
No phone field in UIMediumBackend supports SMS but UI can't set phone numbers
No device management in UIMediumPush notifications require device tokens but UI can't add them
No paginationMediumWill break with > 100 users

3.4 Template Management (The Biggest Pain)

IssueSeverityDetail
No template editingCriticalMust delete and re-create to change a typo
HTML templates as JSON stringsCriticalNo WYSIWYG, no file upload, no preview in creation flow
Variables must be manually listedHighAuto-detect in UI adds but never removes; API requires manual list
Channel-locked templatesHighSame message needs separate templates per channel
app_id required but auto-setMediumConfusing redundant field
Locale required for single-langMediumUnnecessary friction; should default to "en"
No versioning UIMediumBackend supports versions but UI doesn't expose it
Go text/template syntaxHighUsers must learn {{.variable}} syntax; no Handlebars/Mustache
No template library/marketplaceMediumNo pre-built templates for common use cases
Uses text/template not html/templateMediumXSS risk for email HTML content

3.5 Sending Notifications

IssueSeverityDetail
User ID is internal UUID onlyHighCan't send by email address or external ID
Channel must match templateMediumRedundant specification — template already knows its channel
Send form is extremely denseHighWebhook targets, scheduling, recurrence, multi-user — all in one form
Multi-send doesn't use bulk endpointMediumUI fires parallel individual calls instead of POST /bulk
No real-time status updatesMediumMust manually refresh to see delivery status
No retry/cancel from notification listMediumBackend supports it but UI doesn't expose it
Broadcast does N sequential API callsMediumBackend already has POST /broadcast but service loops internally

3.6 Monitoring & Debugging

IssueSeverityDetail
No delivery log viewerHighCan't see provider responses, error messages, or latency
No analytics dashboardMediumBackend model exists but no UI or data pipeline
History has no pagination/filterMediumAll notifications fetched at once
DLQ has no dashboard or alertingMediumFailed notifications silently enter dead letter queue
No health status for providersMediumCan't tell if SMTP is connected, APNS cert is valid, etc.
Debug logging in production codeLowfmt.Printf("DEBUG: ...") statements in notification_handler.go

4. Proposed Solutions

4.1 Quick-Start Wizard

Goal: Send a notification in < 2 minutes from signup.

A) "Try It Now" Sandbox Mode

After creating an app, automatically:

  1. Create a sandbox user with the admin's email
  2. Create a default webhook template ("Hello {{.name}}!")
  3. Show a one-click "Send Test Notification" button
  4. Display the webhook/SSE payload in real-time
Dashboard after app creation:
┌──────────────────────────────────────────┐
│  ✅ App created! Here's your API key:    │
│  frn_abc123...                           │
│                                          │
│  🚀 Try it now:                          │
│  [Send Test Notification]                │
│                                          │
│  📡 Waiting for delivery...              │
│  ✅ Delivered! Webhook received:         │
│  { "title": "Hello Admin!", ... }        │
└──────────────────────────────────────────┘

B) Guided Setup Flow (for the UI)

Replace the current "create users → create templates → send" flow with a step-by-step wizard:

Step 1: What do you want to send?
   [ ] Email  [ ] Push  [ ] SMS  [ ] Webhook  [ ] Browser (SSE)

Step 2: Write your message
   Subject: [Welcome to {{.product}}!        ]
   Body:    [Rich text editor with variables  ]
   
   Detected variables: product, name
   (auto-saved as template)

Step 3: Who should receive it?
   [ ] Paste email addresses
   [ ] Upload CSV
   [ ] Select existing users
   [ ] Everyone (broadcast)

Step 4: When?
   (•) Send now  ( ) Schedule  ( ) Recurring

   [Send Notification →]

C) One-Line API Quickstart

For API users, provide a single-call "just send it" endpoint:

# NEW: Simplified send — no template, no user pre-registration
curl -X POST http://localhost:8080/v1/quick-send \
  -H "Authorization: Bearer frn_xxxxx" \
  -d '{
    "to": "john@example.com",
    "channel": "email",
    "subject": "Welcome!",
    "body": "Hello John, welcome to Acme!"
  }'

The backend would:

  1. Auto-create or lookup the user by email
  2. Use an inline body (no template required)
  3. Default priority to "normal"
  4. Return the notification status

4.2 Template Simplification

A) Enable Template Editing

The backend PUT /v1/templates/:id already works. The UI's AppTemplates.tsx needs an Edit button + pre-filled form. This is the #1 quickest win.

B) Rich HTML Editor for Email Templates

Replace the raw textarea with a WYSIWYG editor:

  • Option 1 (Recommended): Integrate Unlayer or React Email Editor — drag-and-drop HTML email builder
  • Option 2: Use TipTap for rich text with HTML output
  • Option 3: File upload endpoint — let users upload .html files

C) Multi-Channel Templates

Allow a single "template group" to have channel-specific variants:

{
  "name": "welcome",
  "variants": {
    "email": { "subject": "Welcome!", "body": "<h1>Hello {{.name}}</h1>" },
    "push":  { "body": "Welcome to {{.product}}, {{.name}}!" },
    "sms":   { "body": "Welcome {{.name}}! Reply STOP to unsubscribe" }
  }
}

When sending, the system auto-selects the right variant based on the channel.

D) Template Variable Auto-Detection

Remove the manual variables array requirement. The backend should parse {{.xxx}} from the body and auto-populate the variables list. The TemplateService already validates them — just reverse the flow.

E) Default Locale

Make locale default to "en" when not specified. Most apps start single-language.

F) Pre-Built Template Library

Ship with common templates:

  • Welcome email (HTML)
  • Password reset
  • Order confirmation
  • Weekly digest/newsletter
  • Alert/warning
  • Promotional offer

Users clone from the library instead of starting from scratch.

G) Template Preview with Live Data

The backend already has POST /v1/templates/:id/render. The UI has a preview panel. Improve it by:

  • Showing rendered HTML in an iframe (not just text)
  • Auto-suggesting test data based on variable names
  • Adding a "Send Test" button that delivers to the admin's email

4.3 Send Flow Simplification

A) Send by Email/External ID (not just internal UUID)

Add a to field that accepts:

  • Email address → auto-lookup user
  • External ID → auto-lookup user
  • Internal UUID → direct (current behavior)
{
  "to": "john@example.com",       // NEW: resolves to user internally
  "template": "welcome",          // NEW: by name, not just UUID
  "data": { "name": "John" }
  // channel auto-inferred from template
  // priority defaults to "normal"
}

B) Infer Channel from Template

The template already specifies its channel. When sending a notification with a template_id, don't require channel to be specified separately — infer it from the template.

C) Simplified Send Form in UI

Split the current monolithic send form into 3 modes:

  1. Quick Send: Recipient + Template + Data → Send
  2. Advanced Send: Full form with scheduling, recurrence, priority, webhook targets
  3. Broadcast: Template + Data → Send to all

D) Use Bulk Endpoint in UI

The AppNotifications.tsx multi-send currently fires parallel individual API calls:

// Current (broken):
await Promise.all(selectedUsers.map(userId => api.post('/notifications', { user_id: userId, ... })))

// Should be:
await api.post('/notifications/bulk', { user_ids: selectedUsers, ... })

4.4 User Management Improvements

A) Bulk User Import

Add POST /v1/users/bulk endpoint accepting CSV or JSON array:

{
  "users": [
    { "email": "alice@x.com", "language": "en" },
    { "email": "bob@x.com", "language": "fr" }
  ]
}

The backend user.Repository already has BulkCreate — just wire it to a handler.

B) User Lookup by Email

Add GET /v1/users?email=john@example.com support. The repository already has GetByEmail.

C) Validated Dropdowns

In the UI, replace free-text timezone and language with dropdown selects:

  • Timezone: IANA timezone list (America/New_York, Europe/London, etc.)
  • Language: ISO 639-1 codes (en, fr, es, de, etc.)

D) Phone Number Field in UI

The backend supports phone but the UI's create user form doesn't show it. Just add the field.

E) Device Token Management in UI

For push notifications (Apple Push Notification service / Firebase Cloud Messaging), expose the POST /v1/users/:id/devices endpoint in the UI with fields for platform and token.

4.5 Dashboard & Observability

A) Notification Activity Feed

Real-time feed showing notification status changes:

✅ 12:01 PM — "Welcome email" delivered to john@x.com (142ms)
❌ 12:02 PM — "Order confirm" failed for bob@x.com — SMTP timeout
🔄 12:02 PM — Retrying "Order confirm" (attempt 2/3)
✅ 12:03 PM — "Order confirm" delivered to bob@x.com (89ms)

B) Provider Health Dashboard

Show the status of each configured provider:

┌─────────────┬─────────┬──────────┬──────────────┐
│ Provider    │ Status  │ Latency  │ Success Rate │
├─────────────┼─────────┼──────────┼──────────────┤
│ SMTP        │ ✅ Up   │ 230ms    │ 99.2%        │
│ APNS        │ ✅ Up   │ 120ms    │ 98.8%        │
│ FCM         │ ❌ Down │ —        │ —            │
│ Twilio SMS  │ ✅ Up   │ 340ms    │ 97.5%        │
│ Webhook     │ ✅ Up   │ 85ms     │ 99.9%        │
│ SSE         │ ✅ Up   │ 2ms      │ 100%         │
└─────────────┴─────────┴──────────┴──────────────┘

C) Dead Letter Queue Viewer

Surface the existing admin DLQ API in the UI:

  • List of failed notifications with error messages
  • One-click retry
  • Bulk retry/dismiss

D) Notification Search & Filtering

Add filters to the notification history:

  • By status (pending, sent, failed, delivered)
  • By channel
  • By user
  • By date range
  • By template
  • Full-text search on content

4.6 SDK & Integration Layer

A) Client SDKs

Provide thin wrapper libraries that hide the setup complexity:

JavaScript/TypeScript SDK:

import { FreeRangeNotify } from '@freerangenotify/sdk';

const frn = new FreeRangeNotify('frn_your_api_key');

// One-liner to send
await frn.send({
  to: 'john@example.com',
  template: 'welcome',
  data: { name: 'John', product: 'Acme' }
});

Go SDK:

client := freerangenotify.New("frn_your_api_key")

client.Send(ctx, freerangenotify.Notification{
    To:       "john@example.com",
    Template: "welcome",
    Data:     map[string]any{"name": "John"},
})

Python SDK:

from freerangenotify import Client

frn = Client("frn_your_api_key")
frn.send(to="john@example.com", template="welcome", data={"name": "John"})

B) Framework Integrations

  • Next.js receiver (already exists in examples/nextjs-receiver)
  • Express.js middleware for webhook reception
  • React component for in-app notification bell
  • Browser SDK for SSE subscription

C) Webhook Playground

An in-dashboard tool that:

  1. Generates a temporary webhook URL (like webhook.site)
  2. Sends a test notification
  3. Shows the received payload in real-time

4.7 API Simplification

A) Reduce Required Fields

Current required fields for POST /v1/notifications:

  • user_id (internal UUID)
  • channel (must match template)
  • priority (usually "normal")
  • template_id (UUID, not name)

Proposed minimum required fields:

  • to (email, external ID, or UUID — any works)
  • template (name or UUID — either works)

Everything else has sensible defaults.

B) Template References by Name

Allow templates to be referenced by name instead of UUID:

{ "template": "welcome" }          // Resolves to latest active "welcome" template
{ "template_id": "uuid-123" }      // Explicit UUID (current behavior, still works)

C) Inline Content (No Template Required)

For simple notifications, allow inline content:

{
  "to": "john@example.com",
  "channel": "email",
  "subject": "Hello!",
  "body": "Welcome to our platform."
}

No template creation needed. The system creates a transient content block.

D) Unified Auth Documentation

Create a single page explaining:

  • JWT tokens: For admin operations (app CRUD, dashboard login)
  • API keys (frn_ prefix): For programmatic access (send notifications, manage users/templates)
  • When to use which: Clear decision table
  • SSO: When and how OIDC works

5. Priority Matrix

Phase 1 — Quick Wins (1-2 weeks, immediate impact)

#ChangeEffortImpactDescription
1Enable template editing in UILowHighAdd Edit button to AppTemplates.tsx — backend already supports PUT
2Default locale to "en"LowMediumOne line change in TemplateService.Create
3Auto-detect variablesLowMediumRemove variables from required fields; auto-parse from body
4Fix multi-send to use bulk APILowMediumChange Promise.all → single POST /bulk call
5Add phone field to UILowLowSimple input field addition
6Remove debug fmt.PrintfLowLowClean up notification_handler.go
7Add pagination to user/template/notification listsMediumMediumPrevents UI from breaking at scale

Phase 2 — Core Simplifications (2-4 weeks)

#ChangeEffortImpactDescription
8Quick-Send API endpointMediumCriticalPOST /v1/quick-send with email resolution and optional inline content
9Send by email / external IDMediumHighUser lookup layer in notification service
10Template reference by nameMediumHighAllow "template": "welcome" in send requests
11Infer channel from templateLowMediumRemove redundant channel requirement
12Simplified send form in UIMediumHighQuick/Advanced/Broadcast mode split
13Timezone/language dropdownsLowMediumReplace free-text inputs with validated selects
14Unified auth documentationMediumHighSingle page explaining JWT vs API key
15Bulk user import endpointMediumMediumWire existing BulkCreate to handler

Phase 3 — Premium Experience (1-2 months)

#ChangeEffortImpactDescription
16WYSIWYG email template editorHighCriticalUnlayer/TipTap integration for HTML emails
17Setup wizard in UIHighHighGuided flow: channel → message → recipients → send
18Pre-built template libraryMediumHighShip common templates (welcome, reset, digest)
19Notification activity feedMediumHighReal-time delivery status stream
20Provider health dashboardMediumMediumStatus, latency, success rate per provider
21DLQ viewer in UIMediumMediumList, retry, dismiss failed notifications
22Multi-channel template groupsHighHighSingle template with per-channel variants
23Client SDKs (JS, Go, Python)HighHighHide API complexity behind libraries

Phase 4 — Competitive Edge (2-3 months)

#ChangeEffortImpactDescription
24Webhook playgroundMediumMediumIn-dashboard webhook testing tool
25Notification search & filteringMediumMediumFull-text search with status/channel/date filters
26React notification bell componentMediumMediumDrop-in UI component for in-app notifications
27Template diff/versioning UIMediumLowVisual comparison between template versions
28Analytics dashboardHighMediumDelivery rates, channel breakdown, user engagement
29Provider fallback chainsHighMediumAuto-failover: SendGrid → SMTP, FCM → APNS

6. Appendix — Current API Call Sequence

Today: Sending a Welcome Email (6 calls, ~3 minutes)

1. POST /v1/auth/register    → { username, email, password }    → JWT token
2. POST /v1/auth/login       → { email, password }              → JWT token
3. POST /v1/apps             → { app_name } + JWT               → API key + app_id
4. POST /v1/users            → { email, preferences } + APIkey  → user_id (UUID)
5. POST /v1/templates        → { name, channel, body, variables, locale } + APIkey → template_id
6. POST /v1/notifications    → { user_id, channel, priority, template_id, data } + APIkey → notification

Goal: Sending a Welcome Email (2 calls, ~30 seconds)

1. POST /v1/auth/register    → { email, password }              → JWT + auto-created app + API key
2. POST /v1/quick-send       → { to: "john@x.com", subject: "Welcome!", body: "Hello!" } + APIkey → notification

Goal for SDK Users (1 call, ~10 seconds)

// Assumes API key is configured
frn.send({ to: "john@x.com", template: "welcome", data: { name: "John" } })

Summary

FreeRangeNotify has a strong architecture — priority queues, smart delivery, multi-channel routing, template rendering, SSE real-time, circuit breakers, rate limiting, quiet hours, DND. These are enterprise-grade features.

But the developer experience is optimized for the system's internal complexity, not the user's mental model. Users don't think in terms of "create application, then create user with internal UUID, then create a channel-locked template with Go syntax variables, then send a notification by specifying the channel again." They think: "Send this message to this person."

Every solution in this document moves toward that mental model: fewer required fields, smarter defaults, auto-resolution, and guided flows. The architecture stays the same — we just need to build a simpler surface on top of it.

The quickest wins (Phase 1) can ship in a week and immediately reduce the "time to first notification" from 15 minutes to 5 minutes. Phase 2 gets it under 1 minute. Phase 3 makes it competitive with the best in the market.

Related Documents