Approval Matrix
The approval matrix governs who can approve what across the platform. It combines authority profiles (what a user is allowed to do), referral rules (when to escalate), and approval records (the decision trail). The system is implemented across two API route groups: apps/api/src/routes/authority.ts for profile and rule management, and apps/api/src/routes/approvals.ts for the approval workflow.
Authority Profiles
Section titled “Authority Profiles”An authority profile defines the underwriting limits for a group of users. Profiles are scoped to an organization and ordered by level (1 = most junior, 10 = most senior).
Profile Fields
Section titled “Profile Fields”| Field | Type | Description |
|---|---|---|
name | string | Profile name (e.g., “Junior Underwriter”, “VP Underwriting”) |
level | 1—10 | Authority tier — higher levels can approve larger risks |
maxTiv | number | Maximum total insured value this profile can bind |
maxLimit | number | Maximum policy limit this profile can bind |
maxPremium | number | Maximum premium this profile can bind |
authorizedLobs | string[] | Lines of business the profile can underwrite |
prohibitedStates | string[] | States where this profile cannot write |
canOverride | boolean | Whether violations can be overridden (senior authority) |
constraints | JSON | Additional org-specific constraints |
CRUD Operations
Section titled “CRUD Operations”# List all active profiles for an orgGET /v1/authority/profiles?orgId={orgId}
# Include inactive profilesGET /v1/authority/profiles?orgId={orgId}&includeInactive=true
# Create a new profilePOST /v1/authority/profiles{ "orgId": "uuid", "name": "Senior Underwriter", "level": 3, "maxTiv": 5000000, "maxLimit": 2000000, "maxPremium": 100000, "authorizedLobs": ["commercial_auto", "general_liability"], "prohibitedStates": []}
# Update a profilePATCH /v1/authority/profiles/:id{ "maxPremium": 150000, "canOverride": true}User Assignment
Section titled “User Assignment”Each user is assigned to exactly one active authority profile at a time. Assigning a user to a new profile automatically deactivates their previous assignment.
POST /v1/authority/profiles/:id/assign{ "userId": "uuid", "effectiveFrom": "2026-03-24T00:00:00Z", "effectiveTo": "2026-12-31T23:59:59Z", "notes": "Promoted to senior tier"}Assignments have optional effectiveFrom and effectiveTo dates. Expired assignments are automatically excluded from authority checks.
Profile Detail
Section titled “Profile Detail”The GET /v1/authority/profiles/:id endpoint returns the profile with its currently assigned users (userId, name, email, assignedAt, assignedBy).
Authority Check
Section titled “Authority Check”The POST /v1/authority/check endpoint evaluates whether a specific user can perform a given action in context. This is called during submission processing to determine whether the underwriter can bind directly or must refer.
Request
Section titled “Request”{ "userId": "uuid", "action": "bind", "context": { "tiv": 3500000, "premium": 75000, "limit": 1000000, "lob": "commercial_auto", "state": "FL" }}Evaluation Logic
Section titled “Evaluation Logic”The check evaluates five dimensions against the user’s assigned authority profile:
| Check | Condition | Violation Message |
|---|---|---|
| TIV | context.tiv > profile.maxTiv | ”TIV $X exceeds limit of $Y” |
| Premium | context.premium > profile.maxPremium | ”Premium $X exceeds limit of $Y” |
| Limit | context.limit > profile.maxLimit | ”Limit $X exceeds authority of $Y” |
| LOB | context.lob not in profile.authorizedLobs | ”LOB ‘X’ not authorized” |
| State | context.state in profile.prohibitedStates | ”State ‘X’ is prohibited” |
Decision Rules
Section titled “Decision Rules”- Allowed: No violations found.
- Allowed (override): Violations exist, but the profile has
canOverride: true. - Allowed (refer): The action is
refer— referrals are always permitted regardless of violations. - Denied: Violations exist and the profile cannot override.
Response
Section titled “Response”{ "data": { "allowed": false, "violations": ["TIV $3,500,000 exceeds limit of $2,000,000"], "authority": { "level": 2, "name": "Junior Underwriter", "canOverride": false, "maxTiv": 2000000, "maxPremium": 50000, "maxLimit": 1000000 } }}Authority Matrix View
Section titled “Authority Matrix View”The GET /v1/authority/matrix?orgId={orgId} endpoint returns the complete authority grid for an organization — all active profiles with their assigned users and the full set of lines of business in use. This powers the matrix view in the admin UI.
{ "data": { "profiles": [ { "level": 1, "name": "Assistant UW", "maxTiv": 1000000, "assigned_users": [...] }, { "level": 2, "name": "Underwriter", "maxTiv": 3000000, "assigned_users": [...] }, { "level": 3, "name": "Senior UW", "maxTiv": 10000000, "assigned_users": [...] } ], "lineOfBusinesses": ["commercial_auto", "general_liability", "cargo"] }}Referral Rules
Section titled “Referral Rules”Referral rules define the conditions under which a submission is automatically escalated. Rules are evaluated in priority order (lower number = higher priority) and can trigger referral, auto-approval, or auto-rejection.
Rule Fields
Section titled “Rule Fields”| Field | Type | Description |
|---|---|---|
name | string | Rule name |
priority | number | Evaluation order (lower = first) |
conditions | JSON | Condition object evaluated against submission data |
action | enum | refer, auto_approve, or auto_reject |
targetAuthorityLevel | number | Minimum authority level required to handle the referral |
targetProfileId | uuid | Specific profile to route to (optional) |
notifyChannels | array | Notification channels: slack, email, in_app |
slackChannel | string | Slack channel for notifications |
notifyEmails | array | Email addresses to notify |
escalationEnabled | boolean | Whether to auto-escalate after timeout |
escalationHours | number | Hours before escalation (default: 24) |
escalationLevel | number | Authority level to escalate to |
Example: High-TIV Referral Rule
Section titled “Example: High-TIV Referral Rule”POST /v1/authority/rules{ "orgId": "uuid", "name": "High TIV Referral", "priority": 10, "conditions": { "tiv": { "$gt": 5000000 } }, "action": "refer", "targetAuthorityLevel": 4, "notifyChannels": ["slack", "email"], "slackChannel": "#uw-referrals", "notifyEmails": ["senior-uw@mga.com"], "escalationEnabled": true, "escalationHours": 12, "escalationLevel": 5}Rules are soft-deleted — DELETE /v1/authority/rules/:id sets is_active to false.
Approval Workflow
Section titled “Approval Workflow”When a submission exceeds an underwriter’s authority or a referral rule triggers, an approval record is created in the approvals table.
Approval Types
Section titled “Approval Types”| Type | Entity Type | Trigger |
|---|---|---|
bind | submission | Bind request exceeds underwriter’s authority limits |
siu_referral | claim | Special Investigations Unit referral |
reserve_change | claim | Reserve change exceeds adjuster’s authority |
bordereaux_submit | bordereaux | Bordereaux submission requires sign-off |
Creating an Approval
Section titled “Creating an Approval”Approvals are created programmatically by the system (role: system or org_admin) when an action requires escalation:
{ "orgId": "uuid", "type": "bind", "entityType": "submission", "entityId": "uuid", "workflowId": "workflow-instance-id", "requestedBy": "uuid", "metadata": { "submissionId": "uuid", "premium": 120000 }, "expiresAt": "2026-04-01T00:00:00Z"}Decision Endpoint
Section titled “Decision Endpoint”POST /v1/approvals/:id/decide{ "decision": "approved", "decidedBy": "uuid", "notes": "Reviewed loss history, acceptable risk"}The endpoint enforces:
- Idempotency: Already-decided approvals return
409. - Expiry: Expired approvals return
409.
Side Effects
Section titled “Side Effects”Bind rejection: When a bind approval is rejected, the system reverts both the policy and the linked submission to declined status.
Workflow resumption: If the approval is tied to a Cloudflare Workflow instance (workflowId), the decision event is sent to the workflow to resume processing:
bindapprovals send toBIND_WORKFLOWwith event typeunderwriter-bind-approval.reserve_changeapprovals send toCLAIM_WORKFLOWwith event typeclaim.reserve_approved.
API Reference
Section titled “API Reference”Authority Endpoints
Section titled “Authority Endpoints”| Method | Path | Description |
|---|---|---|
GET | /v1/authority/profiles | List authority profiles |
POST | /v1/authority/profiles | Create authority profile |
GET | /v1/authority/profiles/:id | Get profile with assigned users |
PATCH | /v1/authority/profiles/:id | Update profile limits |
POST | /v1/authority/profiles/:id/assign | Assign user to profile |
GET | /v1/authority/rules | List referral rules |
POST | /v1/authority/rules | Create referral rule |
PATCH | /v1/authority/rules/:id | Update referral rule |
DELETE | /v1/authority/rules/:id | Soft-delete referral rule |
GET | /v1/authority/matrix | Full authority matrix grid |
POST | /v1/authority/check | Check user authority for action |
Approval Endpoints
Section titled “Approval Endpoints”| Method | Path | Description |
|---|---|---|
GET | /v1/approvals | List approvals (paginated, filtered by status) |
POST | /v1/approvals | Create approval record |
POST | /v1/approvals/:id/decide | Approve or reject |
Related
Section titled “Related”- Submission Work Queue — Triage scoring and swimlane routing
- Underwriting — Rules engine and risk evaluation
- Claims — Reserve change approvals