Every mutation in the kit uses Next.js Server Actions. They run server-side, use the appropriate Supabase client for reads and writes, and call revalidatePath() to refresh page data.
Architecture
Client Component --> Server Action --> Supabase Client --> Database
--> revalidatePath() --> Page Refresh
All Server Actions are in lib/actions/ and marked with "use server" at the top of each file.
Auth Actions (lib/actions/auth.ts)
| Action |
Parameters |
Description |
signIn |
email, password |
Sign in with email/password |
signUp |
email, password, fullName |
Create account; triggers profile creation |
signOut |
- |
Clear session and redirect to /login |
resetPassword |
email |
Send password reset email |
signInWithOAuth |
provider |
Redirect to Google or GitHub OAuth |
| Action |
Parameters |
Description |
getContacts |
filters, search, page, limit |
Paginated list with full-text search |
getContact |
id |
Single contact with joins |
createContact |
FormData |
Insert contact and contact_tags |
updateContact |
id, FormData |
Update contact and sync tags |
deleteContact |
id |
Delete contact and associated tags |
searchContacts |
query |
Full-text search via tsvector |
importContacts |
contacts[] |
Bulk insert from CSV import |
Company Actions (lib/actions/companies.ts)
| Action |
Parameters |
Description |
getCompanies |
filters, search, page, limit |
Paginated list with full-text search |
getCompany |
id |
Single company with joins |
createCompany |
FormData |
Insert company and company_tags |
updateCompany |
id, FormData |
Update company and sync tags |
deleteCompany |
id |
Delete company (admin only) |
searchCompanies |
query |
Full-text search via tsvector |
Deal Actions (lib/actions/deals.ts)
| Action |
Parameters |
Description |
getDeals |
pipelineId, filters |
Deals with stage, company, contact joins |
getDeal |
id |
Single deal with all related data |
createDeal |
FormData |
Insert deal, deal_products, deal_tags |
updateDeal |
id, FormData |
Update deal details |
updateDealStage |
dealId, stageId |
Move deal to new stage (triggers activity log) |
deleteDeal |
id |
Delete deal (admin only) |
Pipeline Actions (lib/actions/pipelines.ts)
| Action |
Parameters |
Description |
getPipelines |
- |
All pipelines with stage counts |
getPipelineStages |
pipelineId |
Ordered stages for a pipeline |
createPipeline |
name, description |
Create new pipeline (admin only) |
updatePipeline |
id, data |
Update pipeline details (admin only) |
createPipelineStage |
pipelineId, data |
Add stage to pipeline (admin only) |
updatePipelineStage |
id, data |
Update stage name, color, probability (admin only) |
reorderPipelineStages |
stageIds[] |
Update position ordering (admin only) |
deletePipelineStage |
id |
Remove stage with deal reassignment (admin only) |
Task Actions (lib/actions/tasks.ts)
| Action |
Parameters |
Description |
getTasks |
filters, search, page |
Paginated tasks with joins |
createTask |
FormData |
Insert task |
updateTask |
id, data |
Update task fields or toggle status |
deleteTask |
id |
Delete task |
bulkUpdateTasks |
ids[], status |
Batch status change |
bulkDeleteTasks |
ids[] |
Batch delete |
Activity Actions (lib/actions/activities.ts)
| Action |
Parameters |
Description |
getActivities |
filters, page |
Paginated activity log with joins |
createActivity |
type, description, entityIds |
Log a new activity |
getActivitiesForEntity |
entityType, entityId |
Activities for a specific contact/deal/company |
exportActivities |
filters |
Generate CSV export |
| Action |
Parameters |
Description |
getTags |
- |
All tags |
createTag |
name, color |
Create new tag |
deleteTag |
id |
Delete tag (admin only) |
addTagsToEntity |
entityType, entityId, tagIds |
Add tags via junction table |
removeTagFromEntity |
entityType, entityId, tagId |
Remove tag link |
Email Actions (lib/actions/emails.ts)
| Action |
Parameters |
Description |
getEmails |
folder, search, page |
Paginated emails by folder |
getEmail |
id |
Single email with contact join |
createEmail |
data |
Create email (sent, draft, or inbox) |
updateEmail |
id, data |
Toggle read, starred, or move folder |
deleteEmail |
id |
Delete email |
bulkUpdateEmails |
ids[], data |
Batch read/star updates |
Email Template Actions (lib/actions/email-templates.ts)
| Action |
Parameters |
Description |
getEmailTemplates |
category, search |
Templates with optional filters |
getEmailTemplate |
id |
Single template |
createEmailTemplate |
data |
Create template |
updateEmailTemplate |
id, data |
Update template; increments usage_count when used |
duplicateEmailTemplate |
id |
Clone template with "Copy of" prefix |
deleteEmailTemplate |
id |
Delete template |
Email Sequence Actions (lib/actions/email-sequences.ts)
| Action |
Parameters |
Description |
getEmailSequences |
- |
All sequences with step counts |
getEmailSequence |
id |
Sequence with all steps |
createEmailSequence |
data |
Create sequence |
updateEmailSequence |
id, data |
Update sequence status or details |
deleteEmailSequence |
id |
Delete sequence and steps |
createSequenceStep |
sequenceId, data |
Add step to sequence |
updateSequenceStep |
id, data |
Update step content or position |
deleteSequenceStep |
id |
Remove step |
reorderSequenceSteps |
stepIds[] |
Update step positions |
Team Actions (lib/actions/team.ts)
| Action |
Parameters |
Description |
getTeamMembers |
- |
All profiles |
updateTeamMember |
id, data |
Update role, quota, job title (admin only) |
removeTeamMember |
id |
Deactivate team member (admin only) |
inviteTeamMember |
email, role |
Send invite via Supabase Auth (admin only) |
Notification Actions (lib/actions/notifications.ts)
| Action |
Parameters |
Description |
getNotifications |
userId |
Unread notifications for current user |
markAsRead |
id |
Mark single notification as read |
markAllAsRead |
userId |
Mark all notifications as read |
dismissNotification |
id |
Delete notification |
Settings Actions (lib/actions/settings.ts)
| Action |
Parameters |
Description |
getSettings |
- |
Get CRM settings row |
updateSettings |
data |
Update settings (admin only) |
Report Actions (lib/actions/reports.ts)
| Action |
Parameters |
Description |
getSalesOverview |
dateRange |
KPIs, revenue trend, won/lost, by source |
getPipelineAnalytics |
pipelineId |
Funnel, stage counts, time in stage, velocity |
getTeamPerformance |
dateRange |
Leaderboard, radar data, activity distribution |
getRevenueBreakdown |
dateRange |
By product, industry, deal size, LTV table |
getForecastData |
quarter |
Monthly forecast by category and rep |
Error Handling Pattern
All Server Actions follow this pattern:
"use server"
import { createAdminClient } from "@/lib/supabase/admin"
import { revalidatePath } from "next/cache"
export async function updateDealStage(dealId: string, stageId: string) {
try {
const supabase = createAdminClient()
const { error } = await supabase
.from("deals")
.update({ stage_id: stageId })
.eq("id", dealId)
if (error) throw error
revalidatePath("/deals")
revalidatePath(`/deals/${dealId}`)
return { success: true }
} catch (err) {
console.error("Failed to update deal stage:", err)
return { success: false, error: "Failed to update deal stage" }
}
}
Using Server Actions in Components
"use client"
import { updateDealStage } from "@/lib/actions/deals"
function StageSelect({ dealId, currentStage }: Props) {
const [isPending, startTransition] = useTransition()
function handleStageChange(stageId: string) {
startTransition(async () => {
const result = await updateDealStage(dealId, stageId)
if (result.success) {
toast.success("Deal stage updated")
} else {
toast.error(result.error)
}
})
}
return (
<Select onValueChange={handleStageChange} disabled={isPending}>
{/* ... */}
</Select>
)
}
Supabase Client Selection
| Context |
Client |
RLS |
| Dashboard reads (all CRM data) |
Server client |
Yes (team member session) |
| Report aggregates |
Server client |
Yes (team member session) |
| Write operations (create, update) |
Admin client |
Bypassed (service role) |
| Delete operations |
Admin client |
Bypassed (service role) |
| Auth operations |
Server client |
N/A (Supabase Auth API) |
| Notifications (own only) |
Server client |
Yes (user's session) |
The admin client (lib/supabase/admin.ts) uses SUPABASE_SERVICE_ROLE_KEY and should never be exposed to the client.