• 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
Buydocschangelogsdemocommunity

API

PreviousGetting StartedNextDiscovery
On this page
AuthenticationThe organization is never in the requestThe key acts as the person who created itWho can do whatResponse envelopeUnits and conventionsEvery endpointRate limitWhere to start

Everything the app does to a villa, a booking or a customer is reachable over HTTP. A script — or an agent — works on the same data as the interface, under the same rules, without a browser session.

This page is the map: authentication, the shape of every answer, and every endpoint with the page that details it. Each endpoint has its own page: one call, its parameters, its answers and what it refuses.

Authentication

Every /api/v1 request carries an API key in the x-api-key header. A key is created from Account → API Keys and belongs to one organization: that organization is the entire scope of the key.

bash
curl -H "x-api-key: $VILLASLOT_API_KEY" \
  "https://villaslot.app/api/v1/me"

The organization is never in the request

Not in the body, not in the URL, not in a header. It comes from the key and from nothing else. A resource belonging to another organization answers 404, exactly like an id that does not exist.

The key acts as the person who created it

A key is not a second identity with its own powers. It carries the rights of the member who created it, in the organization it was created for. Nothing is possible through the API that the same person could not do in the interface — and nothing is refused that they could.

A key stops working when its bearer leaves the organization. No orphan access survives a departure: the key answers 401 from that moment, exactly as if it had been revoked. Revoking the key of someone who left is therefore housekeeping, not a security fix.

Two questions decide every call, and both have to pass:

QuestionDecided byAnswer when it fails
Is the bearer still a member, and with which role?The key's bearer and their role401 / 403
Does the resource belong to the key's organization?The key's organization404

Who can do what

OperationMinimum role in the organization
Reading anythingmember
Creating and correcting customers, creating and managing bookingsmember
Creating, renaming, archiving a villa, its photo, its hours, its ratesadmin
Changing the booking settings of the organizationadmin

That split is not an API decision: it is the product's own permission matrix. A member is often the one with the customer on the phone, and the product reserves villa configuration to the owner and the organization admins.

Response envelope

CaseBody
Success{"success": true, "data": …}
Paginated list{"success": true, "data": [...], "pagination": {"total", "page", "limit", "totalPages"}}
Error{"error": "…"}
Validation error{"error": "…", "details": [ … field-level issues … ]}
Business refusal{"error": "…", "detail": { … reason, names, amounts … }}
StatusWhen
200Read or change done
201Resource created
401Missing or invalid key, or a key whose bearer left the organization
403The bearer is a legitimate member, but their role is too low
404Unknown resource, or a resource of another organization
409State conflict — a slot taken, a status that moved, a plan limit reached
413A batch over its bound; the message names the limit
422Invalid parameters or body — details lists the offending fields
429Rate limit of the key exceeded (120 requests per minute)
500Unexpected server error

A resource of another organization answers 404, never 403. A 403 would confirm that the id exists. The same response for an unknown id and for an id you are not allowed to read — you cannot tell them apart, and that is the point. The 403 is reserved for the other question: you are in the right organization, your role is too low.

Units and conventions

  • Amounts are integers in the minor unit of the organization currency. 1500000 in IDR is 15 000 rupiah; 4500 in EUR is 45,00 €. Never divide by 100 without knowing the currency.
  • Instants are ISO 8601 in UTC (startAt, endAt, createdAt).
  • Days and times are local to the organization: a booking is created with date (YYYY-MM-DD) and startMinute (minutes from local midnight). Read settings.timezone from GET /api/v1/me.
  • Opening hours are weekly local minutes, dayOfWeek following 0 Sunday … 6 Saturday.

Every endpoint

MethodPathWhat it doesRole
GET/api/v1/meMy contextmember
GET/api/v1/propertiesList villasmember
POST/api/v1/propertiesCreate a villaadmin
GET/api/v1/properties/{propertyId}Get a villamember
PATCH/api/v1/properties/{propertyId}Rename a villaadmin
POST/api/v1/properties/{propertyId}/archiveArchive a villaadmin
POST/api/v1/properties/{propertyId}/photoUpload a photoadmin
GET/api/v1/properties/{propertyId}/opening-hoursGet opening hoursmember
PUT/api/v1/properties/{propertyId}/opening-hoursReplace opening hoursadmin
GET/api/v1/properties/{propertyId}/ratesGet the rate gridmember
PUT/api/v1/properties/{propertyId}/ratesReplace the rate gridadmin
GET/api/v1/properties/{propertyId}/availabilityGet availabilitymember
GET/api/v1/bookingsList bookingsmember
POST/api/v1/bookingsCreate a bookingmember
GET/api/v1/bookings/{bookingId}Get a bookingmember
POST/api/v1/bookings/{bookingId}/confirmConfirm a bookingmember
POST/api/v1/bookings/{bookingId}/completeComplete a bookingmember
POST/api/v1/bookings/{bookingId}/cancelCancel a bookingmember
POST/api/v1/bookings/{bookingId}/rescheduleReschedule a bookingmember
GET/api/v1/customersList customersmember
POST/api/v1/customersCreate a customermember
POST/api/v1/customers/bulkImport customersmember
GET/api/v1/customers/{customerId}Get a customermember
PATCH/api/v1/customers/{customerId}Update a customermember
GET/api/v1/settings/bookingGet booking settingsmember
PATCH/api/v1/settings/bookingUpdate booking settingsadmin

Rate limit

The limit is carried by the key, not by the IP: 120 requests per minute. A key over its limit answers 429 on every endpoint. Back off and retry — nothing was done.

Where to start

Call GET /api/v1/me first: it turns a bare key into a context — the organization it opens, the role it carries, the timezone and currency every other call depends on, and how much room the plan leaves.

Then the usual path: find or create the customer, ask the availability of a villa, and create the booking. The whole sequence, with its refusals, is walked in Create a booking.