Skip to content

Aucip Appointments documentation

Laravel booking system for salons, spas and clinics · version 1.0.0

  • Requirements
  • Installation
  • Configuration
  • Feature usage
  • FAQ
  • Test suite
  • Troubleshooting (cron, queue, payments)
  • Changelog

Requirements

Component Requirement
PHP 8.3 or later (CI tests 8.3 and 8.4)
PHP extensions pdo_mysql (or pdo_sqlite), mbstring, openssl, intl, fileinfo, tokenizer, curl, bcmath, sodium
Database MySQL 8+ or MariaDB 10.6+; SQLite for evaluation
Cron one entry: * * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1
Web server any PHP host with the document root set to public/ (Apache/cPanel, Nginx + PHP-FPM)
Composer / Node not needed on the server: the zip ships vendor/, the built widget, admin and font assets
Writable storage/ and bootstrap/cache/

Hosting: shared hosting (cPanel, Plesk, DirectAdmin) works through the web installer, with no SSH, Composer, Node, Redis or Supervisor. On a VPS you can optionally run a real queue worker.

Outbound connections: aucip.com for licence and update checks, plus the payment and messaging providers you configure (Stripe, PayPal, Twilio or your SMS gateway, WhatsApp Cloud API, your mail server).

Installation

Shared hosting (cPanel, Plesk, DirectAdmin)

  1. Create a MySQL database and user in your control panel and note the host, name, user and password.
  2. Upload appointment-booking-dist.zip and extract it, e.g. to /home/you/booking.
  3. Point the (sub)domain's document root at /home/you/booking/public. If your host cannot change the document root, create a subdomain whose root you can set; do not expose the project root.
  4. Make storage/ and bootstrap/cache/ writable (755 directories are usually enough; 775 if PHP runs as a different user).
  5. Visit https://your-domain/install:
    • Requirements: every line must be green.
    • Database: enter the details from step 1. The installer tests the connection before writing anything.
    • Owner account and business name.
    • Licence key (optional): with a key the install activates it at aucip.com. Leave it empty, or install while aucip.com is unreachable, and the install still completes with a 14-day grace period; activate later in Admin → Licence.
    • Demo data: tick to load 2 branches (Manhattan and London), 6 specialists, 12 services and about 170 bookings over 6 weeks, which you can explore and delete.
  6. Add the cron entry (cPanel → Cron Jobs, "Once per minute"): * * * * * cd /home/you/booking && php artisan schedule:run >> /dev/null 2>&1 Use the full PHP path if needed, e.g. /usr/local/bin/php or /opt/cpanel/ea-php83/root/usr/bin/php.
  7. Sign in at /admin. The installer is locked afterwards (/install returns 404).

.env is written with permissions 0640.

VPS (Ubuntu 24.04, Nginx, PHP-FPM, MariaDB)

sudo apt install nginx mariadb-server php8.3-fpm php8.3-{mysql,mbstring,xml,intl,curl,bcmath,zip,sqlite3}
sudo mysql -e "CREATE DATABASE booking; CREATE USER 'booking'@'localhost' IDENTIFIED BY 'change-me'; GRANT ALL ON booking.* TO 'booking'@'localhost';"
sudo mkdir -p /var/www && cd /var/www && sudo unzip ~/appointment-booking-dist.zip && sudo mv appointment-booking booking
sudo chown -R www-data:www-data /var/www/booking/storage /var/www/booking/bootstrap/cache

Nginx server block:

server {
    listen 80;
    server_name booking.example.com;
    root /var/www/booking/public;
    index index.php;
    location / { try_files $uri $uri/ /index.php?$query_string; }
    location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; }
    location ~ /\.(?!well-known) { deny all; }
}

Then sudo certbot --nginx -d booking.example.com, open /install (or use the CLI below), and add the cron for www-data: * * * * * cd /var/www/booking && php artisan schedule:run >> /dev/null 2>&1 (sudo crontab -u www-data -e).

Optional on a VPS: run a real queue worker instead of the cron-driven one (php artisan queue:work --tries=3 under systemd or Supervisor) and set QUEUE_CONNECTION=database.

CLI install

cp .env.example .env    # set DB_* first
php artisan appointments:install --name="Your Name" --email=you@example.com --password='change-me' \
  --business="Your Salon" [--license=AUCIP-XXXXX-XXXXX-XXXXX-XXXXX] [--domain=booking.example.com] [--demo]

Without --license (or with aucip.com down) the install completes and starts the 14-day grace period. A key that the server explicitly rejects stops the install, so typos are caught; rerun without --license to activate later.

Updating

Admin → Licence → Check for updates (needs an active licence). Download the new zip, check its SHA-256 against the one shown, replace all files except .env and storage/, then run php artisan migrate --force and php artisan optimize:clear.

Public demo mode

Set DEMO=true in .env for a public demo install:

  • licensing is bypassed (no calls to aucip.com) and the licence banner is hidden;
  • Settings, gateway and provider keys, and the Licence page are read-only;
  • deletions are refused, and portal account deletion and password changes are disabled;
  • no email, SMS or WhatsApp message is delivered (messages are written to the log). Configure gateways and services before switching demo mode on, because Settings become read-only.

Demo logins (with demo data, password password)

Email Role
owner@demo.test Owner
manager@demo.test Manhattan branch manager
reception@demo.test Receptionist
staff@demo.test Staff (own calendar only)
customer@demo.test Customer portal
Change or delete these accounts before going live.

Configuration

First-time order: Branches → Service categories → Services → Staff , then Settings.

Branches, staff and services

Work through these in order the first time: Branches → Service categories → Services → Staff.

Branches (Admin → Business → Branches)

Each branch has its own name, address, timezone , currency , tax rate (included or added), weekly opening hours and holidays/closures. All slot times, reminders and calendar views use the branch timezone, so a London and a New York branch can run side by side. DST changes are handled automatically.

Users and roles (Admin → Business → Customers & team)

Role Can do
Owner Everything, including Settings, Licence and the audit log.
Branch manager Bookings, calendar, reports, payment and message logs for their branch ; edit staff, schedules and services offered at their branch.
Receptionist Create, move, cancel bookings and record desk payments at their branch.
Staff Their own calendar and bookings only. Link a staff profile to a user to give them a login.
Customer The customer portal only. Created automatically on the first booking.

Service categories and services (Admin → Catalogue)

A service has a name, category, duration, price, optional buffer before/after, capacity (for classes), specialists required (2 = team booking), extras (add-ons with their own price and duration), the branches it is offered at (with optional per-branch prices), and payment settings:

  • Pay at venue , deposit (fixed amount or percentage) or full prepayment.
  • Cancellation window (hours) and late cancellation refund %. Customers cancelling earlier get a full refund of what they paid; later, the percentage applies.

Services that no active staff member at a branch can perform are hidden from that branch's booking widget automatically.

Staff (Admin → Catalogue → Staff)

For each specialist set the branches they work at, the services they perform (optionally with their own price), the weekly schedule per branch with breaks, time off, and a weight used by weighted round-robin.

Scheduling

How slots are calculated

For a branch, service and day the engine intersects: branch opening hours (minus holidays) ∩ each qualified specialist's schedule (minus breaks and time off) ∩ free time (existing bookings plus buffers). Slots start every slot interval minutes (Settings → Scheduling). Times earlier than minimum lead time or beyond max days ahead are not offered. Classes (capacity > 1) stay bookable until full.

Two customers cannot get the same specialist at the same time: the booking is created inside a database transaction with a per-specialist slot claim, so concurrent requests get one success and a "Someone just took that time" message for the rest. Unpaid deposit holds are released after 30 minutes (appointments:release-holds).

"Any available" and round-robin

When a customer picks Any available specialist , a qualified free specialist is chosen with the strategy from Settings → Scheduling :

  • Least recently booked : whoever had the oldest last booking. Spreads work evenly over time.
  • Fewest bookings that day : evens out each day's diary.
  • Weighted priority : higher staff weight gets proportionally more bookings (e.g. a senior stylist with weight 1 and two juniors with weight 2). Specialists who are not qualified for the service, not at the branch, or not working never get assigned.

Team bookings (2+ specialists together)

Set Specialists required on the service (e.g. 2 for a four-hands massage). The widget skips the specialist step, only offers times when enough qualified specialists are free together, and books them all on one appointment. The calendar shows the booking in every assigned specialist's column, and moving it in the calendar moves it for all of them.

Admin calendar

Day or week view, staff or branch columns, a red line for "now". Drag a booking to another time or column to reschedule; conflicts are refused with a message. Click a booking to edit it.

Payments and deposits

Settings → Payments. Every gateway has a Sandbox/Live switch; keys are stored encrypted. A live key is refused while the gateway is in sandbox mode.

Stripe (cards, Apple Pay, Google Pay; SCA ready)

  1. Stripe Dashboard → Developers → API keys: paste the publishable and secret keys.
  2. Developers → Webhooks → add endpoint https://your-domain/api/webhooks/stripe with events payment_intent.succeeded and payment_intent.payment_failed. (Refunds are made through the Stripe API by the app, so no refund webhook is needed.) Paste the signing secret.
  3. Tick Enable Stripe. Test cards: 4242 4242 4242 4242, any future date and CVC; 4000 0025 0000 3155 triggers 3-D Secure.

PayPal (Orders v2)

  1. developer.paypal.com → Apps & Credentials → create an app (Sandbox first). Paste the client id and secret.
  2. Add a webhook for https://your-domain/api/webhooks/paypal with PAYMENT.CAPTURE.COMPLETED; paste the webhook id.
  3. Tick Enable PayPal.

How deposits work

  • The booking is created as pending and the slot is held. After payment it becomes confirmed. Unpaid holds are released automatically after 30 minutes (appointments:release-holds --minutes=30 in routes/console.php).
  • Webhooks are verified (Stripe signature, PayPal verify-webhook API), de-duplicated by event id, and only mark a payment paid when the amount (and currency) matches what was requested.
  • The balance can be paid online from the customer portal, or recorded at the desk (Bookings → ⋯ → Record desk payment).
  • Cancellations refund automatically through the original gateway according to the service's cancellation rule. The portal shows the refund amount before the customer confirms.
  • Reports → Revenue shows online, desk and refunded amounts per branch and can be exported to CSV.

Messages: email, SMS and WhatsApp

Settings → Messaging. Choose the enabled channels and the reminder offsets in minutes before the appointment (e.g. 1440,120 = 24 h and 2 h). Events: confirmation, reminder, rescheduled, cancelled and an optional review request after the visit. Messages are queued, retried up to 3 times, and every attempt is listed in Logs → Message delivery log. Customers can opt out per channel in the portal; opted-out channels are logged as skipped.

Every driver has a Log only mode that records messages without sending them (used by the demo and for testing).

Email

Any Laravel mailer: set MAIL_MAILER, MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, MAIL_FROM_ADDRESS in .env (SMTP), or MAIL_MAILER=ses with AWS keys. With MAIL_MAILER=log, emails are written to storage/logs/mail.log.

SMS

  • Twilio: Account SID, auth token and a sender number (or Messaging Service SID) from console.twilio.com.
  • Generic HTTP gateway: URL and bearer token. Aucip POSTs JSON {"to": "+15551234567", "message": "…"} and treats any 2xx as sent. Phone numbers should be stored in international format (+44…).

WhatsApp Cloud API (Meta)

  1. In Meta Business Manager create a WhatsApp Business app and add a phone number. Copy the phone number id and a permanent access token (System user → generate token with whatsapp_business_messaging).
  2. Business-initiated messages must use approved templates. Create templates (e.g. booking_confirmed, appointment_reminder) with body variables {{1}} customer name, {{2}} service, {{3}} date and time (branch timezone), {{4}} branch, {{5}} booking reference.
  3. In Settings enter the phone number id, token, template language (e.g. en_US) and the event-to-template map, e.g. confirmation:booking_confirmed, reminder:appointment_reminder, rescheduled:booking_moved, cancelled:booking_cancelled.
  4. Customers must opt in to WhatsApp in the widget or the portal.

Editing message text

Email and SMS texts live in lang/{en,es,ar}/notifications.php and are sent in the customer's language.

Licence and updates (Admin → Licence)

Updates are free while the licence is active. Support lasts 6 months and is renewable on aucip.com.

  • Activation: the web installer and appointments:install --license=… activate the key (optional at install time; without it the 14-day grace period starts) against https://aucip.com/api/license/activate. You can also activate, re-check or deactivate (to move domains) in Admin → Licence.
  • Daily re-check: appointments:license-check is scheduled hourly but only calls /api/license/verify once every 24 hours (failed checks are retried with back-off from 1 hour up to 24 hours).
  • Signed answers: every licence and update answer must carry an Ed25519 signature that verifies against the public key pinned in config/aucip.php (aQ4vhiKP6LNsHvW+jIQhqysMHtiNSwOy6/47mTCnOP0=). The key is never fetched at runtime. Answers older than 24 hours are rejected as replays, and only the values inside the signed payload are used.
  • 14-day grace: if the server is unreachable, the signature is bad, or the licence is rejected, everything keeps working for 14 days from the last successful check, and the owner sees a banner. After that, only admin Settings and updates lock until the licence verifies again.
  • Never affected: the public booking pages, the widget and its API, the customer portal, payments and webhooks, reminders, the calendar, bookings and all customer data never consult the licence.

Exactly what is sent to aucip.com (and nothing else)

Call When Fields
POST /api/license/activate install, Admin → Licence license_key, product (aucip-appointments), domain, instance_id (random UUID made at install), version, environment (production/staging)
POST /api/license/verify once a day same as activate
POST /api/license/deactivate Admin → Licence → Deactivate license_key, product, domain, instance_id
GET /api/updates/check Admin → Licence → Check for updates product, version, channel
POST /api/updates/download Admin → Licence → Get version license_key, product, domain

No customer, booking, staff, user, payment, usage or server data is sent. The only other outbound calls go to the payment and messaging providers you configure (Stripe, PayPal, Twilio or your SMS gateway, WhatsApp Cloud API, your mail server).

Updates are delivered as a signed, time-limited download link plus the SHA-256 checksum from the signed answer. Check the checksum and replace the files yourself (keep .env and storage/), then run php artisan migrate --force. There is no automatic in-place updater yet.

Feature usage

Booking widget reference

Copy the snippet from Settings → Widget , or write it yourself:

<div id="booking"></div>
<script src="https://booking.example.com/widget/aucip-widget.js" data-aucip-booking
        data-mode="inline" data-target="#booking" defer></script>
Attribute Values Default
data-mode inline (in the page) or popup (a button that opens a modal) inline
data-target CSS selector to render into (inline mode) right after the script tag
data-branch, data-service, data-staff ids to preselect and skip steps none
data-lang en, es, ar (Arabic switches to right-to-left) site default
data-color brand colour, e.g. #1f5c4e Settings → Widget
data-radius corner radius in px 10
data-label, data-title popup button text / iframe title "Book now" / "Book an appointment"

The loader script is about 1.1 KB gzipped; the booking app inside the iframe is 9.9 KB gzipped (JavaScript), well under a 60 KB budget. The iframe resizes to its content.

WordPress, Webflow, Wix, Squarespace

Paste the snippet into a Custom HTML block (WordPress), an Embed element (Webflow), or a Code block. Nothing else is required.

Content Security Policy

If your site sends a CSP header, allow the booking domain in script-src and frame-src. The widget itself uses no eval and no inline styles. Limit which sites may embed it with Settings → Widget → Allowed origins (sent as frame-ancestors).

Events (analytics)

window.addEventListener('aucip:booking', (e) => console.log(e.detail.type, e.detail.data));

Types: opened, closed (popup), ready, branch_selected, service_selected, slot_selected, booking_created, payment_completed, booking_confirmed, error ({status} only). If Google Tag Manager is present, each event is also pushed to dataLayer as aucip_<type>. Events never contain names, emails or phone numbers.

JSON API

The widget uses /api/v1: GET config, GET branches, GET branches/{id}/services, GET branches/{id}/services/{id}/staff, GET slots?branch&service&date[&staff], GET slots/summary?branch&service&from&days, POST bookings, GET bookings/{ref}?email= and GET bookings/{ref}/ics?email=. Rate-limited to 120 requests/min (20 bookings/min) per IP. Bookings for an email that already has an account never change that account's details.

Customer portal

https://your-domain/portal. Customers sign in with a one-time email link (valid 30 minutes, works once) or a password they set in the portal. Five failed password attempts for an email lock that email for a minute; successful sign-ins are never limited.

Customers can:

  • see upcoming and past appointments in the branch's local time, with a countdown;
  • reschedule within the service's rules (week strip, times grouped by morning/afternoon/evening);
  • cancel : a dialog shows exactly how much will be refunded before they confirm;
  • pay the balance online, download receipts and add appointments to their calendar (.ics);
  • choose email/SMS/WhatsApp notifications and change their phone number and password;
  • download all their data (JSON) or erase their account (GDPR). Receipts are kept anonymised for accounting.

Set Settings → General → Booking page URL to the page on your website that holds the widget; the portal's "Book now" buttons link there.

Translations

Shipped: English, Spanish and Arabic (right-to-left). The portal and widget pick the language from ?lang=xx (remembered in the session), then the signed-in customer's language, then APP_LOCALE. For the widget, set data-lang on the snippet.

What File
Portal and emails lang/{locale}/portal.php, lang/{locale}/notifications.php
Booking widget resources/widget/i18n.ts (rebuild with npm ci && npm run build:widget)

Adding a language (e.g. French)

  1. Copy lang/en to lang/fr and translate the values. Keep :placeholders unchanged.
  2. Add fr to the supported list in app/Http/Middleware/SetLocale.php (and to the RTL list if it is a right-to-left language).
  3. Widget: add a fr dictionary to resources/widget/i18n.ts and rebuild the widget. Missing keys fall back to English.

Importing from another system (CSV)

Admin → Customers & team → Import CSV (customers) and Admin → Services → Import CSV (services). The first row must be a header; UTF-8 or Excel CSV both work. Re-importing the same file updates existing rows instead of duplicating them (matched by email for customers, by name for services).

Customers

Column (any of these names) Required
name, full_name, customer_name, client_name yes
email, email_address, customer_email yes
phone, mobile, phone_number, telephone no

Services

Column Notes
name / service_name / title required
category created if missing
duration / duration_minutes / minutes minutes
price / amount / cost number
buffer_before, buffer_after minutes
payment_mode venue, deposit or full
deposit_type, deposit_value fixed or percent, and the value
branches branch names (or slugs) separated by a vertical bar (|); added to existing assignments. Leave empty and assign branches in the admin.
description text

From other booking systems

Export customers and services from your current booking system to CSV. The common column names above are recognised as they are, so most exports import without editing. Upcoming appointments are not imported automatically; enter them in Bookings → New booking, or ask support for help.

FAQ

Can I run several locations? Yes. Each branch has its own hours, holidays, timezone, currency, tax, prices and managers. Branch managers and receptionists only see their own branch.

Can two specialists be booked together? Yes. Set Specialists required on the service (e.g. 2 for a four-hands massage). Only times when enough qualified specialists are free together are offered, and the booking appears in each of their calendar columns.

How are double bookings prevented? Each booking is created in a database transaction with a per-specialist slot claim. When two customers pick the same time, one succeeds and the other sees "Someone just took that time".

Which payment gateways are included? Stripe (Payment Intents, SCA-ready, Apple Pay and Google Pay through Stripe) and PayPal Orders v2, each with a Sandbox/Live switch. Per service you choose pay at venue, a fixed or percentage deposit, or full prepayment.

How do refunds work? Each service has a cancellation window and a late-cancellation refund percentage. Refunds go back through the original gateway automatically, and the portal shows the amount before the customer confirms.

Do I need a Twilio or WhatsApp account? Only if you want SMS or WhatsApp. Email works with any Laravel mailer. Each channel has a Log only mode for testing.

Will the widget slow my site down? The loader is about 1.1 KB gzipped and the booking app runs in an iframe (9.9 KB gzipped JavaScript). It's CSP-safe, with no inline scripts or styles.

Does it work on WordPress, Webflow, Wix or Squarespace? Yes. Paste the snippet into a Custom HTML, Embed or Code block.

Which languages are included? English, Spanish and Arabic (right-to-left, with IBM Plex Sans Arabic). Add more by copying lang/en and the widget dictionary.

Can I import customers and services from my old system? Yes, by CSV, including exports from other booking systems. Upcoming appointments are entered manually. See Importing from another system (CSV).

Does it need SSH, Docker or Redis? No. Upload, run the web installer and add one cron entry. A VPS can optionally run a real queue worker.

What does the licence check send? The licence key, product, domain, a random install id, the version and production/staging, once a day. No customer, booking, payment or usage data.

What if the licence can 't be verified? You get a 14-day grace period with a banner. After that, admin Settings and updates lock until the licence verifies again. Booking pages, the widget, the portal, payments, reminders, the calendar and your data keep working.

Which licence do I need? Regular is $35 and Extended is $99. Regular licence: use in one end product that end users are not charged for. Free updates while licensed and 6 months of support (renewable). Extended licence: use in one end product that end users are charged for (for example a paid SaaS or paid membership). Free updates while licensed and 6 months of support (renewable). One licence = one end product. Free local installs (localhost/.test) plus up to 3 staging installs on staging subdomains per licence; staging doesn't count against your production domain. Each additional end product needs another licence. Installation service: paid add-on, sold separately.

Updates and support? Updates are free while your licence is active. Support is 6 months and can be renewed.

Can customers delete their data? Yes. The portal offers a JSON export and account erasure; receipts are kept anonymised for accounting.

Test suite

The zip includes the Pest test suite, Larastan and Pint configuration, and a GitHub Actions workflow. It ships production dependencies only (so it runs on shared hosting), so install the dev tools first:

composer install                       # adds Pest, Larastan, Pint (Composer 2, PHP 8.3+)
php artisan test                       # Pest feature suite on in-memory SQLite
TEST_MYSQL_DB=appt_test TEST_MYSQL_USER=root TEST_MYSQL_PASSWORD=secret php artisan test --filter=Concurrency
vendor/bin/phpstan analyse             # Larastan level 6
vendor/bin/pint --test                 # code style

Without composer install, php artisan test prints these instructions instead of running.

Feature tests cover the slot engine, round-robin, team bookings, payments and webhooks, the widget API, the portal, reminders, the installer, licensing and a check that no unexpected outbound calls are made. The concurrency test races real bookings against MySQL/MariaDB.

All external HTTP is faked (Http::preventStrayRequests()), so tests never need real Stripe, PayPal, Twilio or Meta credentials and never call aucip.com.

CI: .github/workflows/tests.yml runs Pint, Larastan, the suite with a 70% coverage gate on PHP 8.3 and 8.4, and the MariaDB concurrency test.

Rebuilding front-end assets: npm ci && npm run build && npm run build:widget.

Troubleshooting (cron, queue, payments)

The single cron entry runs php artisan schedule:run every minute, which runs:

Task When
queue:work --stop-when-empty every minute: sends queued emails, SMS and WhatsApp
appointments:reminders every 5 minutes
appointments:release-holds every 5 minutes: frees unpaid deposit holds
appointments:license-check hourly, but contacts aucip.com at most once a day

Reminders are not sent

  1. Is cron running? php artisan schedule:list shows next run times. Run php artisan schedule:run by hand and watch for errors.
  2. Check Logs → Message delivery log : skipped (opted out) , skipped (no recipient) , or failed with the provider's error.
  3. Is the channel enabled in Settings → Messaging, and the driver not Log only?
  4. Stuck jobs: php artisan queue:failed and php artisan queue:retry all.

Emails go nowhere

With MAIL_MAILER=log they are written to storage/logs/mail.log. Otherwise check SMTP details and storage/logs/laravel.log.

Payments stay "pending"

The webhook did not arrive or was rejected. Check the webhook URL and secret in Stripe/PayPal, and their delivery logs. Unpaid holds are released automatically after 30 minutes.

Times look wrong by an hour

Check the branch timezone (not the server's). All times are stored in UTC and shown in the branch timezone.

Licence banner

"Grace period": the daily check could not confirm the licence; everything keeps working. Open Admin → Licence and click Re-check now. After 14 days only Settings and updates lock; bookings never stop.

Other

  • Blank page or 500: run php artisan optimize:clear, check storage/logs/laravel.log, and make sure storage/ is writable.
  • "419 Page expired": the session cookie was blocked; check APP_URL matches the domain (https vs http).
  • Expected booking conflicts (two people choosing the same time) are not logged as errors.

Changelog

1.0.0

  • First public release.
  • Multi-branch scheduling with per-branch hours, holidays, timezone, currency, tax and prices.
  • Round-robin (least recently booked, fewest that day, weighted) and team bookings with 2+ specialists.
  • Stripe and PayPal deposits, full prepayment or pay at venue; automatic refunds by cancellation window.
  • Email, SMS (Twilio or HTTP gateway) and WhatsApp Cloud API reminders, with per-channel opt-out.
  • Embeddable iframe widget (1.1 KB loader, 9.9 KB app gzipped) with analytics events.
  • Customer portal: reschedule, cancel with refund preview, pay balance, receipts, .ics, data export and erasure.
  • English, Spanish and Arabic (RTL); CSV import of customers and services.
  • Web and CLI installer, public demo mode, signed daily licence check with 14-day grace.

Aucip Appointments · aucip.com