๐Ÿ“– Tutorial ยท 10 min read

How to Embed a Booking System in Your App โ€” Complete Developer Guide

Learn the trade-offs between building your own booking system vs using an API, and follow a practical tutorial to embed booking into any app using the AvailEngine REST API.

1. Build vs Buy: Should You Build Your Own Booking System?

Every team considers building their own booking system at some point. After all, how hard can availability and scheduling be? The reality is more nuanced.

๐Ÿ› ๏ธ Build In-House

When it makes sense:

  • You have extremely specialized scheduling logic (e.g., medical resource allocation)
  • You need full control over every aspect of the UX
  • You already have calendar/availability infrastructure

Cost: 3โ€“6 months engineering time + ongoing maintenance. Typical team cost: $50kโ€“$150k+

๐Ÿ”Œ Buy (Booking API)

When it makes sense:

  • You want to launch in days, not months
  • You need multi-tenant isolation out of the box
  • Stripe deposits, webhooks, and idempotency matter
  • You want AI agent compatibility (Cursor, Claude Code)

Cost: Free to start, โ‚ฌ129/mo at scale. Zero maintenance overhead.

๐Ÿ’ก Recommendation: Start with a booking API to validate your product-market fit. You can always build custom scheduling later if your needs become highly specialized. Most teams never need to โ€” modern booking APIs handle 95%+ of use cases.

2. API-First vs Iframe Embedding

Not all "embed booking" approaches are equal. There are fundamentally two ways to add booking to your app:

๐Ÿ“ฆ Iframe / SDK Embed

Drop in a pre-built UI via iframe or JavaScript SDK (Calendly-style).

  • Fastest to implement (minutes)
  • No control over UI/UX
  • Limited programmatic control
  • No data ownership
  • Branding mismatch with your app
  • Poor mobile/web responsiveness

๐Ÿงฉ API-First Embed

Use a REST API and build your own booking UI that matches your brand.

  • Full control over UI/UX
  • Seamless integration into your app
  • Complete data ownership
  • Programmatic booking management
  • White-label ready
  • AI agent compatible

For developer-built apps, API-first embedding is almost always the right choice. It gives you full ownership of the user experience while the API handles the complex scheduling logic, payment processing, and webhook delivery.

3. Prerequisites & Setup

Before you start, you need:

The AvailEngine sandbox lets you test without any setup. Here's the sandbox base URL:

# Sandbox (no API key needed)
SANDBOX_URL=https://api.availengine.com/v1/sandbox

# Production (requires API key)
PROD_URL=https://api.availengine.com/v1

๐Ÿ’ก The sandbox is a fully functional booking system with seeded data. You can create, read, update, and delete bookings just like in production โ€” no signup required. Perfect for prototyping.

4. Step 1: Create Resources with the API

Every booking in AvailEngine is tied to a resource โ€” the thing being booked (a meeting room, a stylist, a car wash bay). Let's create one:

# Create a resource (sandbox)
curl -X POST https://api.availengine.com/v1/sandbox/resources \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Consultation Room A",
    "type": "meeting_room",
    "timezone": "Europe/Berlin",
    "slot_duration_minutes": 30,
    "buffer_minutes": 15,
    "daily_start": "09:00",
    "daily_end": "17:00"
  }'

Response:

{
  "id": "res_abc123",
  "name": "Consultation Room A",
  "type": "meeting_room",
  "slot_duration_minutes": 30,
  "buffer_minutes": 15,
  "daily_start": "09:00",
  "daily_end": "17:00",
  "timezone": "Europe/Berlin"
}

5. Step 2: Query Availability Slots

Now that you have a resource, check its availability for a given date range:

# Get available slots for the next 7 days
curl "https://api.availengine.com/v1/sandbox/availability?resource_id=res_abc123&date_from=2026-07-22&date_to=2026-07-29"

Response:

{
  "slots": [
    {
      "start": "2026-07-22T09:00:00+02:00",
      "end": "2026-07-22T09:30:00+02:00",
      "available": true
    },
    {
      "start": "2026-07-22T09:45:00+02:00",
      "end": "2026-07-22T10:15:00+02:00",
      "available": true
    }
    // ... more slots
  ]
}

Notice the 15-minute buffer between slots โ€” configured when you created the resource. AvailEngine handles buffer times, overlapping bookings, and recurring availability windows automatically.

6. Step 3: Create a Booking

Now let's create a booking in three languages. Each example creates the same booking โ€” a 30-minute session in Consultation Room A.

curl

curl -X POST https://api.availengine.com/v1/sandbox/bookings \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-req-id-001" \
  -d '{
    "resource_id": "res_abc123",
    "start_time": "2026-07-22T09:00:00+02:00",
    "end_time": "2026-07-22T09:30:00+02:00",
    "customer": {
      "name": "Alice Johnson",
      "email": "alice@example.com",
      "phone": "+49123456789"
    },
    "notes": "Initial consultation"
  }'

Python

import requests
import uuid

BASE_URL = "https://api.availengine.com/v1/sandbox"

booking = requests.post(
    f"{BASE_URL}/bookings",
    headers={
        "Content-Type": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "resource_id": "res_abc123",
        "start_time": "2026-07-22T09:00:00+02:00",
        "end_time": "2026-07-22T09:30:00+02:00",
        "customer": {
            "name": "Alice Johnson",
            "email": "alice@example.com",
        },
        "notes": "Initial consultation",
    },
).json()

print(booking)
# { "id": "bkg_xyz789", "status": "confirmed", ... }

Node.js

const BASE_URL = "https://api.availengine.com/v1/sandbox";
const crypto = require("crypto");

const response = await fetch(`${BASE_URL}/bookings`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    resource_id: "res_abc123",
    start_time: "2026-07-22T09:00:00+02:00",
    end_time: "2026-07-22T09:30:00+02:00",
    customer: { name: "Alice Johnson", email: "alice@example.com" },
    notes: "Initial consultation",
  }),
});

const booking = await response.json();
console.log(booking);
// { id: "bkg_xyz789", status: "confirmed", ... }

๐Ÿ’ก Idempotency is critical. Always send an Idempotency-Key header. If the request fails (network timeout, 500 error), you can safely retry with the same key โ€” AvailEngine will return the original result instead of creating a duplicate booking. This is essential for payment-integrated bookings.

7. Step 4: Handle Webhooks & Stripe Deposits

Booking systems are event-driven by nature. AvailEngine sends HMAC-signed webhooks for 10+ event types including:

  • booking.created โ€” a new booking was confirmed
  • booking.cancelled โ€” a booking was cancelled
  • booking.rescheduled โ€” time slot changed
  • deposit.collected โ€” Stripe deposit was successfully charged
  • deposit.refunded โ€” deposit was returned on cancellation

Here's how to verify a webhook signature in Node.js:

const crypto = require("crypto");

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(JSON.stringify(payload))
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Usage in your webhook endpoint
app.post("/webhooks/availengine", (req, res) => {
  const signature = req.headers["x-availengine-signature"];
  if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  // Process the webhook event
  handleBookingEvent(req.body);
  res.status(200).send("OK");
});

Stripe deposits are handled at booking creation. Pass a deposit amount and the API handles the Stripe PaymentIntent lifecycle โ€” authorization, capture, and refund โ€” with proper idempotency for payment retries.

8. Step 5: Build Your Booking UI

With the API working, you now build a native booking UI in your app. A typical booking flow has these screens:

  1. Service/Resource selector โ€” List available resources (rooms, staff, time slots)
  2. Date & Time picker โ€” Fetch availability from the API, render your own calendar widget
  3. Customer details form โ€” Collect name, email, phone
  4. Payment (optional) โ€” Use Stripe Elements to collect payment, then create the booking with a deposit
  5. Confirmation โ€” Display booking details and sync to Google Calendar via AvailEngine

Because you own the UI, every element matches your brand โ€” colors, fonts, spacing, animations. No iframe chrome, no third-party branding.

9. Next Steps & Production Deployment

You now have a working embedded booking system. Here's what to do next:

  • Complete the official Getting Started guide โ†’
  • Set up your production API key in the Developer Portal
  • Configure Stripe Connect to collect payments on bookings
  • Enable Google Calendar sync for automatic calendar integration
  • Set up webhook endpoints for real-time booking notifications
  • Review pricing โ€” Free โ‚ฌ0/mo for 500 bookings, Scale โ‚ฌ129/mo for 50k bookings

Ready to Embed Booking in Your App?

Try the sandbox with zero signup. Create a booking in 30 seconds.

Get Started โ†’ View Pricing