Skip to main content
RapidDev - Software Development Agency

How to Build a Insurance Claims Tool with Lovable

Build a full insurance claims management tool in Lovable by setting up Supabase tables for policies, claims, and documents, then creating a multi-step claims workflow with role-based access for claimants, adjusters, and admins. You get a DataTable for claims, a Sheet panel with claim timeline, file uploads, and automated email notifications via Edge Functions — all without writing a line of backend code manually.

What you'll build

  • Policy and claims database with status lifecycle (submitted, under_review, approved, rejected, closed)
  • Role-based access control for claimants, adjusters, and admins via Supabase RLS
  • DataTable with filtering and sorting for all claims
  • Slide-out Sheet panel showing full claim detail with activity timeline
  • Multi-file document upload attached to each claim
  • Edge Function for status transition validation and email notifications
  • Admin dashboard showing claim stats and pending review queue
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced18 min read3-4 hoursLovable Pro or higherApril 2026RapidDev Engineering Team
TL;DR

Build a full insurance claims management tool in Lovable by setting up Supabase tables for policies, claims, and documents, then creating a multi-step claims workflow with role-based access for claimants, adjusters, and admins. You get a DataTable for claims, a Sheet panel with claim timeline, file uploads, and automated email notifications via Edge Functions — all without writing a line of backend code manually.

What you're building

An insurance claims management tool is a web application that lets claimants submit and track insurance claims, while adjusters review, document, and action them through a defined status lifecycle. This kind of tool replaces email-based claims handling and spreadsheet tracking with a structured, auditable workflow.

The core of this build is a multi-step claims process: a claim moves from submitted to under_review, then either approved or rejected, and finally closed. Each transition is validated by a Supabase Edge Function that enforces business rules (e.g., an adjuster can't approve without a required document) and sends email notifications to relevant parties.

Role-based access is enforced at the database level using Row Level Security. Claimants only see their own policies and claims. Adjusters see claims assigned to them. Admins have full visibility. This separation means your data is secure even if application logic has a bug.

Final result

A production-ready insurance claims portal with multi-role access, document management, status workflows, and automated notifications.

Tech stack

LovableFrontend builder and code generation
Supabase PostgreSQLDatabase for policies, claims, documents, notes
Supabase AuthUser authentication with role management
Supabase StorageEncrypted document and file storage
Supabase Edge FunctionsStatus transition validation and email notifications
shadcn/uiDataTable, Sheet, Form, Badge, Dialog components
React Hook Form + ZodForm validation for claim submissions

Prerequisites

  • A Lovable account (Pro plan required for Edge Functions and multiple Supabase tables)
  • A Supabase project created at supabase.com with the project URL and anon key ready
  • An email service API key for notifications (e.g., Resend — free tier works)
  • Basic understanding of how Supabase Row Level Security works
  • A Lovable project already created and connected to your Supabase project

Build steps

1

Set up the database schema with RLS policies

Start by creating all four tables in the Supabase SQL Editor. The schema includes policies, claims, claim_documents, and claim_notes. Each table needs RLS enabled immediately. Define the status enum first so the lifecycle is enforced at the database level, not just the UI.

supabase-schema.sql
1-- Run this in Supabase SQL Editor (Dashboard SQL Editor New query)
2
3CREATE TYPE claim_status AS ENUM ('submitted', 'under_review', 'approved', 'rejected', 'closed');
4CREATE TYPE user_role AS ENUM ('claimant', 'adjuster', 'admin');
5
6CREATE TABLE profiles (
7 id UUID REFERENCES auth.users PRIMARY KEY,
8 full_name TEXT NOT NULL,
9 role user_role NOT NULL DEFAULT 'claimant',
10 email TEXT NOT NULL
11);
12
13CREATE TABLE policies (
14 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
15 claimant_id UUID REFERENCES profiles(id) NOT NULL,
16 policy_number TEXT UNIQUE NOT NULL,
17 coverage_type TEXT NOT NULL,
18 coverage_amount DECIMAL(12,2) NOT NULL,
19 active BOOLEAN DEFAULT true,
20 created_at TIMESTAMPTZ DEFAULT now()
21);
22
23CREATE TABLE claims (
24 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
25 policy_id UUID REFERENCES policies(id) NOT NULL,
26 claimant_id UUID REFERENCES profiles(id) NOT NULL,
27 adjuster_id UUID REFERENCES profiles(id),
28 status claim_status DEFAULT 'submitted',
29 incident_date DATE NOT NULL,
30 description TEXT NOT NULL,
31 claimed_amount DECIMAL(12,2) NOT NULL,
32 approved_amount DECIMAL(12,2),
33 created_at TIMESTAMPTZ DEFAULT now(),
34 updated_at TIMESTAMPTZ DEFAULT now()
35);
36
37CREATE TABLE claim_documents (
38 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
39 claim_id UUID REFERENCES claims(id) ON DELETE CASCADE NOT NULL,
40 uploaded_by UUID REFERENCES profiles(id) NOT NULL,
41 file_name TEXT NOT NULL,
42 storage_path TEXT NOT NULL,
43 file_type TEXT NOT NULL,
44 created_at TIMESTAMPTZ DEFAULT now()
45);
46
47CREATE TABLE claim_notes (
48 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
49 claim_id UUID REFERENCES claims(id) ON DELETE CASCADE NOT NULL,
50 author_id UUID REFERENCES profiles(id) NOT NULL,
51 note TEXT NOT NULL,
52 is_internal BOOLEAN DEFAULT false,
53 created_at TIMESTAMPTZ DEFAULT now()
54);
55
56-- Enable RLS on all tables
57ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
58ALTER TABLE policies ENABLE ROW LEVEL SECURITY;
59ALTER TABLE claims ENABLE ROW LEVEL SECURITY;
60ALTER TABLE claim_documents ENABLE ROW LEVEL SECURITY;
61ALTER TABLE claim_notes ENABLE ROW LEVEL SECURITY;
62
63-- Profiles: users see own, admins see all
64CREATE POLICY "Users see own profile" ON profiles FOR SELECT USING (auth.uid() = id);
65CREATE POLICY "Admins see all profiles" ON profiles FOR SELECT USING (
66 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
67);
68CREATE POLICY "Users insert own profile" ON profiles FOR INSERT WITH CHECK (auth.uid() = id);
69
70-- Claims: claimants see own, adjusters see assigned, admins see all
71CREATE POLICY "Claimants see own claims" ON claims FOR SELECT USING (claimant_id = auth.uid());
72CREATE POLICY "Adjusters see assigned claims" ON claims FOR SELECT USING (adjuster_id = auth.uid());
73CREATE POLICY "Admins see all claims" ON claims FOR ALL USING (
74 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
75);
76CREATE POLICY "Claimants insert claims" ON claims FOR INSERT WITH CHECK (claimant_id = auth.uid());
77CREATE POLICY "Adjusters update assigned claims" ON claims FOR UPDATE USING (adjuster_id = auth.uid());
78
79-- Documents and notes follow claim access
80CREATE POLICY "Claim participants see documents" ON claim_documents FOR SELECT USING (
81 EXISTS (SELECT 1 FROM claims WHERE id = claim_id AND (claimant_id = auth.uid() OR adjuster_id = auth.uid()))
82 OR EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
83);
84CREATE POLICY "Claim participants insert documents" ON claim_documents FOR INSERT WITH CHECK (
85 EXISTS (SELECT 1 FROM claims WHERE id = claim_id AND (claimant_id = auth.uid() OR adjuster_id = auth.uid()))
86);
87
88CREATE POLICY "Non-internal notes visible to claimants" ON claim_notes FOR SELECT USING (
89 (is_internal = false AND EXISTS (SELECT 1 FROM claims WHERE id = claim_id AND claimant_id = auth.uid()))
90 OR EXISTS (SELECT 1 FROM claims WHERE id = claim_id AND adjuster_id = auth.uid())
91 OR EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
92);
93CREATE POLICY "Participants insert notes" ON claim_notes FOR INSERT WITH CHECK (
94 EXISTS (SELECT 1 FROM claims WHERE id = claim_id AND (claimant_id = auth.uid() OR adjuster_id = auth.uid()))
95 OR EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
96);

Pro tip: Run the CREATE TYPE statements first before the CREATE TABLE statements. If you need to rerun, drop tables in reverse dependency order before dropping the types.

Expected result: All five tables appear in Supabase Dashboard under Table Editor with RLS enabled (shown by the lock icon). The SQL Editor shows no errors.

2

Prompt Lovable to build the claims DataTable and layout

Now ask Lovable to generate the main claims list page. The DataTable should show claim ID, policy number, status badge, claimed amount, incident date, and assigned adjuster. Include column filtering and a search input. The layout should have a sidebar with navigation for Claims, Policies, and Admin sections depending on user role.

prompt.txt
1Build an insurance claims management dashboard with the following:
2
31. Main layout with a left sidebar (using shadcn Sidebar) showing navigation items:
4 - "My Claims" for claimants
5 - "Assigned Claims" for adjusters
6 - "All Claims" for admins
7 - "Policies" section
8 - Admin-only "Users" section
9
102. Claims list page (/claims) with a shadcn DataTable showing columns:
11 - Claim ID (short UUID, monospace)
12 - Policy Number
13 - Status (shadcn Badge with color: submitted=blue, under_review=yellow, approved=green, rejected=red, closed=gray)
14 - Incident Date (formatted)
15 - Claimed Amount (formatted currency)
16 - Adjuster (name or "Unassigned")
17 - Actions column with "View" button
18
193. Search input above the table filtering by claim ID or policy number
204. Status filter dropdown (All, Submitted, Under Review, Approved, Rejected, Closed)
21
22Fetch data from Supabase table 'claims' joined with 'policies' and 'profiles'. Use the Supabase client from src/integrations/supabase/client.ts. Show skeleton loading state while fetching.

Pro tip: If Lovable generates the DataTable without server-side filtering, ask it to add a useEffect that re-fetches with Supabase .filter() when the status dropdown changes, instead of filtering in JavaScript.

Expected result: The claims list page renders with the DataTable, showing loading skeletons then populated rows. Status badges display in the correct colors. The search input filters rows as you type.

3

Build the claim detail Sheet with timeline and file upload

The claim detail view is a full-width slide-out Sheet panel that opens when clicking any claim row. It shows all claim fields, an activity timeline combining status changes and notes, and a multi-file upload section for claim documents. Adjusters see an additional section with status transition controls.

prompt.txt
1Add a claim detail Sheet panel that opens when clicking any row in the claims DataTable:
2
31. Sheet slides in from the right, 600px wide
42. Sheet header: Claim ID, status Badge, close button
53. Tabs inside the Sheet (shadcn Tabs):
6 - "Details" tab: all claim fields in a two-column grid (policy number, incident date, description, claimed amount, approved amount if exists)
7 - "Documents" tab: list of uploaded files with download links + multi-file upload dropzone
8 - "Timeline" tab: chronological list of status changes and notes (internal notes shown only to adjusters/admins with a lock icon badge)
9
104. For adjusters and admins, add a sticky footer inside the Sheet with:
11 - "Status" select showing valid next transitions only
12 - "Add Note" textarea with "Internal" checkbox
13 - "Save Changes" button
14
155. Document upload: accept PDF, PNG, JPG. Upload to Supabase Storage bucket 'claim-documents' with path: {claim_id}/{timestamp}-{filename}. Insert record into claim_documents table after successful upload.
16
17Query claim_documents and claim_notes for the selected claim ID. Show shadcn Skeleton while loading each tab.

Expected result: Clicking a claim row opens the Sheet. All three tabs load correctly. Files can be dragged into the upload zone and appear in the Documents tab after upload. Timeline shows notes in chronological order.

4

Create the Edge Function for status transitions and email notifications

Status changes need validation (e.g., can't approve without a document, can't reopen a closed claim) and should trigger email notifications. Create a Supabase Edge Function that enforces these rules and sends emails via Resend. Add the Resend API key to Lovable's Secrets.

supabase/functions/transition-claim-status/index.ts
1// supabase/functions/transition-claim-status/index.ts
2import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
3import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
4
5const corsHeaders = {
6 'Access-Control-Allow-Origin': '*',
7 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
8};
9
10const VALID_TRANSITIONS: Record<string, string[]> = {
11 submitted: ['under_review'],
12 under_review: ['approved', 'rejected'],
13 approved: ['closed'],
14 rejected: ['closed'],
15 closed: [],
16};
17
18serve(async (req) => {
19 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders });
20
21 try {
22 const supabase = createClient(
23 Deno.env.get('SUPABASE_URL')!,
24 Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
25 );
26
27 const { claim_id, new_status, note, approved_amount } = await req.json();
28
29 const { data: claim, error: fetchError } = await supabase
30 .from('claims')
31 .select('*, policies(*), profiles!claims_claimant_id_fkey(*)')
32 .eq('id', claim_id)
33 .single();
34
35 if (fetchError || !claim) throw new Error('Claim not found');
36
37 const validNext = VALID_TRANSITIONS[claim.status] || [];
38 if (!validNext.includes(new_status)) {
39 return new Response(JSON.stringify({ error: `Cannot transition from ${claim.status} to ${new_status}` }), {
40 status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' }
41 });
42 }
43
44 if (new_status === 'approved') {
45 const { count } = await supabase
46 .from('claim_documents')
47 .select('id', { count: 'exact' })
48 .eq('claim_id', claim_id);
49 if ((count || 0) === 0) {
50 return new Response(JSON.stringify({ error: 'Cannot approve claim without at least one document' }), {
51 status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' }
52 });
53 }
54 }
55
56 const updateData: Record<string, unknown> = { status: new_status, updated_at: new Date().toISOString() };
57 if (new_status === 'approved' && approved_amount) updateData.approved_amount = approved_amount;
58
59 await supabase.from('claims').update(updateData).eq('id', claim_id);
60
61 if (note) {
62 const authHeader = req.headers.get('Authorization')!;
63 const { data: { user } } = await supabase.auth.getUser(authHeader.replace('Bearer ', ''));
64 await supabase.from('claim_notes').insert({
65 claim_id, author_id: user!.id, note,
66 is_internal: new_status === 'under_review'
67 });
68 }
69
70 const resendKey = Deno.env.get('RESEND_API_KEY');
71 if (resendKey && claim.profiles?.email) {
72 await fetch('https://api.resend.com/emails', {
73 method: 'POST',
74 headers: { Authorization: `Bearer ${resendKey}`, 'Content-Type': 'application/json' },
75 body: JSON.stringify({
76 from: 'claims@yourdomain.com',
77 to: claim.profiles.email,
78 subject: `Claim ${claim_id.slice(0, 8)} status updated to ${new_status}`,
79 html: `<p>Your claim status has been updated to <strong>${new_status}</strong>.</p>${note ? `<p>Note: ${note}</p>` : ''}`
80 })
81 });
82 }
83
84 return new Response(JSON.stringify({ success: true, new_status }), {
85 headers: { ...corsHeaders, 'Content-Type': 'application/json' }
86 });
87 } catch (err) {
88 return new Response(JSON.stringify({ error: err.message }), {
89 status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' }
90 });
91 }
92});

Pro tip: Add the Resend API key in Lovable via Cloud tab → Secrets → Add Secret with key name RESEND_API_KEY. Edge Functions automatically receive SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY — you don't need to add those manually.

Expected result: The Edge Function deploys successfully. Status transitions that violate business rules return a 400 error message. Valid transitions update the claim and trigger an email to the claimant.

5

Build the claim submission form for claimants

Claimants need a guided multi-step form to submit new claims. Step 1 selects a policy. Step 2 fills incident details. Step 3 uploads supporting documents. Each step is validated with Zod before proceeding. On completion, the claim is inserted and the claimant is redirected to their claims list.

prompt.txt
1Create a multi-step claim submission form at /claims/new with these steps:
2
3Step 1 - Select Policy:
4- shadcn Select populated with the claimant's active policies from Supabase
5- Show policy number, coverage type, and coverage amount for each option
6- Validate that a policy is selected before proceeding
7
8Step 2 - Incident Details (react-hook-form + zod):
9- Incident date (shadcn Calendar via Popover, max date = today)
10- Description textarea (min 50 characters, max 1000)
11- Claimed amount (number input, max = policy coverage_amount)
12- Validation: incident date cannot be in the future
13
14Step 3 - Document Upload:
15- File dropzone accepting PDF, PNG, JPG (max 10MB each, max 5 files)
16- Show file list with remove buttons
17- Upload files to Supabase Storage 'claim-documents' bucket
18- Files are required (at least 1)
19
20Progress indicator at top showing Step 1 / 2 / 3 using shadcn Progress
21Back and Next buttons between steps
22On final submit: insert into 'claims' table, then insert into 'claim_documents' for each file
23Redirect to /claims on success with a toast notification: "Claim submitted successfully"

Expected result: The three-step form renders with a progress bar. Attempting to proceed without completing a step shows inline validation errors. Successfully completing step 3 inserts the claim and redirects to the claims list with a success toast.

6

Add the admin dashboard with stats and assignment controls

Admins need a summary view showing claim counts by status, average processing time, and a queue of unassigned claims. They should be able to assign adjusters directly from this dashboard. Add a simple stats card row at the top of the admin view.

prompt.txt
1Add an admin-only dashboard page at /admin with:
2
31. Stats row using shadcn Card (4 cards side by side):
4 - Total Claims (count of all claims)
5 - Pending Review (count where status = 'under_review')
6 - Approved This Month (count approved in current calendar month)
7 - Unassigned (count where adjuster_id IS NULL and status = 'submitted')
8
92. Unassigned Claims table below the stats:
10 - Show claim ID, claimant name, policy number, incident date, claimed amount
11 - Each row has an "Assign Adjuster" button opening a shadcn Dialog
12 - Dialog shows a Select of all users with role='adjuster'
13 - On confirm, update claims.adjuster_id and show success toast
14
153. Redirect non-admin users to /claims with a toast error "Access denied"
16
17Query stats using Supabase .select('status').then count in JavaScript.
18Check user role from profiles table on page load.
19Polling interval: re-fetch stats every 60 seconds using setInterval.

Pro tip: Ask Lovable to use Supabase .select('id', { count: 'exact' }).eq('status', 'under_review') for each stat card. Four separate small queries are faster and clearer than one aggregated query when you're just counting.

Expected result: Admin users see the stats dashboard with accurate counts. Non-admin users are redirected. The assign adjuster dialog updates the claim and the Unassigned count card decreases immediately.

Complete code

src/components/claims/ClaimStatusBadge.tsx
1import { Badge } from '@/components/ui/badge';
2
3type ClaimStatus = 'submitted' | 'under_review' | 'approved' | 'rejected' | 'closed';
4
5const STATUS_CONFIG: Record<ClaimStatus, { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline'; className: string }> = {
6 submitted: { label: 'Submitted', variant: 'default', className: 'bg-blue-100 text-blue-800 hover:bg-blue-100' },
7 under_review: { label: 'Under Review', variant: 'secondary', className: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-100' },
8 approved: { label: 'Approved', variant: 'default', className: 'bg-green-100 text-green-800 hover:bg-green-100' },
9 rejected: { label: 'Rejected', variant: 'destructive', className: 'bg-red-100 text-red-800 hover:bg-red-100' },
10 closed: { label: 'Closed', variant: 'outline', className: 'bg-gray-100 text-gray-600 hover:bg-gray-100' },
11};
12
13interface ClaimStatusBadgeProps {
14 status: ClaimStatus;
15}
16
17export function ClaimStatusBadge({ status }: ClaimStatusBadgeProps) {
18 const config = STATUS_CONFIG[status];
19 return (
20 <Badge variant={config.variant} className={config.className}>
21 {config.label}
22 </Badge>
23 );
24}
25
26export const VALID_TRANSITIONS: Record<ClaimStatus, ClaimStatus[]> = {
27 submitted: ['under_review'],
28 under_review: ['approved', 'rejected'],
29 approved: ['closed'],
30 rejected: ['closed'],
31 closed: [],
32};
33
34export function getNextStatuses(current: ClaimStatus): ClaimStatus[] {
35 return VALID_TRANSITIONS[current] || [];
36}

Customization ideas

Add a claimant mobile app view

Prompt Lovable to create a mobile-optimized view with a bottom navigation bar instead of the sidebar, large touch-friendly buttons for submitting claims, and a simplified status timeline. Use Tailwind responsive breakpoints to switch between layouts.

Integrate fraud detection scoring

Add a fraud_score column to the claims table, then create an Edge Function that calls an external AI API when a claim is submitted. Display the score as a colored indicator visible only to adjusters and admins.

Add automated payment disbursement

When a claim is approved, trigger a Stripe payout via Edge Function. Store payment_intent_id in the claims table and show payment status in the claim detail Sheet.

Build a claims analytics dashboard

Add a /analytics page with Recharts charts showing claim volume over time, average approval time by coverage type, and rejection rate trends. Use Supabase RPC functions to aggregate the data server-side.

Add deadline reminders

Create a Supabase scheduled function (pg_cron) that checks for claims stuck in under_review for more than 5 business days and sends reminder emails to the assigned adjuster via Resend.

Enable claimant digital signature

Add a signature pad component at the end of the claim submission form that saves a base64 PNG to Supabase Storage as the claim signature document.

Common pitfalls

Pitfall: Forgetting to enable RLS before adding policies

How to avoid: Always run ALTER TABLE x ENABLE ROW LEVEL SECURITY before creating any policies. Check the lock icon on each table in Supabase Dashboard under Table Editor.

Pitfall: Using the anon key in the Edge Function for operations that need to bypass RLS

How to avoid: Use SUPABASE_SERVICE_ROLE_KEY inside Edge Functions for server-side operations. Never expose the service role key in frontend code.

Pitfall: Uploading files directly from the frontend without a storage bucket policy

How to avoid: In Supabase Dashboard → Storage → Policies, add a policy allowing authenticated users to upload to paths starting with their claim ID, and read paths they have claim access to.

Pitfall: Not handling optimistic updates after status transitions

How to avoid: After a successful transition response, update the local state immediately with the new status, then trigger a background re-fetch to confirm the database state.

Pitfall: Allowing any status transition in the UI without server-side validation

How to avoid: The Edge Function is the source of truth for valid transitions. The UI transition select should only show valid options as UX convenience, but the Edge Function always re-validates.

Best practices

  • Always use SUPABASE_SERVICE_ROLE_KEY only inside Edge Functions, never in frontend code — it bypasses all RLS
  • Set up the claim-documents Storage bucket as private, and generate signed URLs server-side for document downloads
  • Add a trigger function in PostgreSQL to automatically update claims.updated_at on every UPDATE
  • Use Supabase Realtime on the claims table so adjusters see new submissions without refreshing
  • Log all status transitions to claim_notes as internal notes for a complete audit trail
  • Test RLS policies thoroughly by signing in as different role users and verifying data visibility in the Supabase Table Editor
  • Add loading states on every async action — claim submission, file upload, status transitions — using shadcn Button's disabled state
  • Validate file types on the frontend (accept attribute) AND in the Edge Function to prevent unauthorized file type uploads

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building an insurance claims management tool with Supabase and React. I have tables: policies, claims (with status enum: submitted, under_review, approved, rejected, closed), claim_documents, claim_notes, and profiles with a role column (claimant, adjuster, admin). Can you write the complete set of Row Level Security policies so that claimants only see their own policies and claims, adjusters only see claims assigned to them, and admins see everything? Include both SELECT and INSERT/UPDATE policies for each table.

Lovable Prompt

Add real-time updates to the claims DataTable so that when any claim's status changes in the database, the row updates automatically without requiring a page refresh. Use Supabase Realtime channel with postgres_changes event on the claims table. Show a subtle toast notification when a claim in the current view is updated.

Build Prompt

In Lovable, create a Supabase Edge Function called transition-claim-status that accepts a claim_id, new_status, optional note, and optional approved_amount in the request body. The function should validate that the status transition is allowed (submitted→under_review, under_review→approved/rejected, approved/rejected→closed), require at least one document exists before approving, update the claims table, insert a note if provided, and call the Resend API to email the claimant. The Resend API key is stored in Secrets as RESEND_API_KEY.

Frequently asked questions

How do I make sure claimants can't see other claimants' documents?

This is handled by Row Level Security at the database level. The policy on claim_documents checks that the claim_id belongs to a claim where the current user is either the claimant or the assigned adjuster. Even if a user tries to query documents directly via the Supabase API, RLS blocks access to rows they don't own.

Can I deploy this to my own domain after building in Lovable?

Yes. Click the Publish icon in the top-right of Lovable, then connect a custom domain in the publish settings. Lovable Pro and higher plans support custom domains. Your Supabase backend stays separate and connected regardless of where the frontend is deployed.

Why does the Edge Function need the service role key instead of the anon key?

The anon key respects all RLS policies, which means the function would only see data the currently authenticated user is allowed to see. For operations like sending admin emails, reading all claims for validation, or performing cross-user updates, you need the service role key which bypasses RLS. Keep this key only in Secrets — never in frontend code.

How do I handle large file uploads (100MB+ PDFs) in Supabase Storage?

For files over 6MB, use Supabase Storage's resumable upload feature. Ask Lovable to use the upload method from @supabase/storage-js with the multipart option. In the Supabase Dashboard under Storage settings, you can also increase the file size limit per bucket.

What happens if the Edge Function fails after updating the status but before sending the email?

The status update will persist in the database but the email won't send. For production use, consider wrapping the DB update and email in a way that logs failed emails to a separate table, then add a retry Edge Function. Alternatively, use a Supabase Database Webhook that triggers the email function on status change rather than inline.

Can I customize the status names (e.g., use 'pending' instead of 'submitted')?

Yes, but change them in the SQL enum before inserting any data. Once you have rows using enum values, you'll need to run ALTER TYPE claim_status RENAME VALUE 'submitted' TO 'pending' in the SQL Editor. Update the corresponding frontend code in Lovable to match.

How should I test that RLS policies are working correctly?

In the Supabase Dashboard, go to the SQL Editor and run: SET ROLE authenticated; SET request.jwt.claims TO '{"sub": "user-uuid-here"}'; then run your SELECT queries. This impersonates a user and shows exactly what they can see. Create test users with each role and verify the results match your expectations.

Can RapidDev help me extend this claims tool with custom integrations?

Yes — RapidDev specializes in extending Lovable-built apps with custom backend integrations, payment processors, and legacy system connectors. If you need to connect your claims tool to an existing policy management system or payment disbursement API, that's a common project type.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help building your app?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.