• VillaSlotDocumentation
  • Introduction
  • Getting Started
  • API
    • Discovery
      • GETGet my context
    • Villas
      • GETList villas
      • POSTCreate a villa
      • GETGet a villa
      • PATCHRename a villa
      • POSTArchive a villa
      • POSTUpload a villa photo
      • GETGet opening hours
      • PUTReplace opening hours
      • GETGet the rate grid
      • PUTReplace the rate grid
      • GETGet availability
    • Bookings
      • GETList bookings over a period
      • POSTCreate a booking
      • GETGet a booking
      • POSTConfirm a booking
      • POSTComplete a booking
      • POSTCancel a booking
      • POSTReschedule a booking
    • Customers
      • GETList customers
      • POSTCreate a customer
      • POSTImport a batch of customers
      • GETGet a customer
      • PATCHUpdate a customer
    • Booking settings
      • GETGet booking settings
      • PATCHUpdate booking settings
  1. Documentation
  2. API
  3. Bookings
  4. Create a booking
Buydocschangelogsdemocommunity

Create a booking

PreviousList bookings over a periodNextGet a booking
On this page
AuthorizationRequest bodyExampleResponseWhat it refusesAgent recipeNotes

POST /api/v1/bookings

The heart of the product. No business rule is rewritten here: availability, buffer, opening hours, the rate grid, the currency, the timezone and the plan ceiling are all enforced by the same service the app calls.

The date and the start time are local to the organization, not instants: send date: "2026-09-12" and startMinute: 540 for 09:00 local. Read settings.timezone from /api/v1/me if you need to know which local that is.

Authorization

Role in the organizationThis endpoint
ownerAllowed
adminAllowed
memberAllowed

The product gives members the right to create bookings — they are often the ones with the customer on the phone. Refusing them here would make the API stricter than the screen.

Request body

FieldTypeRequiredDescription
propertyIduuidyesAn active villa of the organization
customerIduuidyesA customer of the organization
datestringyesLocal day YYYY-MM-DD
startMinuteintegeryesMinutes from local midnight, e.g. 540 for 09:00
durationMinutesintegeryesMust be priced for this villa
statusenumyesHOLD or CONFIRMED
priceAmountintegernoNegotiated price; defaults to the grid amount
depositPercentintegerno1 to 100; null means the full price is due

Example

bash
curl -X POST -H "x-api-key: $VILLASLOT_API_KEY" -H "content-type: application/json" \
  -d '{"propertyId":"1f0a…","customerId":"8d3e…","date":"2026-09-12","startMinute":540,"durationMinutes":120,"status":"HOLD"}' \
  "https://villaslot.app/api/v1/bookings"

Response

  • 201 Created — the booking. A HOLD carries holdExpiresAt; a CONFIRMED does not.

What it refuses

  • 409 Conflict — the booking was not created, and detail.reason says why:

    reasonMeaning
    OVERLAPThe slot, or its buffer, is taken
    CLOSEDThe villa is closed that day
    OUTSIDE_OPENING_HOURSThe slot falls outside the opening range
    INVALID_INTERVALEnd before start
    DURATION_NOT_PRICEDThat duration has no rate for this villa
    CURRENCY_NOT_CONFIGUREDThe organization has not chosen a currency
    PROPERTY_NOT_BOOKABLEUnknown, archived, or another organization's villa
    CUSTOMER_NOT_FOUNDUnknown, or another organization's customer
  • 409 Conflict — the plan limit for bookings is reached.

  • 422 Unprocessable Entity — a malformed field; details lists them.

  • 401 Unauthorized — missing or invalid key, or a key whose bearer left the organization.

  • 404 Not Found — unknown id, or an id belonging to another organization. The two are deliberately indistinguishable.

  • 429 Too Many Requests — over 120 requests in a minute for this key.

Agent recipe

Ask availability first, then create — a refused POST costs a round trip and tells you no more than the free slots already did:

js
const {data} = await get(
  `/api/v1/properties/${propertyId}/availability?date=2026-09-12&durationMinutes=120`
)
if (data.slots.length === 0) return 'no slot that day'

await post('/api/v1/bookings', {
  propertyId, customerId, date: '2026-09-12', startMinute: 540,
  durationMinutes: 120, status: 'HOLD',
})

Notes

  • Overriding a conflict is not available over the API. Forcing a booking onto a taken slot is a deliberate human decision, made on screen after seeing what it collides with.
  • Overnight stays are not covered by this endpoint yet.