ZvenBook API Documentation

Complete guide to integrating ZvenBook's booking system API into your applications.

Quick Start

Get up and running with ZvenBook in under 5 minutes. This guide will walk you through creating your first booking.

1. Install the SDK

npm install @zvenbook/sdk

Or use your preferred package manager: yarn add @zvenbook/sdk or pnpm add @zvenbook/sdk

2. Initialize the SDK

import { createSdk } from '@zvenbook/sdk';

const sdk = createSdk({ 
  baseUrl: 'https://your-zvenbook-instance.vercel.app'
  // No API key required for public endpoints
});

// Health check
const health = await sdk.health();
console.log(health); // { ok: true }

Replace the baseUrl with your deployed ZvenBook instance URL.

3. Complete Integration Example

async function bookAppointment() {
  try {
    // 1. List available tenants or use tenantId from environment variables
    const { tenants } = await sdk.listTenants();
    const tenantId = tenants[0]._id || process.env.NEXT_PUBLIC_TENANT_ID;
    
    // 2. Get services and providers
    const [{ services }, { providers }] = await Promise.all([
      sdk.listServices({ tenantId }),
      sdk.listProviders({ tenantId })
    ]);
    
    // 3. Check availability
    const { slots } = await sdk.availability({
      tenantId,
      serviceId: services[0]._id,
      providerId: providers[0]._id,
      start: '2024-01-15T08:00:00Z',
      end: '2024-01-15T18:00:00Z',
      timezone: 'Europe/Stockholm'
    });
    
    // 4. Create booking
    const booking = await sdk.createBooking({
      tenantId,
      serviceId: services[0]._id,
      providerId: providers[0]._id,
      start: slots[0].start,
      end: slots[0].end,
      contactEmail: 'customer@example.com',
      contactFirstName: 'John',
      contactLastName: 'Doe'
    });
    
    console.log('Booking created:', booking);
  } catch (error) {
    console.error('Booking failed:', error);
  }
}

Core Concepts

Tenants

Tenants represent separate organizations or business units. Each tenant has isolated data including services, providers, and bookings.

Best Practice: Use meaningful tenant IDs like your company name or domain (e.g., 'acme-corp', 'salon-downtown').

Services

Services define what can be booked - haircuts, consultations, equipment rentals, etc. Each service has duration, buffer times, and capacity settings.

await sdk.createService({
  tenantId: 'salon',
  name: 'Haircut & Style',
  durationMin: 60,        // Service takes 60 minutes
  bufferBeforeMin: 10,    // 10 min prep time
  bufferAfterMin: 15,     // 15 min cleanup time
  capacity: 1             // One customer at a time
});

Providers

Providers are the people or resources that deliver services - stylists, doctors, conference rooms, etc. They have working schedules and can be assigned to multiple services.

await sdk.createProvider({
  tenantId: 'salon',
  name: 'Alex Johnson',
  timezone: 'Europe/Stockholm',
  email: 'alex@salon.com'
});

// Set working hours
await sdk.createWeeklyRule({
  tenantId: 'salon',
  providerId: 'alex',
  timezone: 'Europe/Stockholm',
  days: [
    { weekday: 1, start: '09:00', end: '17:00' }, // Monday
    { weekday: 2, start: '09:00', end: '17:00' }, // Tuesday
    // ... more days
  ]
});

API Endpoints

GET/availability

Get available time slots for a service and provider.

Parameters: tenantId, serviceId, providerId, start, end, timezone
POST/bookings

Create a new booking with automatic email confirmation.

Body: tenantId, serviceId, providerId, start, end, contactEmail, contactFirstName, contactLastName
GET/cancel/:token

Cancel a booking using its cancellation token (no auth required).

Public endpoint: Safe to use in customer-facing applications
POST/reschedule/:token

Reschedule a booking to a new time slot.

Body: start, end

📚 Complete API Reference

For a complete list of all endpoints with detailed parameters and examples, check out our comprehensive SDK documentation and the README file.

Integration Examples

Real-world examples of integrating ZvenBook into different platforms and frameworks.

React/Next.js Booking Widget

import { createSdk } from '@zvenbook/sdk';
import { useState, useEffect } from 'react';

const sdk = createSdk({ baseUrl: process.env.NEXT_PUBLIC_ZVENBOOK_URL });

export default function BookingWidget({ tenantId, serviceId }) {
  const [slots, setSlots] = useState([]);
  const [providers, setProviders] = useState([]);
  
  useEffect(() => {
    async function loadData() {
      const { providers } = await sdk.listProviders({ tenantId });
      setProviders(providers);
      
      if (providers.length > 0) {
        const { slots } = await sdk.availability({
          tenantId, serviceId, providerId: providers[0]._id,
          start: new Date().toISOString(),
          end: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
          timezone: 'Europe/Stockholm'
        });
        setSlots(slots);
      }
    }
    loadData();
  }, [tenantId, serviceId]);

  const bookSlot = async (slot) => {
    await sdk.createBooking({
      tenantId, serviceId, providerId: providers[0]._id,
      start: slot.start, end: slot.end,
      contactEmail: 'customer@example.com',
      contactFirstName: 'John', contactLastName: 'Doe'
    });
  };

  return (
    <div className="space-y-2">
      {slots.map(slot => (
        <button key={slot.start} onClick={() => bookSlot(slot)}
                className="p-2 bg-blue-500 text-white rounded">
          {new Date(slot.start).toLocaleString()}
        </button>
      ))}
    </div>
  );
}

Express.js Backend Integration

const { createSdk } = require('@zvenbook/sdk');
const express = require('express');

const sdk = createSdk({ baseUrl: process.env.ZVENBOOK_URL });
const app = express();
app.use(express.json());

app.post('/book-appointment', async (req, res) => {
  try {
    const booking = await sdk.createBooking({
      tenantId: req.body.tenantId,
      serviceId: req.body.serviceId,
      autoSelectProvider: true, // Auto-select optimal provider
      start: req.body.start,
      end: req.body.end,
      contactEmail: req.body.email,
      contactFirstName: req.body.firstName,
      contactLastName: req.body.lastName
    });
    
    res.json({ success: true, booking });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

Advanced Features

// Virtual meetings with auto-generated Jitsi rooms
const booking = await sdk.createBooking({
  // ... other fields
  virtualMeetingPlatform: 'jitsi', // Auto-generates meeting room
  notes: 'First time customer - please call if needed'
});

// Multi-location support
const booking = await sdk.createBooking({
  // ... other fields
  locationName: 'Downtown Office',
  locationAddress: '123 Main St, Stockholm',
  entryPinCode: '1234',
  entryInstructions: 'Use side entrance after hours'
});

// Provider workload analytics
const { providers } = await sdk.getProviderWorkload({
  tenantId: 'salon-123',
  start: '2024-01-01T00:00:00Z',
  end: '2024-01-31T23:59:59Z'
});

providers.forEach(provider => {
  console.log(`${provider.name}: ${provider.booking_count} bookings`);
});

Email System

ZvenBook includes a comprehensive email system with automatic confirmations, reminders, and cancellation notifications.

✅ Booking Confirmations

Sent automatically when bookings are created with ICS calendar attachments.

⏰ 24h Reminders

Automatic reminders sent 24 hours before appointments.

❌ Cancellation Emails

Sent when bookings are cancelled with calendar removal.

🔄 Reschedule Confirmations

Updated calendar invitations when bookings are rescheduled.

Setup Required

Email functionality requires environment variables to be configured:

RESEND_API_KEY=your-resend-api-key
FROM_EMAIL=bookings@yourdomain.com
BASE_URL=https://yourdomain.com

Error Handling

The API uses standard HTTP status codes and returns detailed error messages in JSON format.

400Bad Request

Invalid request parameters or missing required fields.

409Conflict

Booking conflicts with existing appointments or business rules.

404Not Found

Resource not found (service, provider, booking, etc.).

// Example error response
{
  "error": "Time overlaps with existing booking",
  "code": "BOOKING_CONFLICT",
  "details": {
    "conflictingBooking": "booking-123",
    "suggestedSlots": [
      { "start": "2024-01-15T11:00:00Z", "end": "2024-01-15T12:00:00Z" }
    ]
  }
}

Support & Resources

Need Help?

Get in touch with our support team for technical assistance.

support@zvenbook.se

Community

Join our community for discussions and updates.

GitHub Repository