Reference
Server actions
OpsDash uses Next.js Server Actions as the exclusive data layer for all authenticated operations. Every action follows the same security pattern:
const { supabase, org, user } = await withOrg(slug);
const parsed = schema.safeParse(input);
if (!parsed.success) return { success: false, error: 'validation_error' };
// ... data operation ...
await createAuditLog(supabase, org.id, user.id, 'entity.action', { ... });
return { success: true, data };Auth & core
| File | Action | Description |
|---|---|---|
| helpers.ts | withOrg(slug) | Validates auth, resolves org, checks membership. Used in every server action. |
| helpers.ts | withOrgAdmin(slug) | Same as withOrg() but also requires admin or owner role. |
| helpers.ts | requireNonViewer(supabase, orgId, userId) | Throws if user has viewer role. |
| helpers.ts | createAuditLog(supabase, orgId, userId, action, meta) | Writes to audit_logs table. |
| helpers.ts | checkUsageLimit(supabase, orgId, feature, count) | Checks usage against plan limits. |
| organizations.ts | createOrganization(input) | Barrel → organizations-create.ts: team workspace, flags, switch current org. |
| organizations-user.ts | getUserDefaultOrg() | Default org slug after auth (same rules as middleware). |
| organizations-user.ts | getUserOrganizations() | Lists memberships with org rows for org switcher. |
| organizations-switch.ts | switchCurrentOrg(orgId) | Sets users.current_org_id. |
| organizations-profile.ts | getCurrentUserProfile() / updateProfile(input) | User row + auth metadata sync for profile and menu. |
| organizations-password.ts | changePassword(input) | Auth password update. |
| organizations-leave.ts | leaveOrganization(orgId) | Leave team workspace unless sole owner. |
| settings.ts (barrel → settings-organization.ts) | updateOrganization(slug, data) | Updates org name, slug, logo_url. |
| settings.ts (barrel → settings-organization.ts) | deleteOrganization(slug) | Owner-only soft-delete; returns personalSlug for redirect. |
| members.ts (barrel → members-invite.ts) | inviteMember(orgSlug, formData) | Validates invitation; plan limits; creates row; sends email when token + URL exist. |
| members.ts (barrel → members-mutations.ts) | changeMemberRole(orgSlug, memberId, newRole) | RPC update_org_member_role; audit member.role_changed. |
| members.ts (barrel → members-mutations.ts) | removeMember(orgSlug, memberId) | Owner/admin rules; cannot remove last owner. |
| members.ts (barrel → members-queries.ts) | getMembers(orgSlug) | RPC get_org_members; maps rows for pickers. |
| members.ts (barrel → members-queries.ts) | getInvitations(orgSlug) | Lists pending invitations. |
| members.ts (barrel → members-mutations.ts) | revokeInvitation(orgSlug, invitationId) | Deletes pending invite. |
| members.ts (barrel → members-invitation-token.ts) | acceptInvitation(token) | RPC accept_invitation; switches current org on success. |
| members.ts (barrel → members-invitation-token.ts) | getInvitationByToken(token) | RPC get_invitation_by_token for invite landing page. |
CRM
| File | Action | Description |
|---|---|---|
| contacts.ts | getContact(orgSlug, contactId) | Single contact with all linked data. |
| contacts-list.ts | getContactsPaginated(orgSlug, options) | Paginated contacts with filters and sorting. |
| contacts.ts | createContact(orgSlug, data) | Creates contact, respects crm:contacts usage limit. |
| contacts.ts | updateContact(orgSlug, contactId, data) | Updates contact fields. |
| contacts.ts | deleteContact(orgSlug, contactId) | Soft-deletes contact. |
| contacts.ts | importContactsFromCsv(orgSlug, rows) | Bulk upsert from CSV rows (Pro). |
| contacts-bulk.ts | bulkUpdateContacts(orgSlug, ids, data) | Bulk update contacts (Pro). |
| contacts-bulk.ts | bulkDeleteContacts(orgSlug, ids) | Bulk soft-delete contacts (Pro). |
| companies.ts (barrel → companies-queries.ts) | getCompanies(orgSlug) | All companies (name order) for pickers. |
| companies.ts (barrel → companies-queries.ts) | getCompaniesPaginated(orgSlug, cursor?, filters?) | Paginated list; optional industry/size/search filters (tiered when filters active). |
| companies.ts (barrel → companies-queries.ts) | getCompany(orgSlug, companyId) | Single company row. |
| companies.ts (barrel → companies-queries.ts) | getCompanyWithRelated(orgSlug, companyId) | Company + linked contacts, deals, projects. |
| companies.ts (barrel → companies-mutations.ts) | createCompany(orgSlug, data) | Creates company; checks crm:companies usage cap. |
| companies.ts (barrel → companies-mutations.ts) | updateCompany(orgSlug, companyId, data) | Updates company (incl. metadata for custom fields). |
| companies.ts (barrel → companies-mutations.ts) | deleteCompany(orgSlug, companyId) | Soft-deletes company. |
| companies.ts (barrel → companies-enrich.ts) | enrichCompany(orgSlug, companyId) | Clearbit when API key + domain; deterministic fallback otherwise. |
| leads.ts | getLeadsPaginated(orgSlug, options) | Paginated leads with filters. |
| leads.ts | createLead(orgSlug, data) | Creates lead. |
| leads.ts | convertLeadToContactAndDeal(orgSlug, leadId) | Atomic conversion — creates contact + deal, fires lead_converted workflow. |
| deals.ts | getDealsPaginated(orgSlug, options) | Paginated deals with filters. |
| deals.ts | createDeal(orgSlug, data) | Creates deal. |
| deals.ts | updateDealStage(orgSlug, dealId, stageId) | Changes deal stage, fires deal_stage_change workflow. |
| deals.ts | getDealAtRisk(orgSlug) | Returns deals with no activity for 7+ days. |
| quotes.ts | createDealQuote(orgSlug, dealId, data) | Creates deal quote (Pro). |
| quotes.ts | exportQuoteToPdf(orgSlug, quoteId) | Generates PDF quote. |
Projects
| File | Action | Description |
|---|---|---|
| projects.ts | getProjectsPaginated(orgSlug, options) | Paginated projects. |
| projects.ts | createProject(orgSlug, data) | Creates project, respects projects:crud limit. |
| projects.ts | updateProject(orgSlug, projectId, data) | Updates project. |
| projects.ts | deleteProject(orgSlug, projectId) | Soft-deletes project. |
| tasks.ts | getTasks(orgSlug, projectId) | All tasks for a project. |
| tasks.ts | createTask(orgSlug, data) | Creates task. |
| tasks.ts | updateTask(orgSlug, taskId, data) | Updates task. |
| tasks.ts | updateTaskStatus(orgSlug, taskId, status) | Changes task status, writes audit log. |
| tasks.ts | deleteTask(orgSlug, taskId) | Soft-deletes task (sets deleted_at). |
| tasks.ts | createTaskTimeEntry(orgSlug, taskId, data) | Logs time entry (Pro). |
| milestones.ts | getProjectMilestones(orgSlug, projectId) | All milestones for a project. |
| milestones.ts | createMilestone(orgSlug, projectId, data) | Creates milestone (Pro). |
Automation
| File | Action | Description |
|---|---|---|
| workflows.ts | getWorkflowTriggers(orgSlug) | List all workflow triggers. |
| workflows.ts | createWorkflowTrigger(orgSlug, data) | Create a workflow with trigger + conditions. |
| workflows.ts | updateWorkflowTrigger(orgSlug, triggerId, data) | Update trigger. |
| workflows.ts | deleteWorkflowTrigger(orgSlug, triggerId) | Delete trigger + all actions. |
| workflow-runs.ts | listWorkflowRuns(orgSlug, options) | Cursor-paginated run history (Pro). |
Platform
| File | Action | Description |
|---|---|---|
| dashboard.ts | getDashboardStats(orgSlug, dateRange?) | KPIs, pipeline value, win rate, revenue. |
| dashboard.ts | getAiInsights(orgSlug, locale) | At-risk deals, overdue tasks, SLA breaches. |
| settings.ts (barrel → settings-api-keys.ts) | createApiKey(orgSlug, name) | Generates API key, stores hash (Enterprise). |
| settings.ts (barrel → settings-api-keys.ts) | revokeApiKey(orgSlug, keyId) | Revokes API key. |
| webhooks.ts | createOutboundWebhook(orgSlug, data) | Creates outbound webhook (Enterprise). |
| webhooks.ts | testOutboundWebhook(orgSlug, webhookId) | Sends test payload. |
| export.ts | exportOrgData(orgSlug) | Generates ZIP of all org data (Pro). |
| admin.ts (barrel → admin-feature-gates.ts) | requireFeatureAccess(orgSlug, featureKey) | Returns error if feature is not accessible. |
| admin.ts (barrel → admin-feature-gates.ts) | ensureFeatureAccess(orgSlug, featureKey, locale) | Redirects to dashboard if not accessible. |