curl --request POST \
--url https://api.1club.ai/v1/platform/classes \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"clubId": 123,
"name": "<string>",
"startTime": "2023-11-07T05:31:56Z",
"endTime": "2023-11-07T05:31:56Z",
"areaId": 123,
"classTypeId": 123,
"instructorIds": [
123
],
"description": "<string>",
"priceBeforeTax": 1,
"isFree": true,
"maxCapacity": 2,
"sport": "<string>",
"color": "<string>"
}
'import requests
url = "https://api.1club.ai/v1/platform/classes"
payload = {
"clubId": 123,
"name": "<string>",
"startTime": "2023-11-07T05:31:56Z",
"endTime": "2023-11-07T05:31:56Z",
"areaId": 123,
"classTypeId": 123,
"instructorIds": [123],
"description": "<string>",
"priceBeforeTax": 1,
"isFree": True,
"maxCapacity": 2,
"sport": "<string>",
"color": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
clubId: 123,
name: '<string>',
startTime: '2023-11-07T05:31:56Z',
endTime: '2023-11-07T05:31:56Z',
areaId: 123,
classTypeId: 123,
instructorIds: [123],
description: '<string>',
priceBeforeTax: 1,
isFree: true,
maxCapacity: 2,
sport: '<string>',
color: '<string>'
})
};
fetch('https://api.1club.ai/v1/platform/classes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.1club.ai/v1/platform/classes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'clubId' => 123,
'name' => '<string>',
'startTime' => '2023-11-07T05:31:56Z',
'endTime' => '2023-11-07T05:31:56Z',
'areaId' => 123,
'classTypeId' => 123,
'instructorIds' => [
123
],
'description' => '<string>',
'priceBeforeTax' => 1,
'isFree' => true,
'maxCapacity' => 2,
'sport' => '<string>',
'color' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.1club.ai/v1/platform/classes"
payload := strings.NewReader("{\n \"clubId\": 123,\n \"name\": \"<string>\",\n \"startTime\": \"2023-11-07T05:31:56Z\",\n \"endTime\": \"2023-11-07T05:31:56Z\",\n \"areaId\": 123,\n \"classTypeId\": 123,\n \"instructorIds\": [\n 123\n ],\n \"description\": \"<string>\",\n \"priceBeforeTax\": 1,\n \"isFree\": true,\n \"maxCapacity\": 2,\n \"sport\": \"<string>\",\n \"color\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.1club.ai/v1/platform/classes")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"clubId\": 123,\n \"name\": \"<string>\",\n \"startTime\": \"2023-11-07T05:31:56Z\",\n \"endTime\": \"2023-11-07T05:31:56Z\",\n \"areaId\": 123,\n \"classTypeId\": 123,\n \"instructorIds\": [\n 123\n ],\n \"description\": \"<string>\",\n \"priceBeforeTax\": 1,\n \"isFree\": true,\n \"maxCapacity\": 2,\n \"sport\": \"<string>\",\n \"color\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.1club.ai/v1/platform/classes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"clubId\": 123,\n \"name\": \"<string>\",\n \"startTime\": \"2023-11-07T05:31:56Z\",\n \"endTime\": \"2023-11-07T05:31:56Z\",\n \"areaId\": 123,\n \"classTypeId\": 123,\n \"instructorIds\": [\n 123\n ],\n \"description\": \"<string>\",\n \"priceBeforeTax\": 1,\n \"isFree\": true,\n \"maxCapacity\": 2,\n \"sport\": \"<string>\",\n \"color\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"slug": "<string>",
"description": "<string>",
"images": [
"<string>"
],
"classTypeId": 123,
"classType": {
"id": 123,
"name": "<string>",
"slug": "<string>",
"maxPartySize": 123
},
"clubId": 123,
"club": {
"id": 123,
"name": "<string>",
"slug": "<string>"
},
"areaId": 123,
"area": {
"id": 123,
"name": "<string>"
},
"startTime": "2023-11-07T05:31:56Z",
"endTime": "2023-11-07T05:31:56Z",
"durationMinutes": 123,
"isMultiDay": true,
"status": "active",
"visibility": "Public",
"sport": "<string>",
"color": "<string>",
"price": 123,
"priceBeforeTax": 123,
"isFree": true,
"maxCapacity": 123,
"maxPartySize": 123,
"bookedCount": 123,
"currentBookings": 123,
"spotsLeft": 123,
"waitlistCount": 123,
"allowWaitlist": true,
"timeStatus": "past",
"availabilityStatus": "available",
"recurrenceId": 123,
"occurrenceIndex": 123,
"recurrence": {
"id": 123,
"frequency": "daily",
"interval": 123,
"daysOfWeek": [
"<string>"
],
"endsType": "until",
"endsDate": "2023-11-07T05:31:56Z",
"endsTotalOccurrences": 123,
"status": "active"
},
"instructors": [
{
"id": 123,
"contactId": 123,
"sports": [
"<string>"
],
"typeId": 123,
"type": {
"id": 123,
"name": "<string>"
},
"experience": "<string>",
"hourlyRate": 123,
"bio": {},
"certifications": [
"<string>"
],
"isActive": true,
"isBookable": true,
"contact": {
"name": "<string>",
"profileImage": "<string>"
}
}
],
"externalSource": "<string>",
"externalId": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"translations": {},
"series": {
"recurrenceId": 123,
"occurrencesCreated": 123,
"occurrencesRemoved": 123
}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}Create a class
Creates a class. Times must be ISO 8601 instants with an explicit offset. Supports the Idempotency-Key header.
Without recurrence this creates one occurrence. With it, one request creates the whole repeating series - do not call this once per week. startTime/endTime stay required either way: they are the template slot, giving every occurrence its time of day and its duration, and the series starts from startTime (so there is no separate start date). A weekly rule whose daysOfWeek do not include that day puts the first occurrence on the first listed day at or after it.
A bounded series (ends.type of until or after) is materialized in full inside this request, so it is capped at 520 occurrences - over that the request is refused with RECURRENCE_TOO_MANY_OCCURRENCES rather than silently truncated. An open-ended series (ends omitted, or ends.type: "never") materializes a rolling window and extends itself, so only the first weeks exist immediately.
ends.occurrences counts occurrences, not weeks, across every day in daysOfWeek - twelve weeks of a Monday-and-Wednesday class is 24. To end on a date instead, use ends.type: "until" with an endDate and skip the arithmetic.
The area is checked for a clash on the first occurrence only - the same rule the admin dashboard applies. Later occurrences are written without re-checking the court, so a series can land on a slot that is already held in a later week; read GET /v1/platform/areas//availability for the window first if that matters.
The response is the first occurrence, and for a recurring create it also carries series.occurrencesCreated - what this request actually wrote. Compare it with what you asked for: an occurrence at a club that is closed, or past its closing date, is skipped rather than failing the create. Read the rest of the series back with GET /v1/platform/classes filtered on the recurrenceId it carries.
curl --request POST \
--url https://api.1club.ai/v1/platform/classes \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"clubId": 123,
"name": "<string>",
"startTime": "2023-11-07T05:31:56Z",
"endTime": "2023-11-07T05:31:56Z",
"areaId": 123,
"classTypeId": 123,
"instructorIds": [
123
],
"description": "<string>",
"priceBeforeTax": 1,
"isFree": true,
"maxCapacity": 2,
"sport": "<string>",
"color": "<string>"
}
'import requests
url = "https://api.1club.ai/v1/platform/classes"
payload = {
"clubId": 123,
"name": "<string>",
"startTime": "2023-11-07T05:31:56Z",
"endTime": "2023-11-07T05:31:56Z",
"areaId": 123,
"classTypeId": 123,
"instructorIds": [123],
"description": "<string>",
"priceBeforeTax": 1,
"isFree": True,
"maxCapacity": 2,
"sport": "<string>",
"color": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
clubId: 123,
name: '<string>',
startTime: '2023-11-07T05:31:56Z',
endTime: '2023-11-07T05:31:56Z',
areaId: 123,
classTypeId: 123,
instructorIds: [123],
description: '<string>',
priceBeforeTax: 1,
isFree: true,
maxCapacity: 2,
sport: '<string>',
color: '<string>'
})
};
fetch('https://api.1club.ai/v1/platform/classes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.1club.ai/v1/platform/classes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'clubId' => 123,
'name' => '<string>',
'startTime' => '2023-11-07T05:31:56Z',
'endTime' => '2023-11-07T05:31:56Z',
'areaId' => 123,
'classTypeId' => 123,
'instructorIds' => [
123
],
'description' => '<string>',
'priceBeforeTax' => 1,
'isFree' => true,
'maxCapacity' => 2,
'sport' => '<string>',
'color' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.1club.ai/v1/platform/classes"
payload := strings.NewReader("{\n \"clubId\": 123,\n \"name\": \"<string>\",\n \"startTime\": \"2023-11-07T05:31:56Z\",\n \"endTime\": \"2023-11-07T05:31:56Z\",\n \"areaId\": 123,\n \"classTypeId\": 123,\n \"instructorIds\": [\n 123\n ],\n \"description\": \"<string>\",\n \"priceBeforeTax\": 1,\n \"isFree\": true,\n \"maxCapacity\": 2,\n \"sport\": \"<string>\",\n \"color\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.1club.ai/v1/platform/classes")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"clubId\": 123,\n \"name\": \"<string>\",\n \"startTime\": \"2023-11-07T05:31:56Z\",\n \"endTime\": \"2023-11-07T05:31:56Z\",\n \"areaId\": 123,\n \"classTypeId\": 123,\n \"instructorIds\": [\n 123\n ],\n \"description\": \"<string>\",\n \"priceBeforeTax\": 1,\n \"isFree\": true,\n \"maxCapacity\": 2,\n \"sport\": \"<string>\",\n \"color\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.1club.ai/v1/platform/classes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"clubId\": 123,\n \"name\": \"<string>\",\n \"startTime\": \"2023-11-07T05:31:56Z\",\n \"endTime\": \"2023-11-07T05:31:56Z\",\n \"areaId\": 123,\n \"classTypeId\": 123,\n \"instructorIds\": [\n 123\n ],\n \"description\": \"<string>\",\n \"priceBeforeTax\": 1,\n \"isFree\": true,\n \"maxCapacity\": 2,\n \"sport\": \"<string>\",\n \"color\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"slug": "<string>",
"description": "<string>",
"images": [
"<string>"
],
"classTypeId": 123,
"classType": {
"id": 123,
"name": "<string>",
"slug": "<string>",
"maxPartySize": 123
},
"clubId": 123,
"club": {
"id": 123,
"name": "<string>",
"slug": "<string>"
},
"areaId": 123,
"area": {
"id": 123,
"name": "<string>"
},
"startTime": "2023-11-07T05:31:56Z",
"endTime": "2023-11-07T05:31:56Z",
"durationMinutes": 123,
"isMultiDay": true,
"status": "active",
"visibility": "Public",
"sport": "<string>",
"color": "<string>",
"price": 123,
"priceBeforeTax": 123,
"isFree": true,
"maxCapacity": 123,
"maxPartySize": 123,
"bookedCount": 123,
"currentBookings": 123,
"spotsLeft": 123,
"waitlistCount": 123,
"allowWaitlist": true,
"timeStatus": "past",
"availabilityStatus": "available",
"recurrenceId": 123,
"occurrenceIndex": 123,
"recurrence": {
"id": 123,
"frequency": "daily",
"interval": 123,
"daysOfWeek": [
"<string>"
],
"endsType": "until",
"endsDate": "2023-11-07T05:31:56Z",
"endsTotalOccurrences": 123,
"status": "active"
},
"instructors": [
{
"id": 123,
"contactId": 123,
"sports": [
"<string>"
],
"typeId": 123,
"type": {
"id": 123,
"name": "<string>"
},
"experience": "<string>",
"hourlyRate": 123,
"bio": {},
"certifications": [
"<string>"
],
"isActive": true,
"isBookable": true,
"contact": {
"name": "<string>",
"profileImage": "<string>"
}
}
],
"externalSource": "<string>",
"externalId": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"translations": {},
"series": {
"recurrenceId": 123,
"occurrencesCreated": 123,
"occurrencesRemoved": 123
}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}Authorizations
Organization-scoped bearer credential: a customer API key (1club_sk_live_...) or an MCP OAuth access token.
Headers
Retry-safe key; a repeat of the same request replays the first response
255Body
100ISO 8601 instant with an explicit offset.
ISO 8601 instant with an explicit offset.
205000The price to store, excluding tax - the same field reads return. The tax-inclusive price a class reads back with is computed and cannot be written: sending it is a 400, because posting it back would re-apply tax on every round trip.
x >= 0x >= 1Private, Member_only, Public 60^#[0-9a-fA-F]{6}$Repeat rule. Present, this one request creates the whole series rather than a single occurrence - startTime/endTime become the template slot, and the series runs from startTime. A bounded series (ends.type until or after) is written in full here and capped at 520 occurrences; an open-ended one materializes a rolling window and extends itself. Unknown fields are rejected.
Show child attributes
Show child attributes
Response
Class created. For a recurring create, the first occurrence of the new series.
One class occurrence. A weekly class materializes a row per session, linked by recurrenceId and occurrenceIndex, so id is what a booking's classId points at - and what distinguishes two groups running in the same hour. Read the roster with GET /v1/platform/classes/{id}/bookings.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Room or court the class occupies, when one is assigned.
Show child attributes
Show child attributes
A cancelled occurrence keeps its bookings and roster.
active, cancelled Public, Member_only, Private Price including the organization's default tax - what a member is charged.
The price as stored on the class, before tax.
Seats; null when uncapped.
Largest party one booking may bring, from the class type.
Seats taken: attendee bookings plus standalone walk-in check-ins.
Legacy alias of bookedCount.
maxCapacity - bookedCount, floored at 0; null when capacity is unlimited.
Entries waiting (pending or notified).
past, upcoming available, full The series this occurrence belongs to; null for a one-off class.
Position within the series, as materialized.
The series rule, when this occurrence belongs to one.
Show child attributes
Show child attributes
Who teaches the occurrence. The instructor's own weekly availability is not included; read it from GET /v1/platform/instructors.
Show child attributes
Show child attributes
Import provenance - set when the occurrence came from an external system rather than being created in 1club.
The source system's id for this occurrence, for reconciliation against it.
Every locale's copy, keyed by language code (e.g. 'es', 'fr'). Returned alongside the locale-resolved name/description, so an integration syncing a multilingual timetable can read all of them in one call.
Present only on the response to a recurring create or to an update that moved where the series ends, never on a read: it describes what that one request did. An occurrence at a club that is closed, or past its closing date, is skipped rather than failing the request, so comparing occurrencesCreated with what you asked for is the only way a partially-skipped season is visible.
Show child attributes
Show child attributes
Was this page helpful?