For the complete documentation index, see llms.txt.

Recurring Bookings

A recurring booking creates a series of bookings at a fixed frequency. Each occurrence is a separate booking record tracked under a booking_series parent object.

Endpoints

Method Path Purpose
POST /v1/bookings Create a booking — include a recurrence object to create a series
GET /v1/booking-series List all series
GET /v1/booking-series/:id Get series + all occurrences
GET /v1/bookings?series_id= Filter individual bookings by series

Create a recurring booking

Include a recurrence object in the standard booking creation request:

curl -X POST https://api.vennio.app/v1/bookings \
  -H "Authorization: Bearer $VENNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "your-business-id",
    "customer_email": "customer@example.com",
    "customer_name": "Jane Doe",
    "start_time": "2026-04-07T10:00:00Z",
    "end_time": "2026-04-07T10:30:00Z",
    "recurrence": {
      "frequency": "weekly",
      "count": 8,
      "timezone": "America/New_York"
    }
  }'

Recurrence object fields

Field Type Description
frequency weekly | biweekly | monthly How often the booking repeats
count integer Number of occurrences to create
until_date ISO date Create occurrences up to (and including) this date
timezone IANA timezone Wall-clock timezone for recurrence expansion

Provide count or until_date — not both.

Series response

The response includes the booking_series object alongside the created bookings:

{
  "booking_series": {
    "id": "series_abc123",
    "frequency": "weekly",
    "created_count": 8,
    "skipped_count": 0
  },
  "bookings": [...]
}

Cancellation

Cancelling a series cancels all future confirmed occurrences and fires a booking_series.cancelled webhook. Past occurrences are unaffected.

DST handling

The timezone in the recurrence object is used to expand occurrences. Wall-clock time stays consistent — a 10:00 AM weekly meeting stays at 10:00 AM even when clocks change.

Monthly bookings on day 31

If the start date falls on a day that doesn't exist in a target month (e.g. 31 January → February), the occurrence is clamped to the last valid day of that month (28 or 29 February).

Gotchas