Build an insurance claims management tool with V0 using Next.js, Supabase for claims data, and a PostgreSQL state machine for status transitions. You'll create a claims submission wizard, adjuster review queue, status timeline, and management analytics — all in about 2-4 hours without touching a terminal.
What you're building
Insurance claims management tracks the lifecycle of a claim from submission through investigation to resolution. Policyholders submit claims with supporting documents, adjusters review and process them through defined stages, and managers monitor volumes and payouts.
V0 generates the multi-step submission wizard, review queue, and analytics dashboard from prompts. Supabase handles claims data, document storage, and role-based access control.
The architecture uses Next.js App Router with a client component for the submission wizard, Server Components for the review queue and analytics, a PostgreSQL function implementing a state machine for valid status transitions, Server Actions for claim processing, and Supabase Storage for supporting documents.
Final result
An insurance claims tool with submission wizard, adjuster review queue, status timeline enforcement, document management, internal notes, and management analytics.
Tech stack
Prerequisites
- A V0 account (Premium recommended for the complex multi-page app)
- A Supabase project with Storage enabled for claim documents
- Basic understanding of insurance claims (submit, review, approve/deny, pay)
- Familiarity with role-based access (policyholder, adjuster, manager)
Build steps
Set up the project and claims database schema
Open V0 and create a new project. Connect Supabase via the Connect panel. Create the schema for policyholders, claims, documents, notes, and status tracking.
1// Paste this prompt into V0's AI chat:2// Build an insurance claims tool. Create a Supabase schema with:3// 1. policyholders: id (uuid PK), user_id (uuid FK to auth.users), policy_number (text unique), full_name (text), email (text), phone (text), address (text), policy_type (text check in 'auto','home','health','life','business'), policy_start (date), policy_end (date), coverage_limit_cents (bigint)4// 2. claims: id (uuid PK), policyholder_id (uuid FK to policyholders), claim_number (text unique), type (text check in 'auto_collision','auto_theft','home_damage','home_theft','health_medical','health_dental','life','business_liability'), description (text), incident_date (date), incident_location (text), estimated_amount_cents (bigint), approved_amount_cents (bigint), status (text default 'submitted' check in 'submitted','under_review','investigation','approved','partially_approved','denied','paid','closed'), priority (text default 'normal' check in 'low','normal','high','urgent'), assigned_adjuster_id (uuid FK to auth.users nullable), submitted_at (timestamptz), resolved_at (timestamptz)5// 3. claim_documents: id (uuid PK), claim_id (uuid FK to claims), title (text), file_url (text), document_type (text check in 'photo','police_report','medical_record','receipt','estimate','other'), uploaded_by (uuid FK to auth.users), uploaded_at (timestamptz)6// 4. claim_notes: id (uuid PK), claim_id (uuid FK to claims), author_id (uuid FK to auth.users), content (text), is_internal (boolean default true), created_at (timestamptz)7// 5. claim_status_log: id (uuid PK), claim_id (uuid FK to claims), from_status (text), to_status (text), changed_by (uuid FK to auth.users), reason (text), created_at (timestamptz)8// Add RLS: policyholders see their own claims, adjusters see assigned claims, managers see all.Pro tip: Queue the policyholder submission flow, adjuster review interface, and manager analytics dashboard as three separate prompt sequences using V0's prompt queuing feature.
Expected result: Database schema created with 5 tables, RLS policies for role-based access, and status tracking for complete claim audit trails.
Build the multi-step claim submission wizard
Create the claim submission page with a step-by-step wizard for entering incident details, uploading supporting documents, and reviewing before submission.
1// Paste this prompt into V0's AI chat:2// Build a claim submission wizard at app/claims/new/page.tsx as a 'use client' component.3// Requirements:4// - Stepper showing 4 steps: Incident Details → Documents → Review → Confirmation5// - Step 1 (Incident Details): Select for claim type, Input for incident_date (date picker), Textarea for description, Input for incident_location, Input for estimated_amount (currency formatted)6// - Step 2 (Documents): File upload zone accepting multiple files, Select for document_type per file (photo, police_report, medical_record, receipt, estimate, other), Preview thumbnails for uploaded images7// - Step 3 (Review): Summary Card showing all entered data with edit buttons per section8// - Step 4 (Confirmation): success message with claim_number display9// - Zod validation: incident_date <= today, estimated_amount <= coverage_limit, at least one document required for auto/home claims10// - Server Action that: generates claim_number (CLM-{year}-{sequence}), inserts claim, uploads documents to Supabase Storage bucket 'claim-documents', inserts claim_documents rows, creates initial claim_status_log entry11// - Navigate to /claims/[id] after submission12// - Use Card for step content areas and Badge for document type labelsExpected result: Policyholders can submit claims through a guided 4-step wizard with document uploads, validation, and automatic claim number generation.
Build the claim detail page with status timeline
Create the claim detail page showing the full claim lifecycle with status timeline, documents, internal notes, and action buttons.
1// Paste this prompt into V0's AI chat:2// Build claim detail at app/claims/[id]/page.tsx.3// Requirements:4// - Header: claim_number, type Badge, status Badge (color-coded), priority Badge5// - Status timeline: vertical Stepper showing each status_log entry with timestamp, author, and reason6// - Tabs component with sections:7// 1. Overview: incident details Card, estimated vs approved amount, assigned adjuster8// 2. Documents: gallery grid of uploaded documents with download Button per file, document_type Badge, "Upload Additional" Button9// 3. Notes: threaded conversation of claim_notes, Textarea to add new note, Switch for is_internal (visible only to adjusters/managers)10// 4. History: full status_log Table with from→to transition, changed_by, reason, timestamp11// - Action buttons (role-dependent):12// - Adjuster: "Approve", "Deny", "Request Investigation" buttons that open Dialog with reason Textarea and approved_amount Input (for approve)13// - Manager: "Reassign" Button with adjuster Select14// - AlertDialog for deny action requiring a reason15// - Server Actions for status transitions, note creation, and document uploadExpected result: The claim detail page shows a complete timeline of status changes, organized document gallery, internal notes thread, and role-appropriate action buttons.
Create the state machine for status transitions
Build the PostgreSQL function that validates and enforces the claims status transition rules, preventing invalid status changes.
1// Paste this prompt into V0's AI chat:2// Build the status transition system:3// 1. Create a PostgreSQL function transition_claim_status(claim_id uuid, new_status text, changed_by uuid, reason text):4// - Fetch current status with FOR UPDATE lock5// - Validate against state machine rules:6// submitted → under_review only7// under_review → investigation, approved, partially_approved, denied8// investigation → approved, partially_approved, denied9// approved → paid10// partially_approved → paid11// denied → closed12// paid → closed13// - If invalid transition, RAISE EXCEPTION 'Invalid status transition from X to Y'14// - Insert into claim_status_log (from_status, to_status, changed_by, reason)15// - Update claims.status = new_status16// - If new_status in ('approved','denied','paid','closed'), set resolved_at = now()17// - Return the updated claim18//19// 2. Create API route at app/api/claims/[id]/status/route.ts:20// - PATCH handler that validates role permissions and calls the RPC21// - Adjusters can transition to under_review, investigation, approved, denied22// - Managers can do all transitions23// - Policyholders cannot change status24// - Use Zod to validate input: new_status, reason (required for deny)Pro tip: The FOR UPDATE row lock in the PostgreSQL function prevents race conditions — even if two adjusters try to transition the same claim simultaneously, only one will succeed.
Expected result: The state machine enforces valid transitions at the database level. Invalid status changes raise exceptions even if called directly, ensuring claims follow the proper workflow.
Build the adjuster queue and management dashboard
Create the claims list with filtering, the adjuster's work queue, and the management analytics dashboard.
1// Paste this prompt into V0's AI chat:2// Build three pages:3// 1. app/claims/page.tsx — claims list:4// - Table with columns: claim_number, type Badge, policyholder name, status Badge, priority Badge, submitted date, assigned adjuster5// - Column sorting on submitted_at and priority6// - Select filters: status, priority, claim type7// - Search Input for claim_number or policyholder name8// - Click row to navigate to /claims/[id]9//10// 2. app/page.tsx — role-based dashboard:11// - Policyholder view: my claims list with status indicators, "File New Claim" Button12// - Adjuster view: assigned claims queue sorted by priority DESC, unassigned claims needing pickup13// - Manager view: KPI Cards (total claims, avg processing days, total payouts), claims volume BarChart by month, status distribution PieChart14//15// 3. app/admin/page.tsx — management analytics:16// - Recharts BarChart: claims volume by month with status stacking17// - LineChart: average processing time trend18// - Cards: total payouts this month, denial rate, adjuster workload distribution19// - Table: top 10 highest-value open claims20// - Select for time period and claim type filtering21// - Wrap all charts in 'use client' componentsExpected result: Adjusters see their work queue sorted by priority. Managers see analytics with claims volume, processing times, and payout trends. Policyholders see their own claims.
Complete code
1import { createClient } from '@/lib/supabase/server'2import { NextRequest, NextResponse } from 'next/server'3import { z } from 'zod'45const schema = z.object({6 newStatus: z.enum([7 'under_review', 'investigation', 'approved',8 'partially_approved', 'denied', 'paid', 'closed',9 ]),10 reason: z.string().min(1).max(1000),11 approvedAmount: z.number().int().nonnegative().optional(),12})1314export async function PATCH(15 request: NextRequest,16 { params }: { params: Promise<{ id: string }> }17) {18 const { id } = await params19 const supabase = await createClient()20 const { data: { user } } = await supabase.auth.getUser()21 if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })2223 const body = await request.json()24 const { newStatus, reason, approvedAmount } = schema.parse(body)2526 if (approvedAmount && newStatus === 'approved') {27 await supabase28 .from('claims')29 .update({ approved_amount_cents: approvedAmount })30 .eq('id', id)31 }3233 const { data, error } = await supabase.rpc('transition_claim_status', {34 claim_id: id,35 new_status: newStatus,36 changed_by: user.id,37 reason,38 })3940 if (error) {41 if (error.message.includes('Invalid status transition')) {42 return NextResponse.json({ error: error.message }, { status: 422 })43 }44 return NextResponse.json({ error: error.message }, { status: 500 })45 }4647 return NextResponse.json({ success: true, claim: data })48}Customization ideas
Add fraud detection scoring
Build a risk scoring system that flags claims based on amount, frequency, claim type patterns, and known fraud indicators.
Add SLA tracking
Set target resolution times per claim type and priority, with automated escalation when SLAs are at risk of being breached.
Add email notifications
Send automated emails to policyholders at each status transition and to adjusters when claims are assigned using Resend.
Add claim templates
Create pre-filled claim forms for common scenarios (fender bender, water damage, theft) that reduce submission time.
Add document OCR
Use an AI service to extract text from uploaded receipts and police reports, auto-filling claim fields from the documents.
Common pitfalls
Pitfall: Allowing arbitrary status changes without validation
How to avoid: Implement a PostgreSQL function that validates each transition against a defined state machine and raises an exception on invalid changes.
Pitfall: Not logging status transitions
How to avoid: Automatically insert into claim_status_log within the transition function, capturing from_status, to_status, changed_by, reason, and timestamp.
Pitfall: Storing monetary amounts as floats
How to avoid: Store all amounts as bigint in cents (estimated_amount_cents, approved_amount_cents) and format for display client-side.
Pitfall: Not restricting document access by role
How to avoid: Configure Supabase Storage bucket RLS to restrict reads to the claim owner and assigned adjuster only.
Best practices
- Implement status transitions as a PostgreSQL function with a state machine to enforce valid workflow at the database level.
- Log every status change in a claim_status_log table for complete audit trails required by insurance regulations.
- Store all monetary values as bigint in cents to avoid floating-point rounding errors in financial calculations.
- Use FOR UPDATE row locks in the transition function to prevent race conditions during concurrent claim processing.
- Configure Supabase Storage bucket RLS to restrict document access by role — policyholders see their own, adjusters see assigned claims.
- Use Clerk custom claims for role-based access (policyholder, adjuster, manager) with middleware.ts enforcement.
- Validate that estimated_amount does not exceed coverage_limit in the submission Server Action using Zod.
AI prompts to try
Copy these prompts to build this project faster.
I'm building an insurance claims tool with Next.js and Supabase. I need a PostgreSQL function that implements a state machine for claim status transitions — validating that each transition follows allowed paths (submitted→under_review→investigation|approved|denied, etc.), logging the change, and locking the row to prevent concurrent updates. Show me the function SQL, the valid transitions map, and how to call it from a Next.js API route.
Build the claim submission wizard for an insurance claims tool. Create a 'use client' component with a 4-step Stepper: Step 1 has Select for claim type, date picker for incident_date, Textarea for description, Input for estimated_amount. Step 2 has a file upload zone with document_type Select per file. Step 3 shows a review Card with all entered data. Step 4 shows confirmation with claim_number. Validate with Zod that incident_date is not in the future and estimated_amount is within coverage_limit.
Frequently asked questions
Can I build this with the free V0 plan?
The core submission wizard and claims list fit within the free tier. V0 Premium is recommended for the full multi-page system with analytics dashboards.
How does the status state machine work?
A PostgreSQL function validates each transition against a predefined map of allowed paths. For example, a claim in submitted status can only move to under_review. Attempting an invalid transition raises a database exception.
What authentication should I use?
Clerk is recommended for role-based access. Set custom claims (policyholder, adjuster, manager) in the Clerk Dashboard metadata and enforce them in middleware.ts and Server Actions.
How are documents secured?
Claim documents are stored in a Supabase Storage bucket with RLS policies restricting access to the claim owner and the assigned adjuster. Internal notes are filtered by role on the server side.
Can I add email notifications?
Yes. Add a Resend integration that sends emails at each status transition. Call Resend from within the PostgreSQL transition function trigger or from the API route after a successful transition.
How do I deploy?
Publish via V0's Share menu. Set SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, and Clerk credentials in the Vars tab. Configure the claim-documents Storage bucket and RLS policies.
Can RapidDev help build a custom claims platform?
Yes. RapidDev has built 600+ apps including insurance platforms with fraud detection, SLA tracking, and regulatory compliance features. Book a free consultation to discuss your needs.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation