What You'll Learn
1. Why B2B SaaS Products Need Booking
Scheduling and booking are no longer optional features for B2B SaaS platforms. Your customers โ whether they're salons, clinics, consulting firms, or rental businesses โ need to manage appointments. If your platform doesn't offer it, they'll cobble together Calendly or a booking widget, fragmenting their workflow.
Booking as a growth driver:
- Reduce churn โ Customers who use your booking feature have higher switching costs and longer retention
- Increase revenue โ Charge per-booking fees or include scheduling in a higher-tier plan
- Platform stickiness โ Booking creates daily engagement as customers check and manage their appointments
- Data ownership โ You own the booking data, enabling analytics, forecasting, and upsells
๐ก Market reality: Every major vertical SaaS โ from Mindbody (wellness) to Acuity (professional services) to Jane (clinics) โ succeeded by making booking a core part of their platform. The bar for new SaaS products is now even higher: customers expect built-in scheduling from day one.
2. Multi-Tenant Architecture for Booking
The most important architectural decision when adding booking to a SaaS product is tenant isolation. Each of your customers (tenants) needs their own independent booking system โ their own resources, availability rules, customer data, and payment configuration โ completely isolated from other tenants.
Architecture Overview
โ scoped API keys โ Stripe Connect accounts โ isolated data โ
AvailEngine handles tenant isolation through scoped API keys. Each tenant in your platform gets a unique API key with permissions scoped to their business only. This means:
- Complete data isolation โ Tenant A cannot access Tenant B's bookings
- Independent configuration โ Each tenant sets their own resources, hours, prices, and payment accounts
- Per-key rate limiting โ No single tenant can overwhelm the API for others
- Stripe Connect per-tenant โ Every tenant connects their own Stripe account for direct deposits
Creating Scoped API Keys for Each Tenant
When a new customer signs up for your platform, provision a scoped API key for them:
# Create a scoped API key for a new tenant
curl -X POST https://api.availengine.com/v1/api-keys \
-H "Authorization: Bearer {YOUR_MASTER_KEY}" \
-H "Content-Type: application/json" \
-d '{
"label": "Tenant A - Happy Salon",
"scopes": ["bookings:read", "bookings:write", "resources:read"],
"rate_limit": 100,
"rate_limit_period": "minute"
}'
# Response
{
"id": "key_tenant_a",
"key": "ae_live_abc123def456...",
"label": "Tenant A - Happy Salon",
"scopes": ["bookings:read", "bookings:write", "resources:read"],
"rate_limit": 100,
"rate_limit_period": "minute",
"created_at": "2026-07-21T10:00:00Z"
}
Store the returned key securely in your database alongside the tenant's record. Your backend uses this key when making API calls on behalf of that tenant.
3. Stripe Connect: Platform Fees & Payouts
For B2B SaaS platforms, booking payments typically involve three parties: the end customer (booking a service), the tenant (service provider), and you (the platform). Stripe Connect is the industry standard for this tri-party model.
The Tri-Party Payment Flow
# AvailEngine handles the complexity. Just pass:
{
"stripe_connect": {
"account_id": "acct_tenant_stripe_id",
"platform_fee": 250, // โฌ2.50 platform fee (in cents)
"deposit_amount": 5000, // โฌ50.00 deposit (in cents)
"transfer_amount": 4750 // โฌ47.50 to tenant after fee
}
}
# Full booking creation with Stripe Connect
curl -X POST https://api.availengine.com/v1/bookings \
-H "Authorization: Bearer {TENANT_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: stripe-payment-req-001" \
-d '{
"resource_id": "res_abc123",
"start_time": "2026-07-25T10:00:00+02:00",
"end_time": "2026-07-25T11:00:00+02:00",
"customer": {
"name": "Bob Smith",
"email": "bob@example.com"
},
"payment": {
"method": "stripe_connect",
"stripe_connect_account": "acct_tenant_stripe_id",
"deposit_amount_cents": 5000,
"platform_fee_cents": 250,
"currency": "eur"
}
}'
What happens behind the scenes:
- AvailEngine creates a Stripe PaymentIntent on the tenant's Stripe account
- A โฌ50.00 authorization hold is placed on the customer's card
- A โฌ2.50 platform fee is automatically transferred to your platform's Stripe account
- On booking completion (service rendered), the remaining โฌ47.50 is captured to the tenant
- If the booking is cancelled, the deposit is released and the platform fee is returned
๐ก Idempotency is critical with payments. Always include an Idempotency-Key header. If a network timeout occurs, retry the request with the same key โ AvailEngine will not create a duplicate charge or booking.
4. Scoped API Keys & Tenant Isolation
Proper tenant isolation is not just about data privacy โ it's also about operational safety. A misbehaving tenant shouldn't degrade your API performance or access data they shouldn't see.
AvailEngine supports three isolation mechanisms:
๐ Scoped API Keys
Each key has a defined set of permissions:
bookings:read / writeresources:read / writeavailability:readwebhooks:read / writesettings:read / write
โก Per-Key Rate Limits
Set granular limits per API key:
- Requests per minute
- Requests per hour
- Concurrent requests
- Different limits for different tenants (by plan tier)
# Python: Tenant-aware API client
class AvailEngineClient:
def __init__(self, api_key, tenant_id):
self.api_key = api_key
self.tenant_id = tenant_id
self.base_url = "https://api.availengine.com/v1"
def create_booking(self, resource_id, start, end, customer):
return requests.post(
f"{self.base_url}/bookings",
headers={
"Authorization": f"Bearer {self.api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"resource_id": resource_id,
"start_time": start,
"end_time": end,
"customer": customer,
"metadata": {"tenant_id": self.tenant_id},
},
).json()
# Usage per tenant
tenant_a = AvailEngineClient("ae_live_abc123...", "tenant_a")
tenant_b = AvailEngineClient("ae_live_def456...", "tenant_b")
# Each client operates in its own isolated scope
booking_a = tenant_a.create_booking("res_1", "...", "...", {...})
booking_b = tenant_b.create_booking("res_2", "...", "...", {...})
5. White-Label Booking UI
Your SaaS customers expect the booking experience to look like your brand, not a third-party widget. With an API-first approach, you build the UI and AvailEngine powers the backend โ the booking flow is indistinguishable from the rest of your app.
White-Label Checklist
- Custom domain โ Booking pages live on your tenant's domain (e.g.,
book.happysalon.com) - Your brand โ Colors, fonts, logos, and spacing match your platform
- Your UX patterns โ Transitions, form styles, error messages, and loading states are consistent
- No third-party branding โ No "Powered by" badges, no external iframe chrome
- Custom email templates โ Confirmation and reminder emails use your brand templates
- Custom confirmation pages โ Post-booking flows (reschedule, cancel) stay in your app
๐ก API-first = inherently white-label. Because you build the booking UI yourself using REST API calls, every pixel is yours. There's no iframe to theme, no SDK with brand baggage โ just clean API endpoints that return JSON.
6. Integration Code Walkthrough
Here's a complete end-to-end integration of booking into a fictional SaaS platform called "ProManage." The flow: a tenant admin sets up their services โ an end customer books โ the platform collects a fee.
Step 1: Onboarding โ Provision a New Tenant
# Called when a new customer signs up for ProManage
async function provisionTenant(tenantName) {
// 1. Create a scoped API key for the tenant
const keyRes = await fetch("https://api.availengine.com/v1/api-keys", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.AVAIL_MASTER_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
label: `${tenantName} - ProManage`,
scopes: ["bookings:*", "resources:*", "availability:read"],
rate_limit: 100,
}),
});
const { key } = await keyRes.json();
// 2. Store key in your database (encrypted)
await db.tenants.create({
name: tenantName,
availengine_key: encrypt(key),
});
return key;
}
Step 2: Tenant Sets Up Resources
# Tenant admin configures their services via your UI
POST /api/tenants/:id/resources
{
"name": "Haircut - 30min",
"price_cents": 3500,
"slot_duration": 30,
"buffer_minutes": 10
}
# Your backend proxies to AvailEngine
POST https://api.availengine.com/v1/resources
Authorization: Bearer {tenant_scoped_key}
{
"name": "Haircut - 30min",
"slot_duration_minutes": 30,
"buffer_minutes": 10,
"daily_start": "09:00",
"daily_end": "18:00",
"timezone": "Europe/Berlin"
}
Step 3: Customer Books via Your UI
# Your frontend calls YOUR backend (not AvailEngine directly)
async function handleBooking(tenantId, bookingData) {
// Fetch the tenant's API key from your secure backend
const response = await fetch(`/api/tenants/${tenantId}/bookings`, {
method: "POST",
body: JSON.stringify(bookingData),
});
return response.json();
}
# Your backend creates the booking via AvailEngine
POST https://api.availengine.com/v1/bookings
Authorization: Bearer {tenant_scoped_key}
Idempotency-Key: unique-op-456
{
"resource_id": "res_haircut",
"start_time": "2026-07-25T14:00:00+02:00",
"end_time": "2026-07-25T14:30:00+02:00",
"customer": { "name": "Sarah", "email": "sarah@example.com" },
"payment": {
"method": "stripe_connect",
"stripe_connect_account": "acct_tenant_stripe",
"deposit_amount_cents": 3500,
"platform_fee_cents": 350,
"currency": "eur"
}
}
Step 4: Webhook for Real-Time Sync
# AvailEngine sends webhooks when booking state changes.
# Listen for booking.created to update your local database.
# Example webhook payload (booking.created)
{
"event": "booking.created",
"data": {
"id": "bkg_xyz789",
"resource_id": "res_haircut",
"status": "confirmed",
"start_time": "2026-07-25T14:00:00+02:00",
"end_time": "2026-07-25T14:30:00+02:00",
"customer": { "name": "Sarah", "email": "sarah@example.com" },
"payment": {
"status": "deposit_collected",
"amount_cents": 3500,
"platform_fee_cents": 350
}
}
}
๐ก Architecture best practice: Your frontend always talks to your backend, and your backend uses the scoped API key to call AvailEngine. This keeps tenant API keys secure (never exposed client-side) and gives you control over caching, validation, and rate limiting.
7. Production Checklist
Before launching booking for your SaaS customers, complete this checklist:
โ๏ธ Setup & Configuration
- โ Create a production API key
- โ Configure webhook endpoints (HTTPS only)
- โ Verify HMAC signature on all webhooks
- โ Set up Stripe Connect for platform fees
- โ Configure Google Calendar sync per tenant
- โ Set per-key rate limits by tenant plan tier
๐ก๏ธ Security & Reliability
- โ Store tenant API keys encrypted at rest
- โ Implement idempotency on all booking mutations
- โ Monitor webhook delivery with retries
- โ Test cancellation and refund flows
- โ Set up alerting on failed bookings
- โ Review pricing for your booking volume
Ready to launch? Read the full Getting Started guide for production setup, or jump to the Pricing page to find the right plan.
Add Booking to Your SaaS Platform Today
Free sandbox with no signup. Multi-tenant architecture, Stripe Connect, and white-label UI โ ready in minutes.