For the complete documentation index, see llms.txt.

How do I prevent double-bookings in my app?

Vennio prevents double-bookings at the API level. The availability endpoint only returns genuinely open slots, and the booking endpoint performs a conflict check at creation time — returning a 409 Conflict if the slot is no longer available. Idempotency keys prevent duplicate bookings from retried requests.

The answer

Vennio handles double-booking prevention in three layers:

  1. Availability filteringGET /v1/availability/slots only returns slots not blocked by existing calendar events or confirmed bookings
  2. Conflict detection at booking timePOST /v1/bookings checks for conflicts atomically and returns 409 Conflict if the slot was taken between the availability check and the booking request
  3. Idempotency keys — prevent duplicate bookings from network retries or double-clicks

Availability check before booking

Always query availability before presenting slots to users. The availability API respects your business hours, existing calendar events, and confirmed bookings.

import { createVennioClient } from '@vennio/sdk'

const client = createVennioClient({ apiKey: process.env.VENNIO_API_KEY })

const res = await client.availability.getSlots({
  query: {
    business_id: 'YOUR_BUSINESS_ID',
    duration_minutes: 60,
    timezone: 'UTC',
  },
})

// Only genuinely open slots are returned
const openSlots = res.data.slots
console.log(`${openSlots.length} slots available`)
Calendar sync

Vennio syncs with Google Calendar (and Microsoft, coming soon). Existing events on your calendar are automatically excluded from available slots — no manual blocking required.

Conflict response (409)

Even if you show a user an available slot, someone else might book it before they confirm. Vennio performs an atomic conflict check at booking creation time. Handle the 409 and refresh availability.

async function bookSlot(businessId, slot, customer) {
  try {
    const res = await client.bookings.create({
      body: {
        business_id: businessId,
        customer_email: customer.email,
        customer_name: customer.name,
        start_time: slot.start,
        end_time: slot.end,
      },
    })
    return { success: true, booking: res.data.booking }
  } catch (err) {
    if (err.status === 409) {
      // Slot was taken — fetch fresh availability and let user pick again
      return { success: false, reason: 'slot_taken' }
    }
    throw err
  }
}
{
  "error": "conflict",
  "message": "The requested time slot is no longer available.",
  "code": 409
}

Idempotency keys

Pass an Idempotency-Key header on booking creation requests. If the same key is used twice, Vennio returns the original response rather than creating a second booking. Use a UUID generated per booking attempt.

import { randomUUID } from 'crypto'

const idempotencyKey = randomUUID() // generate once per attempt

const res = await client.bookings.create({
  headers: {
    'Idempotency-Key': idempotencyKey,
  },
  body: {
    business_id: 'YOUR_BUSINESS_ID',
    customer_email: 'customer@example.com',
    customer_name: 'Jane Doe',
    start_time: '2026-04-01T09:00:00Z',
    end_time: '2026-04-01T10:00:00Z',
  },
})
// Safe to retry with the same idempotencyKey — won't create a duplicate
curl -X POST https://api.vennio.app/v1/bookings \
  -H "Authorization: Bearer vennio_sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "business_id": "YOUR_BUSINESS_ID",
    "customer_email": "customer@example.com",
    "customer_name": "Jane Doe",
    "start_time": "2026-04-01T09:00:00Z",
    "end_time": "2026-04-01T10:00:00Z"
  }'

Buffer time between meetings

To prevent back-to-back bookings without breathing room, pass buffer_minutes when querying availability. Slots that would leave less than the buffer time between meetings are excluded.

# Require 15 minutes between meetings
curl "https://api.vennio.app/v1/availability/slots?business_id=YOUR_BUSINESS_ID&duration_minutes=30&buffer_minutes=15&timezone=UTC" \
  -H "Authorization: Bearer vennio_sk_live_YOUR_KEY"

Next steps