Salon Appointment Booking Automation avatar

Salon Appointment Booking Automation

Under maintenance

Pricing

from $70.00 / 1,000 bookings

Go to Apify Store
Salon Appointment Booking Automation

Salon Appointment Booking Automation

Under maintenance

Pricing

from $70.00 / 1,000 bookings

Rating

0.0

(0)

Developer

Gihan Gangadara

Gihan Gangadara

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Categories

Share

Short description: Check availability, book, look up, and cancel salon appointments programmatically — a JSON-in/JSON-out automation layer for AI agents, chatbots, websites, and workflow tools, backed by a real salon booking system.

This is not "a salon website uploaded to Apify." It's an automation/API Actor: other software calls it with structured input and gets a structured result back, the same way it would call any booking API — it just happens to run on Apify's infrastructure instead of you hosting your own endpoint.

Who should use this

  • A salon (or a developer building for one) that already runs the connected booking backend and wants to accept bookings from channels beyond its own website — a WhatsApp bot, an AI receptionist, a Zapier/Make workflow, a booking widget embedded elsewhere.
  • Anyone building an AI agent or chatbot that needs to check and book real salon appointments as a tool call, with structured JSON in and out.
  • Internal automation: nightly availability reports, bulk-importing appointments from another system, syncing booking status into a CRM.

Use cases

  • An AI receptionist that answers "do you have anything free Thursday afternoon?" by calling checkAvailability and reading the slot list back.
  • A website or mobile app booking widget that calls createAppointment instead of implementing its own booking backend.
  • A workflow tool (Zapier/Make/n8n) node that creates or cancels appointments as part of a larger automation.
  • A support bot that looks up an appointment's status with getAppointment before answering "is my booking confirmed yet?"

Features

  • checkAvailability — is this service open at this date/time? (or list every open slot for a day)
  • createAppointment — book a service for a customer, subject to the salon's real working hours and existing bookings (no double-booking)
  • getAppointment — look up a previously created appointment's status, without exposing the customer's phone/email back out
  • cancelAppointment — cancel a booking; cancelling something already cancelled is a safe no-op, not an error
  • Input validation with clear, itemized error messages
  • Idempotent booking: re-running the exact same createAppointment request never creates a second appointment — it returns the original booking with duplicate: true
  • Every run's result is written to the Actor's default Dataset
  • No hard-coded credentials — the backend API key is a secret input field

Architecture

User / AI Agent
|
v
Apify Actor (this package — validation, idempotency, HTTP client)
| HTTPS + x-api-key
v
Salon Booking API (server/src/routes/actorApi.js — new, API-key-protected)
| calls
v
bookingService.js (existing, reused unchanged — availability rules,
| double-booking prevention, the booking model)
v
SQLite (existing database)

This Actor is an adapter, not a database client. Apify Actors run in isolated containers with no shared filesystem, so it can't open the salon's SQLite file directly (and SQLite isn't safe for concurrent multi-process writes anyway). Instead it calls a small set of API-key-protected routes added to the existing Express backend, which call the same booking/ availability functions the salon's own customer-facing website uses — no booking logic is duplicated or reimplemented.

To point the Actor at your own deployment, set apiBaseUrl to wherever you host server/ (e.g. https://api.yoursalon.com) and apiKey to that deployment's ACTOR_API_KEY.

Supported operations

OperationStatusNotes
checkAvailability✅ Fully supportedReal backend logic (working hours + existing bookings)
createAppointment✅ Fully supportedIdempotent; double-booking prevented server-side
getAppointment✅ Fully supportedReturns only booking-relevant fields, not phone/email
cancelAppointment✅ Fully supportedReuses the same status column the owner's dashboard already writes to

Nothing here is faked. Every operation above calls a real, tested code path in the existing backend (server/src/services/bookingService.js).

Input parameters

FieldTypeRequired forNotes
operationstringalwayscheckAvailability | createAppointment | getAppointment | cancelAppointment
salonIdstringoptionalThis is a single-salon deployment — leave blank. If set, validated against the backend's SALON_ID (reserved scaffolding for a future multi-salon version)
serviceIdstringcheckAvailability, createAppointment (or use service)The backend's real numeric service id, e.g. "1" — preferred over service
servicestringalternative to serviceIdExact service name, e.g. "Women's Haircut" (case-insensitive)
staffIdstringoptionalReserved for a future release — no staff/stylist model exists yet, so this is accepted and recorded in the Dataset but has no effect on booking
appointmentDatestringcheckAvailability, createAppointmentYYYY-MM-DD
appointmentTimestringcreateAppointment (optional for checkAvailability)24-hour HH:MM. Omit on checkAvailability to list all open slots for the day
customerNamestringcreateAppointment
customerPhonestringcreateAppointment
customerEmailstringoptional
appointmentIdstringgetAppointment, cancelAppointmentID returned by a prior createAppointment call
apiBaseUrlstringoptionalDefaults to API_BASE_URL env var, then http://localhost:4000
apiKeystring (secret)required in practiceDefaults to API_KEY env var. Sent as x-api-key

Input examples

Check a specific slot (by service id):

{
"operation": "checkAvailability",
"serviceId": "1",
"appointmentDate": "2026-10-15",
"appointmentTime": "10:30"
}

Create a booking:

{
"operation": "createAppointment",
"serviceId": "1",
"appointmentDate": "2026-10-15",
"appointmentTime": "10:30",
"customerName": "John",
"customerPhone": "0771234567",
"customerEmail": "john@example.com"
}

Look up a booking:

{ "operation": "getAppointment", "appointmentId": "12345" }

Cancel a booking:

{ "operation": "cancelAppointment", "appointmentId": "12345" }

More runnable examples (including every failure case) are in test/inputs/.

Output examples

Availability (specific time):

{
"success": true,
"operation": "checkAvailability",
"available": true,
"date": "2026-10-15",
"time": "10:30",
"service": "Haircut"
}

Unavailable:

{
"success": true,
"operation": "checkAvailability",
"available": false,
"reason": "Requested appointment slot is already booked."
}

Successful booking — status reflects the backend's real workflow. New bookings start as "pending" until the salon owner confirms them in their dashboard; this Actor does not fabricate instant "confirmed" status:

{
"success": true,
"operation": "createAppointment",
"appointmentId": "12345",
"customerName": "John",
"service": "Haircut",
"date": "2026-10-15",
"time": "10:30",
"status": "pending"
}

Slot already taken:

{
"success": false,
"operation": "createAppointment",
"available": false,
"reason": "Requested appointment slot is unavailable."
}

Get appointment (no phone/email exposed):

{
"success": true,
"operation": "getAppointment",
"appointmentId": "12345",
"customerName": "John",
"service": "Haircut",
"date": "2026-10-15",
"time": "10:30",
"status": "confirmed"
}

Cancel appointment:

{
"success": true,
"operation": "cancelAppointment",
"appointmentId": "12345",
"status": "cancelled"
}

How appointment availability works

The connected backend generates 15-minute candidate start times across the salon's configured working hours for that day of the week, removes any that overlap an existing pending or confirmed booking (by service duration), and removes past times if the date is today. checkAvailability either checks whether a specific time survived that filter, or — if you omit appointmentTime — returns the full list.

Dataset output

Every run pushes one item to the default Dataset, with fields that don't apply to a given operation/outcome set to null (so every run produces the same schema for the Dataset's table view):

{
"success": true,
"operation": "createAppointment",
"appointmentId": "12345",
"salonId": null,
"serviceId": "1",
"staffId": null,
"customerName": "John",
"service": "Haircut",
"appointmentDate": "2026-10-15",
"appointmentTime": "10:30",
"status": "pending",
"error": null,
"reason": null
}

No password, API key, JWT secret, or database credential is ever written to the Dataset, OUTPUT, or logs.

Authentication & security requirements

  • The Actor never hard-codes credentials. apiKey is a secret input field (encrypted at rest, redacted from logs by Apify) and also accepts an API_KEY environment variable for local/CI use.
  • Nothing sensitive is ever written to the Dataset, OUTPUT, or logs — only booking fields and error messages.
  • The backend's /api/actor/* routes require a matching x-api-key header and are entirely separate from the salon's admin JWT login and the public customer-facing routes — a leaked Actor key cannot log in as the owner.
  • getAppointment deliberately omits the customer's phone and email from its output, even though the backend record has them — the Actor only returns what's needed to answer "what's the status of this booking?"

Example usage (as an automation/API call)

Via the Apify API (replace <TASK_OR_ACTOR_ID> and <TOKEN>):

curl -X POST "https://api.apify.com/v2/acts/<TASK_OR_ACTOR_ID>/run-sync-get-dataset-items?token=<TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"operation": "createAppointment",
"serviceId": "1",
"appointmentDate": "2026-10-15",
"appointmentTime": "10:30",
"customerName": "John",
"customerPhone": "0771234567",
"apiBaseUrl": "https://api.yoursalon.com",
"apiKey": "your-actor-api-key"
}'

An AI agent framework can wrap this same call as a "manage_salon_appointment" tool: pass the structured input, parse the JSON result, and speak the success/available/reason/status fields back to the user.

Limitations

  • Single salon per backend. salonId is validated against one configured id; there's no multi-tenant salon directory yet.
  • No staff/stylist scheduling. staffId is accepted but ignored — the connected backend has no staff model.
  • No notifications yet. No SMS/email/WhatsApp confirmations — status changes are visible via getAppointment and the owner's dashboard only.
  • No rescheduling operation — cancel and create a new one instead.
  • Requires the salon's backend to be reachable over HTTPS from the Actor; it cannot access a SQLite file directly (Actors run in isolated containers with no shared filesystem).
  • Idempotency is keyed on salon + service + date + time + phone/email; a legitimate second booking for the same person at the exact same slot on a retry looks identical to an accidental duplicate by design.
  • This has been tested locally (unit tests + real runs against the local backend) but not yet run on the Apify platform itself — see "Known limitations" in the final project report for what that leaves unverified.

FAQ

Does this scrape the salon's website? No. It calls a dedicated, API-key-protected backend API. Nothing about it depends on the website's HTML or design.

Can I point it at my own salon system instead of the demo backend? Yes — that's the intended use. Deploy server/ (or your own implementation of the same /api/actor/* contract) and set apiBaseUrl/apiKey accordingly.

What happens if I run the exact same booking request twice? You get the same appointment back with duplicate: true — no second booking is created.

Why is a new booking's status "pending" and not "confirmed"? Because that's what actually happens in the connected system — the salon owner confirms new requests from their dashboard. This Actor reports the real state rather than a more convenient-looking fake one.

Can it text or email the customer? Not yet — see Roadmap below.

Pricing suggestion

Pay per event is the best fit for an early product:

TierSuggested priceWhy
checkAvailability, getAppointmentFree or ~$0.005/callRead-only, cheap to serve — the goal is to make these free enough that agents check availability liberally
createAppointment~$0.05–$0.10/bookingThis is the actual business value delivered
cancelAppointmentFree or ~$0.005/callHousekeeping, not new value creation

Offer the first ~20–50 events free (Apify's standard free-tier pattern) so a developer or AI agent builder can integration-test before committing. Avoid charging for Actor compute time directly — this workload is a handful of HTTP calls, not a scrape or a crawl, so compute-based pricing would look disproportionate to what's delivered.

Roadmap (not implemented yet)

WhatsApp/email notifications, Google Calendar sync, reminders, rescheduling, multi-salon and multi-staff support, an AI receptionist integration layer, outbound webhooks on status change, and usage analytics. The /api/actor/* route group and this Actor's operation dispatch were both designed so each of these can be added as a new operation or backend route without restructuring what's here — see server/src/routes/actorApi.js and src/handler.js's switch statement as the extension points.