Skip to main content

Appointments

SCP appointment management for service-based businesses. Appointments represent scheduled visits between end customers and service providers, with integrated payment collection for deposits, balances, and tips.

Contexts

  • SCP.AvailabilityContext - Provider schedules and available time slots
  • SCP.AppointmentContext - Appointment lifecycle management (booking → service → checkout)
  • SCP.PaymentContractContext - Payment rules and contracts for appointments
  • SCP.InvoiceContext - Billing and invoicing for services rendered
  • SCP.MessageContext - Reminders, confirmations, and notifications

Key Functions

Availability & Booking

  • SCP.AvailabilityContext.list_available_slots/3 - Fetch bookable time slots for a provider
  • SCP.AvailabilityContext.book_appointment/4 - Create new appointment with deposit collection
  • SCP.AppointmentContext.cancel_appointment/2 - Cancel appointment with refund processing
  • SCP.AppointmentContext.reschedule_appointment/2 - Move appointment to new time slot

Check-In & Checkout

  • SCP.AppointmentContext.check_in/2 - Mark customer arrival (QR code, SMS, kiosk)
  • SCP.AppointmentContext.complete_appointment/2 - Mark service completion
  • SCP.AppointmentContext.process_checkout/3 - Collect remaining balance and tip

Notifications

  • SCP.MessageContext.send_booking_confirmation/2 - Confirmation at booking time
  • SCP.MessageContext.send_reminder/2 - T-24h and T-2h reminders
  • SCP.MessageContext.send_post_service_message/2 - Thank you and review request

Appointment Booking Flow

The booking flow follows the Zoca PRD model: Discovery → Booking → Deposit → Service → Balance → Tip

Deposit Collection

Deposits are collected at booking time based on merchant payment policy:

Service AmountDeposit Percentage
< $5050%
$50 - $15030-40%
> $15025-30%

Implementation:

  • Merchant configures deposit rules in PaymentPolicyContext
  • AvailabilityContext.book_appointment/4 creates PaymentContract and Invoice for deposit
  • Deposit charged immediately via PaymentContext and Stripe plugin
  • Booking status: pending_paymentconfirmed on successful charge

Day-of-Service Flow

Check-In Methods

MVP: QR Code

  • Unique QR code sent in T-2h reminder SMS
  • Scans to check-in URL with appointment token
  • Updates status to checked_in

Future:

  • SMS reply-based check-in ("Reply YES to check in")
  • Kiosk check-in at location

Checkout & Payment Collection

Remaining Balance:

  • Calculate: service_amount - deposit_paid
  • Charge saved payment method on file
  • If balance = $0, skip to tip collection

Tip Collection:

  • Present options: 15%, 18%, 20%, Custom amount
  • Charge tip as separate transaction
  • Both balance and tip link to original PaymentContract

Final Receipt:

  • Itemized breakdown: Service, Deposit, Balance, Tip, Total
  • Sent via SMS/Email
  • Stored in InvoiceContext

Appointment Lifecycle

Appointment States

StateDescriptionPayment Action
pending_paymentBooking created, awaiting depositCharge deposit
confirmedDeposit paid, appointment confirmedSend reminders
checked_inCustomer arrived at locationNone
in_progressService being deliveredNone
completedService done, final payment collectedCharge balance + tip
cancelledAppointment cancelled before serviceProcess refund per policy
no_showCustomer did not arriveCharge no-show fee

Payment Integration

Appointments are tightly integrated with the payment system:

PaymentContract

Every appointment has a linked PaymentContract that defines:

  • Total service amount
  • Deposit amount and status
  • Remaining balance
  • Associated payment policies (cancellation, no-show)

Invoice Categories

Appointments generate multiple invoices:

  • deposit - Initial payment at booking
  • balance - Remaining service cost at checkout
  • tip - Gratuity after service
  • no_show - Fee for missed appointments
  • refund - Cancellation refunds

Transaction Flow

Appointment

PaymentContract (total amount, deposit rules)

Invoice (deposit) → Transaction (charge)

Invoice (balance) → Transaction (charge)

Invoice (tip) → Transaction (charge)

TransactionJournal (double-entry bookkeeping)

Reminders & Notifications

Automated messages sent via MessageContext and SMS/Email plugins:

Booking Confirmation (Immediate)

Subject: Appointment Confirmed
Body: Your appointment with [Provider] is confirmed for [DateTime].
Service: [Service Name]
Deposit Paid: $XX.XX
Remaining Balance: $XX.XX
[Cancel/Reschedule Link]

T-24h Reminder

Subject: Reminder: Appointment Tomorrow
Body: Your appointment is tomorrow at [Time].
Provider: [Name]
Location: [Address]
[Add to Calendar]

T-2h Reminder (with QR Code)

Subject: Appointment in 2 Hours
Body: Your appointment starts in 2 hours.
Check in here: [QR Code]
Location: [Address with Map Link]

Post-Service Thank You

Subject: Thank You for Visiting
Body: Thanks for visiting [Business Name]!
[View Receipt]
[Leave a Review]
[Book Again]

Cancellation & Refunds

Cancellation Policy

Merchants configure cancellation windows in PaymentPolicyContext:

  • >24h notice: Full refund (100% deposit returned)
  • <24h notice: Partial refund (50% deposit returned)
  • <2h notice: No refund (0% deposit returned)

Refund Processing

# Cancel appointment with policy-based refund
AppointmentContext.cancel_appointment(appointment, %{
cancelled_by: end_customer_id,
reason: "scheduling conflict"
})

# Calculates refund based on:
# - Hours until appointment
# - Merchant cancellation policy
# - Deposit amount paid

# Creates refund transaction via PaymentContext

No-Show Handling

  • If customer doesn't check in within 15 minutes of appointment time
  • Status automatically updated to no_show
  • No-show fee charged per merchant policy (typically 100% of deposit)
  • Merchant can manually waive fee if needed

Search & Filtering

Merchants can search appointments using AppointmentContext.search_appointments/3:

Filter Criteria:

  • Date range (start/end dates)
  • Provider IDs
  • Service IDs
  • Location IDs
  • Status (confirmed, completed, cancelled, no_show)
  • End customer name/phone/email

Export:

  • CSV export for reporting and accounting
  • Includes payment details, provider, service, timestamps

Concurrency & Race Conditions

Slot Booking Conflicts

  • Database-level row locking prevents double-booking
  • Redis cache for real-time availability updates
  • Optimistic locking with version numbers

Implementation:

# Lock slot during booking transaction
Repo.transaction(fn ->
slot = AvailabilityContext.lock_slot!(slot_id)

if slot.status == :available do
AvailabilityContext.book_appointment(slot, customer, service, payment_method)
else
{:error, :slot_unavailable}
end
end)

Multi-Location Support

Post-MVP Feature:

  • Each appointment links to specific Location
  • Providers can work across multiple locations
  • Availability managed per location
  • Location-specific payment policies

Revenue Attribution

Track Zoca-generated bookings vs. existing customer bookings:

Metadata on Appointments:

%Appointment{
source: :zoca_booking, # vs :manual, :phone, :walkin
attribution: %{
channel: "organic_search",
campaign: "google_ads_q1",
booking_page_id: "..."
}
}

Reporting:

  • Revenue dashboard shows Zoca-attributed vs. total
  • Conversion metrics (views → bookings)
  • Average booking value by channel

See the API Reference for appointment-related endpoints:

  • POST /api/bookings - Create a new booking
  • GET /api/appointments - List appointments
  • PUT /api/appointments/:id - Update appointment
  • POST /api/appointments/:id/check-in - Check in customer
  • POST /api/appointments/:id/checkout - Process checkout