API Reference
Opportunities

Opportunities API

Sales pipeline and CRM for tracking leads and converting prospects to clients.

Overview

Opportunities (sales leads) help you:

  • Track potential clients before they book
  • Manage a sales pipeline with stages
  • Log interactions and follow-ups
  • Measure conversion rates
  • Convert prospects to full client records

Data Models

Opportunity

FieldTypeDescription
idUUIDUnique identifier
client_idUUIDLinked client (if converted)
prospect_nameStringProspect's name (if not a client)
prospect_emailStringProspect's email
prospect_phoneStringProspect's phone
category_idUUIDOpportunity category
titleStringBrief description
descriptionStringDetailed notes
estimated_valueDecimalExpected revenue
probabilityIntegerWin probability (0-100%)
statusEnumPipeline stage
sourceStringLead source
assigned_toUUIDAssigned staff member
follow_up_dateDateTimeNext follow-up date
closed_atDateTimeWhen won/lost
closed_reasonStringWhy closed
created_byUUIDWho created

Status Values (Pipeline Stages)

StatusDescription
openNew lead, not yet contacted
contactedInitial contact made
qualifiedLead is qualified, interested
proposalProposal/quote sent
wonConverted to sale
lostDid not convert
staleNo activity, gone cold

Lead Sources

Common values for source:

  • website — Website inquiry form
  • referral — Client referral
  • walk_in — Walk-in visitor
  • phone — Phone inquiry
  • social — Social media
  • event — Event or expo
  • other — Other source

Opportunity Category

FieldTypeDescription
idUUIDUnique identifier
nameStringCategory name
colorStringDisplay color (hex)
sort_orderIntegerDisplay order
is_activeBooleanAvailable for use

Opportunity Note

FieldTypeDescription
idUUIDUnique identifier
opportunity_idUUIDParent opportunity
contentStringNote text
created_byUUIDNote author
created_atDateTimeWhen written

Opportunity Activity

FieldTypeDescription
idUUIDUnique identifier
opportunity_idUUIDParent opportunity
activity_typeEnumType of activity
descriptionStringActivity details
outcomeStringResult of activity
scheduled_atDateTimeWhen scheduled
completed_atDateTimeWhen completed
created_byUUIDWho logged it

Activity Types:

  • call — Phone call
  • email — Email sent
  • meeting — In-person meeting
  • note — General note
  • status_change — Status updated
  • task — Task completed
  • other — Other activity

Categories

Get Categories

import { getOpportunityCategories } from '@/lib/actions/opportunities'
 
const result = await getOpportunityCategories()

Create Category

import { createOpportunityCategory } from '@/lib/actions/opportunities'
 
const result = await createOpportunityCategory({
  name: 'Botox Inquiry',
  color: '#10b981'  // emerald
})

Opportunities

Get Opportunities

import { getOpportunities } from '@/lib/actions/opportunities'
 
// All open opportunities
const result = await getOpportunities({
  status: ['open', 'contacted', 'qualified', 'proposal']
})
 
// Assigned to specific staff
const myLeads = await getOpportunities({
  assigned_to: 'staff-uuid'
})
 
// Search
const search = await getOpportunities({
  search: 'botox',
  limit: 20,
  offset: 0
})

Response includes:

  • Opportunity details
  • Linked client (if any)
  • Category with color
  • Assigned staff
  • Notes and activities counts

Get Single Opportunity

import { getOpportunity } from '@/lib/actions/opportunities'
 
const result = await getOpportunity('opportunity-uuid')
// Includes full notes and activity history

Create Opportunity

import { createOpportunity } from '@/lib/actions/opportunities'
 
// New prospect (not yet a client)
const result = await createOpportunity({
  title: 'Interested in Botox package',
  prospect_name: 'Jane Doe',
  prospect_email: 'jane@example.com',
  prospect_phone: '+15551234567',
  category_id: 'category-uuid',
  estimated_value: 500.00,
  probability: 50,
  source: 'website',
  assigned_to: 'staff-uuid',
  follow_up_date: '2026-02-20T10:00:00Z',
  description: 'Filled out inquiry form for Botox. First-time client.'
})
 
// Existing client with new opportunity
const existing = await createOpportunity({
  title: 'Membership upgrade',
  client_id: 'client-uuid',
  category_id: 'membership-category-uuid',
  estimated_value: 1200.00,
  probability: 75,
  source: 'referral'
})

Update Opportunity

import { updateOpportunity } from '@/lib/actions/opportunities'
 
const result = await updateOpportunity('opportunity-uuid', {
  status: 'qualified',
  probability: 70,
  estimated_value: 600.00,
  follow_up_date: '2026-02-25T14:00:00Z'
})

Close Opportunity

import { closeOpportunity } from '@/lib/actions/opportunities'
 
// Won!
const won = await closeOpportunity('opportunity-uuid', {
  won: true,
  transaction_id: 'transaction-uuid'  // optional: link to sale
})
 
// Lost
const lost = await closeOpportunity('opportunity-uuid', {
  won: false,
  reason: 'Went with competitor - price sensitive'
})

Delete Opportunity

import { deleteOpportunity } from '@/lib/actions/opportunities'
 
const result = await deleteOpportunity('opportunity-uuid')

Notes

Add Note

import { addOpportunityNote } from '@/lib/actions/opportunities'
 
const result = await addOpportunityNote(
  'opportunity-uuid',
  'Called and left voicemail. Will try again tomorrow.'
)

Get Notes

import { getOpportunityNotes } from '@/lib/actions/opportunities'
 
const result = await getOpportunityNotes('opportunity-uuid')
// Returns notes in reverse chronological order

Activities

Log Activity

import { logOpportunityActivity } from '@/lib/actions/opportunities'
 
// Log a phone call
const result = await logOpportunityActivity({
  opportunity_id: 'opportunity-uuid',
  activity_type: 'call',
  description: 'Discussed treatment options',
  outcome: 'Very interested, sending info packet'
})
 
// Schedule a follow-up meeting
const meeting = await logOpportunityActivity({
  opportunity_id: 'opportunity-uuid',
  activity_type: 'meeting',
  description: 'Consultation appointment',
  scheduled_at: '2026-02-20T14:00:00Z'
})
 
// Complete a scheduled activity
const completed = await logOpportunityActivity({
  opportunity_id: 'opportunity-uuid',
  activity_type: 'meeting',
  description: 'Consultation completed',
  outcome: 'Ready to book, sending proposal',
  completed_at: new Date().toISOString()
})

Get Activities

import { getOpportunityActivities } from '@/lib/actions/opportunities'
 
const result = await getOpportunityActivities('opportunity-uuid')
// Full activity timeline

Pipeline Statistics

Get Stats

import { getOpportunityStats } from '@/lib/actions/opportunities'
 
const result = await getOpportunityStats()

Response:

{
  "success": true,
  "data": {
    "counts": {
      "open": 15,
      "contacted": 8,
      "qualified": 5,
      "proposal": 3,
      "won": 12,
      "lost": 7,
      "stale": 2
    },
    "totalValue": 25000.00,
    "weightedValue": 12500.00,
    "needsFollowUp": 4,
    "total": 52
  }
}
  • totalValue — Sum of estimated_value for open opportunities
  • weightedValue — Value × probability for open opportunities
  • needsFollowUp — Opportunities with follow_up_date ≤ today

Convert to Client

Convert a prospect to a full client record.

import { convertToClient } from '@/lib/actions/opportunities'
 
const result = await convertToClient('opportunity-uuid')
 
if (result.success) {
  const { client_id } = result.data
  // Opportunity is now linked to this client
  // Prospect info was used to create client record
}

What happens:

  1. Creates new client from prospect_name, prospect_email, prospect_phone
  2. Links the opportunity to the new client
  3. Logs activity for the conversion

Workflows

Website Lead Workflow

// 1. Receive inquiry from website form
const opportunity = await createOpportunity({
  title: 'Website Inquiry - Botox',
  prospect_name: formData.name,
  prospect_email: formData.email,
  prospect_phone: formData.phone,
  source: 'website',
  description: formData.message,
  follow_up_date: tomorrow
})
 
// 2. Assign to staff
await updateOpportunity(opportunity.data.id, {
  assigned_to: 'sales-staff-uuid'
})
 
// 3. Staff makes contact
await logOpportunityActivity({
  opportunity_id: opportunity.data.id,
  activity_type: 'call',
  description: 'Initial outreach call',
  outcome: 'Left voicemail'
})
 
// 4. Follow up, qualify, etc.
 
// 5. Convert when ready to book
await convertToClient(opportunity.data.id)
await closeOpportunity(opportunity.data.id, { won: true })

Membership Expiration Workflow

// When a membership is expiring, create opportunity for renewal
const opportunity = await createOpportunity({
  title: 'Membership Renewal',
  client_id: existingClientId,
  category_id: 'renewal-category-uuid',
  estimated_value: membershipPrice,
  probability: 70,  // Existing clients more likely to renew
  source: 'internal',
  follow_up_date: thirtyDaysBeforeExpiration
})

Best Practices

  1. Set follow-up dates — Don't let leads go cold
  2. Log all interactions — Build relationship history
  3. Use categories — Segment for reporting
  4. Track sources — Know what marketing works
  5. Review stale leads — Weekly pipeline review
  6. Assign owners — Clear accountability
  7. Convert promptly — Don't duplicate data entry