22 KiB
OnDeck - PRD Input Document
Project Overview
Project Name: OnDeck - Policy Renewal Workflow Management System
Client Type: Insurance Brokerage
Purpose: Streamline policy renewal workflows by centralizing client and policy data from AMS360, enabling custom client classifications, personnel assignments, and department-specific task management tied to policy renewal dates.
1. Problem Statement
Insurance brokerages managing large policy portfolios face challenges coordinating renewal activities across departments. Currently:
- Client and policy data lives in AMS360 with no custom workflow layer
- No systematic way to assign internal designations or classifications to clients
- Renewal tasks are tracked manually or in disconnected systems
- No visibility into workload distribution or deadline compliance across teams
- Personnel assignments to clients aren't centrally managed
Goal: Create OnDeck - a unified web application that syncs AMS360 data, adds custom metadata layers, and provides renewal-based task management with dashboards for workload visibility.
2. User Personas & Roles
2.1 Role Definitions
| Role | Description | Key Permissions |
|---|---|---|
| Admin | System administrators, IT staff | Full access: user management, sync configuration, all data, system settings |
| Manager | Department leads, supervisors | View all department data, assign tasks, manage personnel assignments, view all dashboards |
| Account Executive | Handles client relationships, renewals | View assigned clients, manage own tasks, update client info, view personal dashboard |
| Claims | Claims department staff | View assigned clients, manage claims-related tasks, limited client edit |
2.2 Authorization Model
Dual-layer approach:
-
Entra ID Groups → Base Role Mapping
- Entra Group membership determines initial role assignment
- Example:
SG-OnDeck-Admins→ Admin role - MFA enforced via Entra Conditional Access policies
-
In-App Granular Permissions
- Fine-grained permissions within roles
- Client-level access assignments (primary/additional personnel)
- Department-based data filtering
- Custom permission overrides when needed
3. Functional Requirements
3.1 Authentication & Authorization
3.1.1 Entra ID Integration
- SSO via Entra ID using MSAL.js or NextAuth.js Azure AD provider
- Token claims must include:
groups,preferred_username,name,oid - Group-to-role mapping configurable by Admin
- Leverage existing MFA - no additional MFA implementation needed
- Session management: Configurable timeout, refresh token handling
3.1.2 In-App Permission System
- Permission sets per role (CRUD matrix)
- Ability to assign individuals to specific clients (overrides department-level access)
- Audit log of permission changes
- Admin UI for managing Entra Group → Role mappings
3.2 AMS360 Data Sync Engine
3.2.1 Sync Configuration
- Frequency: Daily automated sync (configurable time window)
- Method: Direct SQL connection to AMS360 database
- Direction: One-way (AMS360 → Local Postgres)
- Entities to sync:
- Clients (customer master data)
- Policies (policy details, renewal dates, coverage info)
3.2.2 Sync Behavior
- Initial load: Full sync of all active clients and policies
- Incremental sync: Based on
ModifiedDateor equivalent timestamp fields - Conflict handling: AMS360 is source of truth for synced fields
- Sync logging: Record sync runs, row counts, errors
- Manual trigger: Admin can initiate on-demand sync
3.2.3 Data Mapping (AMS360 → Local)
AMS360 Client → local.clients
- CustomerID → ams_customer_id (unique key for sync)
- CustomerName → name
- Address fields → address_*
- Phone/Email → contact_*
- Producer → producer_code
- (other relevant fields TBD based on AMS360 schema)
AMS360 Policy → local.policies
- PolicyID → ams_policy_id (unique key for sync)
- CustomerID → client_id (FK to local.clients)
- PolicyNumber → policy_number
- EffectiveDate → effective_date
- ExpirationDate → expiration_date (THIS IS THE RENEWAL DATE)
- PolicyType → policy_type
- Carrier → carrier_name
- Premium → premium_amount
- Status → status
3.3 Client Management (Custom Data Layer)
3.3.1 Custom Client Fields (Local Only)
These fields are NOT synced from AMS360 - they are managed entirely in the app:
| Field | Type | Description |
|---|---|---|
shape |
FK to shapes table | Primary designation (user-defined) |
shape2 |
FK to shapes table | Secondary designation (user-defined) |
primary_personnel_id |
FK to users | Primary account owner (Entra user) |
notes |
Text | Internal notes |
custom_fields |
JSONB | Extensible custom field storage |
3.3.2 Shape/Designation Management
- Admin-configurable list of Shape values
- Each shape has:
id,name,description,color(for UI),active - Shapes can be deactivated but not deleted (preserve historical data)
- Clients can have one Shape and one Shape2 (both optional)
3.3.3 Personnel Assignment
- Primary Personnel: One Entra user designated as primary owner
- Additional Personnel: Zero or more Entra users with access
- Personnel dropdown populated from Entra ID users (synced or on-demand lookup)
- Assignment changes logged for audit
3.4 Task/Action Management
3.4.1 Task Definition
Tasks represent actions to be taken relative to policy renewal dates.
Task Properties:
| Field | Type | Description |
|---|---|---|
id |
UUID | Primary key |
title |
String | Task name |
description |
Text | Detailed instructions |
department |
Enum | Personal Lines, Commercial Lines, Claims, etc. |
timing |
Enum | PRE_RENEWAL or POST_RENEWAL |
days_offset |
Integer | Days before (negative) or after (positive) renewal |
due_date |
Date | Calculated: policy.expiration_date + days_offset |
status |
Enum | NOT_STARTED, IN_PROGRESS, COMPLETED, BLOCKED, CANCELLED |
priority |
Enum | LOW, MEDIUM, HIGH, URGENT |
client_id |
FK | Associated client |
policy_id |
FK | Associated policy (optional - can be client-level) |
assigned_users |
Many-to-many | One or more Entra users |
created_by |
FK | User who created |
created_at |
Timestamp | |
updated_at |
Timestamp | |
completed_at |
Timestamp | When status changed to COMPLETED |
completed_by |
FK | User who completed |
3.4.2 Task Templates
- Department-based templates: Predefined task sets per department
- Templates define: title, description, timing, days_offset, default priority
- When a policy approaches renewal, tasks can be auto-generated from templates
- Admin can manage templates per department
Example Template:
Department: Personal Lines
Template Name: "Standard Auto Renewal"
Tasks:
1. Review coverage limits (PRE_RENEWAL, -45 days)
2. Contact client for updates (PRE_RENEWAL, -30 days)
3. Submit renewal to carrier (PRE_RENEWAL, -21 days)
4. Send renewal docs to client (POST_RENEWAL, +3 days)
3.4.3 Task Assignment
- Tasks can be assigned to one or more users
- Assignment can be manual or auto-assigned to client's primary personnel
- Bulk task assignment for efficiency
- Reassignment with audit trail
3.4.4 Task Lifecycle
NOT_STARTED → IN_PROGRESS → COMPLETED
↓
BLOCKED → IN_PROGRESS → COMPLETED
Any status → CANCELLED (with reason)
3.5 Dashboards & Reporting
3.5.1 Personal Dashboard (Account Executive, Claims)
- My Tasks: Filtered to assigned tasks
- Overdue (red highlight)
- Due today
- Due this week
- Upcoming (next 30 days)
- My Clients: Quick access to assigned clients
- Quick Stats:
- Tasks completed this week/month
- Overdue task count
- Upcoming renewals count
3.5.2 Manager Dashboard
- Team Overview:
- Workload distribution (tasks per team member)
- Completion rates by user
- Overdue tasks by user
- Department Metrics:
- Tasks by status (pie/bar chart)
- Renewal timeline (upcoming 90 days)
- SLA compliance (% tasks completed on time)
- Drill-down capability: Click to see individual user details
3.5.3 Admin Dashboard
- All Manager dashboard features, plus:
- System Health:
- Last sync time, status, row counts
- Error logs
- User activity summary
- Cross-department views
3.5.4 KPIs to Track
| KPI | Calculation | Visualization |
|---|---|---|
| Tasks Completed | Count by period | Line chart (trend) |
| Tasks Overdue | Count where due_date < today AND status not COMPLETED | Number + list |
| Upcoming Renewals | Policies expiring in next 30/60/90 days | Count + calendar view |
| Workload per Person | Active tasks assigned per user | Bar chart |
| On-Time Completion Rate | (Completed on/before due_date) / Total completed | Percentage |
| Avg Days to Complete | Mean(completed_at - created_at) | Number |
4. Data Architecture
4.1 Database Schema (PostgreSQL)
-- Entra user cache (synced from Entra or populated on first login)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entra_oid VARCHAR(36) UNIQUE NOT NULL, -- Entra Object ID
email VARCHAR(255) UNIQUE NOT NULL,
display_name VARCHAR(255),
department VARCHAR(100),
is_active BOOLEAN DEFAULT true,
last_login_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Role definitions
CREATE TABLE roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(50) UNIQUE NOT NULL, -- Admin, Manager, Account Executive, Claims
description TEXT,
permissions JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMP DEFAULT NOW()
);
-- Entra Group to Role mapping
CREATE TABLE entra_group_role_mappings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entra_group_id VARCHAR(36) NOT NULL, -- Entra Group Object ID
entra_group_name VARCHAR(255),
role_id UUID REFERENCES roles(id),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(entra_group_id)
);
-- User role assignments (can override group-based roles)
CREATE TABLE user_roles (
user_id UUID REFERENCES users(id),
role_id UUID REFERENCES roles(id),
assigned_by UUID REFERENCES users(id),
assigned_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (user_id, role_id)
);
-- Shape/Designation definitions
CREATE TABLE shapes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
description TEXT,
color VARCHAR(7), -- Hex color for UI
is_active BOOLEAN DEFAULT true,
display_order INTEGER,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Clients (synced from AMS360 + custom fields)
CREATE TABLE clients (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- AMS360 synced fields
ams_customer_id VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
address_line1 VARCHAR(255),
address_line2 VARCHAR(255),
city VARCHAR(100),
state VARCHAR(50),
zip_code VARCHAR(20),
phone VARCHAR(50),
email VARCHAR(255),
producer_code VARCHAR(50),
ams_created_at TIMESTAMP,
ams_modified_at TIMESTAMP,
-- Custom fields (local only)
shape_id UUID REFERENCES shapes(id),
shape2_id UUID REFERENCES shapes(id),
primary_personnel_id UUID REFERENCES users(id),
notes TEXT,
custom_fields JSONB DEFAULT '{}',
-- Metadata
last_synced_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Additional personnel assignments for clients
CREATE TABLE client_personnel (
client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
assigned_at TIMESTAMP DEFAULT NOW(),
assigned_by UUID REFERENCES users(id),
PRIMARY KEY (client_id, user_id)
);
-- Policies (synced from AMS360)
CREATE TABLE policies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- AMS360 synced fields
ams_policy_id VARCHAR(50) UNIQUE NOT NULL,
client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
policy_number VARCHAR(100),
policy_type VARCHAR(100),
effective_date DATE,
expiration_date DATE NOT NULL, -- This is the RENEWAL DATE
carrier_name VARCHAR(255),
premium_amount DECIMAL(12, 2),
status VARCHAR(50),
ams_created_at TIMESTAMP,
ams_modified_at TIMESTAMP,
-- Metadata
last_synced_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Index for renewal date queries
CREATE INDEX idx_policies_expiration ON policies(expiration_date);
CREATE INDEX idx_policies_client ON policies(client_id);
-- Department enum
CREATE TYPE department_type AS ENUM (
'PERSONAL_LINES',
'COMMERCIAL_LINES',
'CLAIMS',
'BENEFITS',
'OTHER'
);
-- Task timing enum
CREATE TYPE task_timing AS ENUM ('PRE_RENEWAL', 'POST_RENEWAL');
-- Task status enum
CREATE TYPE task_status AS ENUM (
'NOT_STARTED',
'IN_PROGRESS',
'COMPLETED',
'BLOCKED',
'CANCELLED'
);
-- Task priority enum
CREATE TYPE task_priority AS ENUM ('LOW', 'MEDIUM', 'HIGH', 'URGENT');
-- Task templates (department-based)
CREATE TABLE task_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
department department_type NOT NULL,
timing task_timing NOT NULL,
days_offset INTEGER NOT NULL, -- Negative = before renewal, Positive = after
default_priority task_priority DEFAULT 'MEDIUM',
is_active BOOLEAN DEFAULT true,
display_order INTEGER,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Tasks (actual task instances)
CREATE TABLE tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR(255) NOT NULL,
description TEXT,
department department_type NOT NULL,
timing task_timing NOT NULL,
days_offset INTEGER NOT NULL,
due_date DATE NOT NULL,
status task_status DEFAULT 'NOT_STARTED',
priority task_priority DEFAULT 'MEDIUM',
client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
policy_id UUID REFERENCES policies(id) ON DELETE SET NULL,
template_id UUID REFERENCES task_templates(id), -- If generated from template
created_by UUID REFERENCES users(id),
completed_at TIMESTAMP,
completed_by UUID REFERENCES users(id),
cancelled_reason TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Task assignments (many-to-many)
CREATE TABLE task_assignments (
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
assigned_at TIMESTAMP DEFAULT NOW(),
assigned_by UUID REFERENCES users(id),
PRIMARY KEY (task_id, user_id)
);
-- Indexes for task queries
CREATE INDEX idx_tasks_due_date ON tasks(due_date);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_client ON tasks(client_id);
CREATE INDEX idx_tasks_department ON tasks(department);
-- Sync log
CREATE TABLE sync_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
sync_type VARCHAR(50) NOT NULL, -- 'clients', 'policies', 'full'
started_at TIMESTAMP NOT NULL,
completed_at TIMESTAMP,
status VARCHAR(50) NOT NULL, -- 'running', 'completed', 'failed'
rows_processed INTEGER DEFAULT 0,
rows_inserted INTEGER DEFAULT 0,
rows_updated INTEGER DEFAULT 0,
error_message TEXT,
triggered_by UUID REFERENCES users(id) -- NULL if scheduled
);
-- Audit log
CREATE TABLE audit_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
action VARCHAR(100) NOT NULL,
entity_type VARCHAR(50) NOT NULL,
entity_id UUID,
old_values JSONB,
new_values JSONB,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_audit_logs_user ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id);
CREATE INDEX idx_audit_logs_created ON audit_logs(created_at);
4.2 AMS360 Connection Requirements
- Connection type: SQL Server (typically)
- Credentials: Read-only service account recommended
- Network: May require VPN or firewall rules
- Tables to query: (Exact names depend on AMS360 version)
- Customer table
- Policy table
- (Reference AMS360 data dictionary for exact schema)
5. Technical Architecture
5.1 Recommended Stack
| Layer | Technology | Rationale |
|---|---|---|
| Frontend | React + TypeScript | Familiarity, strong ecosystem |
| UI Framework | Tailwind CSS + shadcn/ui | Modern, accessible components |
| State Management | TanStack Query | Server state caching, mutations |
| Backend | Node.js + Express or Next.js API Routes | JavaScript consistency |
| ORM | Prisma or Drizzle | Type-safe database access |
| Database | PostgreSQL | Robust, JSONB support |
| Auth | MSAL.js + NextAuth.js (Azure AD provider) | Native Entra integration |
| Sync Engine | Node.js service + node-mssql | AMS360 SQL connection |
| Scheduler | node-cron or external (systemd timer) | Daily sync trigger |
| Hosting | Docker on Linux server | Your standard deployment |
5.2 Authentication Flow
1. User navigates to app
2. App redirects to Entra ID login (MSAL.js)
3. User authenticates + MFA (per Entra CA policies)
4. Entra returns ID token + access token
5. Token includes group claims
6. App backend:
a. Validates token
b. Looks up entra_oid in users table (create if first login)
c. Resolves roles from group claims + user_roles table
d. Creates session
7. Frontend receives user context with permissions
5.3 Sync Engine Architecture
┌─────────────────┐ ┌──────────────────┐
│ AMS360 DB │ ──SQL── │ Sync Service │
│ (SQL Server) │ │ (Node.js) │
└─────────────────┘ └────────┬─────────┘
│
┌────────▼─────────┐
│ PostgreSQL │
│ (Local DB) │
└──────────────────┘
Sync Process:
1. Cron triggers sync at configured time
2. Sync service connects to AMS360 (read-only)
3. Queries records modified since last sync
4. Upserts into local Postgres (match on ams_*_id)
5. Logs sync results
6. Closes connections
6. Non-Functional Requirements
6.1 Security
- All traffic over HTTPS
- Entra ID MFA enforced via Conditional Access
- Database credentials in environment variables / secrets manager
- Read-only AMS360 connection
- RBAC enforced at API layer
- Audit logging for sensitive operations
- Session timeout configurable (default: 8 hours)
6.2 Performance
- Dashboard queries < 2 seconds
- Sync should complete within 1 hour for typical data volumes
- Pagination on all list endpoints (default 50, max 100)
- Database indexes on frequently queried fields
6.3 Availability
- Daily sync window during off-hours (e.g., 2 AM)
- Sync failures should not affect app availability
- Sync retry logic (3 attempts with backoff)
6.4 Data Integrity
- Foreign key constraints enforced
- Soft deletes where appropriate
- Sync never deletes local records (mark inactive if removed from AMS360)
7. MVP Scope vs. Future Phases
Phase 1 - MVP
- Entra ID authentication with group-based roles
- AMS360 daily sync (Clients + Policies)
- Client list with custom fields (Shape, Shape2, Primary Personnel)
- Basic task management (CRUD, assignment, status)
- Personal dashboard (my tasks, overdue, upcoming)
- Admin: Shape management, sync trigger
Phase 2
- Task templates per department
- Auto-generation of tasks from templates based on renewal dates
- Manager dashboard with team metrics
- Bulk operations (assign, update status)
- Email notifications for overdue tasks
Phase 3
- Advanced reporting / export
- Calendar view for renewals
- Additional personnel assignments
- Custom fields beyond Shape/Shape2
- API for external integrations
8. Open Questions / Decisions Needed
- AMS360 access: Who provides credentials? Any data restrictions?
- Departments: Are the listed departments (Personal Lines, Commercial, Claims, Benefits) complete?
- Task auto-generation: Should tasks auto-generate when policy enters renewal window, or manual trigger?
- Notifications: Email? In-app only? Teams integration?
- Mobile: Responsive web sufficient, or native app needed?
- Hosting: On-prem server or cloud (Azure)?
9. Glossary
| Term | Definition |
|---|---|
| AMS360 | Agency Management System by Vertafore - source of client/policy data |
| Shape/Shape2 | Custom client designations defined by the brokerage |
| Renewal Date | Policy expiration date - triggers workflow tasks |
| Pre-Renewal Task | Action to be completed before policy expires |
| Post-Renewal Task | Action to be completed after policy renews |
| Primary Personnel | Main user responsible for a client |
| Entra ID | Microsoft's identity platform (formerly Azure AD) |
Document Version: 1.0 Created: January 2025 Last Updated: January 2025