Skip to main content
Before creating bookings, discover the areas you can sell and read their availability. In 1Club a bookable padel court is an area; store the mapping between your courts and 1Club area ids on your side.

List areas

Returns the organization’s bookable areas. Filter by club or sport to get just the padel courts. An area is listed only while it is active, Public, and of a bookable area type (or of no type). An area under maintenance or not publicly visible is absent here, but still readable by id.
Query parameters:
  • clubId (optional) - only return areas belonging to this club
  • sport (optional) - only return areas whose type lists this sport, for example padel
Response:
Use each area’s id as the areaId when creating a booking. timezone is the IANA zone the gym keeps, and the one operatingHours are expressed in. Convert a local wall time in it when you send startTime and endTime - a booking sent in the wrong zone lands an hour out twice a year, at the daylight-saving boundary, and looks correct the rest of the time. Map it once when you map the courts.

Get an area

Requires the areas:read scope. Returns one area by id, in the same shape as the list, whatever its state. Unlike the list, it resolves an area that is under maintenance, inactive, or not publicly visible, and status and visibility say which case it is.

Area availability

Returns everything you need to render a court’s calendar for a window: the club timezone, operating hours, the intervals already taken (busy), and ready-made open slots with remaining capacity.
Query parameters:
  • from (required) - window start, ISO date-time
  • to (required) - window end, ISO date-time, at most 31 days after from
  • slotMinutes (optional) - slot granularity: 15, 30, 60 (default), 90, or 120
Response:
  • timezone - the IANA timezone the operatingHours are expressed in. Operating hours are per-weekday local times; slots are already resolved to absolute UTC instants for you (DST included), so prefer slots over re-deriving from operatingHours.
  • slots - open slots computed from effective bookable hours (club ∩ area) minus busy, honouring maxConcurrentBookings. remaining is how many of capacity are still free; bookable is remaining > 0 and no hard block.
  • busy and operatingHours are still returned raw if you’d rather compute slots yourself. An area with maxConcurrentBookings greater than 1 can hold that many overlapping bookings before a slot is full.
  • busy[].subject - what holds the court for that interval: member_booking, class (a scheduled class holding its court), event, or area_block (the gym closed the court for maintenance or a private hire). Same vocabulary as details.conflicts[].subject on a refused booking, so an hour you cannot sell can be explained rather than shown as unattributed busy time. A class is a booking row underneath, so subject is the only thing that separates it from a customer’s reservation. No id or name is attached - an availability read is not a read of who booked. Use List bookings for the rows you may see.
  • version - a consistency token (the highest change sequence for this area). Compare it to the sequence on an availability webhook to know whether this read already reflects that change.

Conditional requests

The response sends an ETag. Send it back as If-None-Match to get a cheap 304 Not Modified when nothing has changed - useful as a polling fallback where you don’t run webhooks.
Treat the token as opaque. It covers the area’s version, the window you asked for, the slot size, and the shape of the response itself - so it changes when we ship a new field, not only when a court’s availability moves. Store it verbatim against the request that produced it and send it back unmodified; do not parse it or rebuild it from version, or a release that adds a field will leave you on a 304 and the old body.

Club availability grid

Render a whole club’s calendar in one request instead of one call per court.
Query parameters: clubId (optional), sport (optional), from/to (required, ≤ 31 days), slotMinutes (optional). The grid returns at most 60 areas per request. For a larger organization, filter by clubId or sport.
The top-level cursor is the reconciliation cursor at read time - store it and pass it to the change feed to catch up later.

Availability change feed

Returns every availability change for your organization after a cursor, in order. Pair it with webhooks: webhooks push changes in near real time; this feed lets you catch up exactly after any missed delivery, so your calendar never drifts.
Query parameters: since (required cursor; 0 for the start of retention), limit (optional, default 100, max 500).
To reconcile: pass your last stored cursor, apply each change by re-pulling /availability for the affected area and window, then store the new cursor. Keep paging while hasMore is true.
On first sync you have no cursor - do a full grid pull and start from the cursor it returns (starting from 0 for a live club replays its whole history). The feed serves a rolling history; if your cursor predates it after a long outage, re-baseline with a full grid pull. Some changes (e.g. area.updated) carry a coarse, wide from/to window rather than a single slot - re-pull the whole window. See the webhooks guide for the full reconciliation flow.
Availability is a snapshot. 1Club remains the source of truth: booking creation re-checks availability atomically, so a slot that was open a moment ago may be rejected if it was taken in between. Always handle a booking rejection gracefully - and use webhooks plus this feed to keep the snapshot fresh.

Create and update areas

Requires the areas:write scope. Use these to add a court, a studio, or a lane, or to change one.
  • name, clubId, and areaTypeId are required on create. clubId comes from GET /v1/platform/clubs and areaTypeId from Area types. Both must belong to your organization.
  • The other fields are description, status (active, maintenance, or inactive; defaults to active), visibility (Private, Member_only, or Public; defaults to Public), pricePerHour, operatingHours, and maxConcurrentBookings (1 to 100). Unknown fields are rejected.
  • Create returns 201 with the area. PATCH /v1/platform/areas/{areaId} takes the same fields except clubId, which cannot change. Send only what changes.
Setting status to maintenance or inactive, or narrowing visibility, takes the area out of availability. That is the durable way to close a court indefinitely, where a block closes it for a stated window. There is no delete: an area carries booking history. A new area, and any edit to its hours, status, visibility, capacity, or area type, emits an availability change, so the change feed and webhooks pick it up.

Area types

An area type is a kind of bookable space, such as “Padel court”. Every area of the type inherits its sports, default price, and default number of players.
  • GET /v1/platform/area-types - every area type, as { "data": [...] } with no paging. Types with isBookable: false are included, so do not build bookable inventory on one. Requires areas:read.
  • GET /v1/platform/area-types/{id} - one area type, with areaCount. Requires areas:read.
  • POST /v1/platform/area-types and PATCH /v1/platform/area-types/{id} - create and update one. Fields include name, sports, isBookable, defaultPricePerHour, defaultMaxPlayers, allowsSingles, maxConcurrentBookings, revenueAccountId, and bookingAudience. Requires areas:write.
Editing an area type is the widest write on this API. Changing its sports, price, players, or bookability reprices or re-sports every area of that type at once, and emits an availability change for each of them.

Area blocks

A block takes an area off the bookable schedule for a period: resurfacing, a holiday, a camp. Availability already reflects blocks, so a blocked slot simply won’t appear in slots. Read blocks directly when you want the reason and the period to show in your own calendar, or when migrating a schedule into 1Club. A block with areaId: null is gym-wide: it blocks every area in its clubId. If you key your calendar by court, expand it against that gym’s areas.

List blocks

Requires the areas:read scope.
Query parameters:
  • from (required) - window start, ISO date-time
  • to (required) - window end, at most 31 days after from
  • clubId (optional) - only blocks for this club
  • areaId (optional) - only blocks affecting this area, gym-wide ones included
Any block that overlaps the window is returned, so a week-long block still appears when you ask about one day inside it. Response:

Create a block

Requires the areas:write scope. Send areaId to close one court, and its gym is derived for you. To close a whole gym, send clubId and leave areaId out - one record rather than one per court, and areas added later are covered too.
Body:
  • startTime, endTime (required) - ISO date-times with offset. endTime must be after startTime.
  • areaId (optional) - the court to block. Omit or send null for a gym-wide block.
  • clubId - required when areaId is omitted or null; otherwise derived from the area.
  • note (optional) - up to 500 characters, shown on the gym’s schedule.
Returns 201 with the created block.
A block stops new bookings. Bookings already inside the period are not cancelled, so migrate or cancel those separately if you need the period clear.

Delete a block

Requires the areas:write scope. Deleting a block reopens the period for booking.
Returns { "success": true }. A second delete of the same id returns 404.
Creating or deleting a block emits an availability change for every affected area, so the changes feed and webhooks will tell you to re-pull those courts. A gym-wide block emits one change per area in the gym.