curl --request POST \
--url https://api.1club.ai/v1/platform/instructors \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"contactId": 123,
"clubId": 123,
"instructorTypeId": 123,
"sports": [
"<string>"
],
"displayName": "<string>",
"hourlyRate": 1,
"priceIncludesArea": true,
"maxConcurrentBookings": 2,
"operatingHours": {},
"isActive": true
}
'import requests
url = "https://api.1club.ai/v1/platform/instructors"
payload = {
"contactId": 123,
"clubId": 123,
"instructorTypeId": 123,
"sports": ["<string>"],
"displayName": "<string>",
"hourlyRate": 1,
"priceIncludesArea": True,
"maxConcurrentBookings": 2,
"operatingHours": {},
"isActive": True
}
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({
contactId: 123,
clubId: 123,
instructorTypeId: 123,
sports: ['<string>'],
displayName: '<string>',
hourlyRate: 1,
priceIncludesArea: true,
maxConcurrentBookings: 2,
operatingHours: {},
isActive: true
})
};
fetch('https://api.1club.ai/v1/platform/instructors', 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/instructors",
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([
'contactId' => 123,
'clubId' => 123,
'instructorTypeId' => 123,
'sports' => [
'<string>'
],
'displayName' => '<string>',
'hourlyRate' => 1,
'priceIncludesArea' => true,
'maxConcurrentBookings' => 2,
'operatingHours' => [
],
'isActive' => true
]),
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/instructors"
payload := strings.NewReader("{\n \"contactId\": 123,\n \"clubId\": 123,\n \"instructorTypeId\": 123,\n \"sports\": [\n \"<string>\"\n ],\n \"displayName\": \"<string>\",\n \"hourlyRate\": 1,\n \"priceIncludesArea\": true,\n \"maxConcurrentBookings\": 2,\n \"operatingHours\": {},\n \"isActive\": true\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/instructors")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"contactId\": 123,\n \"clubId\": 123,\n \"instructorTypeId\": 123,\n \"sports\": [\n \"<string>\"\n ],\n \"displayName\": \"<string>\",\n \"hourlyRate\": 1,\n \"priceIncludesArea\": true,\n \"maxConcurrentBookings\": 2,\n \"operatingHours\": {},\n \"isActive\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.1club.ai/v1/platform/instructors")
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 \"contactId\": 123,\n \"clubId\": 123,\n \"instructorTypeId\": 123,\n \"sports\": [\n \"<string>\"\n ],\n \"displayName\": \"<string>\",\n \"hourlyRate\": 1,\n \"priceIncludesArea\": true,\n \"maxConcurrentBookings\": 2,\n \"operatingHours\": {},\n \"isActive\": true\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"clubId": 123,
"instructorTypeId": 123,
"sports": [
"<string>"
],
"hourlyRate": 123,
"priceIncludesArea": true,
"bookability": "Admin_only",
"isBookable": true,
"maxConcurrentBookings": 123,
"operatingHours": {},
"visibility": "Private",
"isActive": true
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Create an instructor
Makes a bookable instructor (a coach) out of an existing contact. Resolve or create that contact first with the contacts resource, then pass its id as contactId - an instructor profile hangs off a person the organization already knows, and the name on it is theirs.
This grants no access to the staff portal. The instructor gets no login, no role, no organization_users row and no invitation email; they are inventory that can be booked and paid, not a user. sendInvitation, organizationUserId, role and color are refused rather than ignored, so a payload copied from the admin API fails loudly. Invite a coach to the portal from the 1club admin portal instead.
hourlyRate is what a member pays for an hour of one-to-one time, not what the club pays the coach - see POST /v1/platform/pay-rate-policies for the latter.
One instructor per contact. A contact that already has one is a 409 naming the existing profile, including when that profile is inactive: bringing a retired coach back is a PATCH, so whoever does it can see the rates and hours they are restoring. Supports the Idempotency-Key header.
Needs the instructors:write scope, which is new. An API key holding the * wildcard picks it up automatically; an OAuth connection does not, because the wildcard is expanded at consent time - that connection has to be re-authorized before this endpoint is reachable.
curl --request POST \
--url https://api.1club.ai/v1/platform/instructors \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"contactId": 123,
"clubId": 123,
"instructorTypeId": 123,
"sports": [
"<string>"
],
"displayName": "<string>",
"hourlyRate": 1,
"priceIncludesArea": true,
"maxConcurrentBookings": 2,
"operatingHours": {},
"isActive": true
}
'import requests
url = "https://api.1club.ai/v1/platform/instructors"
payload = {
"contactId": 123,
"clubId": 123,
"instructorTypeId": 123,
"sports": ["<string>"],
"displayName": "<string>",
"hourlyRate": 1,
"priceIncludesArea": True,
"maxConcurrentBookings": 2,
"operatingHours": {},
"isActive": True
}
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({
contactId: 123,
clubId: 123,
instructorTypeId: 123,
sports: ['<string>'],
displayName: '<string>',
hourlyRate: 1,
priceIncludesArea: true,
maxConcurrentBookings: 2,
operatingHours: {},
isActive: true
})
};
fetch('https://api.1club.ai/v1/platform/instructors', 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/instructors",
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([
'contactId' => 123,
'clubId' => 123,
'instructorTypeId' => 123,
'sports' => [
'<string>'
],
'displayName' => '<string>',
'hourlyRate' => 1,
'priceIncludesArea' => true,
'maxConcurrentBookings' => 2,
'operatingHours' => [
],
'isActive' => true
]),
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/instructors"
payload := strings.NewReader("{\n \"contactId\": 123,\n \"clubId\": 123,\n \"instructorTypeId\": 123,\n \"sports\": [\n \"<string>\"\n ],\n \"displayName\": \"<string>\",\n \"hourlyRate\": 1,\n \"priceIncludesArea\": true,\n \"maxConcurrentBookings\": 2,\n \"operatingHours\": {},\n \"isActive\": true\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/instructors")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"contactId\": 123,\n \"clubId\": 123,\n \"instructorTypeId\": 123,\n \"sports\": [\n \"<string>\"\n ],\n \"displayName\": \"<string>\",\n \"hourlyRate\": 1,\n \"priceIncludesArea\": true,\n \"maxConcurrentBookings\": 2,\n \"operatingHours\": {},\n \"isActive\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.1club.ai/v1/platform/instructors")
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 \"contactId\": 123,\n \"clubId\": 123,\n \"instructorTypeId\": 123,\n \"sports\": [\n \"<string>\"\n ],\n \"displayName\": \"<string>\",\n \"hourlyRate\": 1,\n \"priceIncludesArea\": true,\n \"maxConcurrentBookings\": 2,\n \"operatingHours\": {},\n \"isActive\": true\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"clubId": 123,
"instructorTypeId": 123,
"sports": [
"<string>"
],
"hourlyRate": 123,
"priceIncludesArea": true,
"bookability": "Admin_only",
"isBookable": true,
"maxConcurrentBookings": 123,
"operatingHours": {},
"visibility": "Private",
"isActive": true
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}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
Fields accepted when creating an instructor. Only contactId is required. Unknown fields are rejected, and sendInvitation, organizationUserId, role, color and isBookable are refused by name - this API creates a bookable coach and never a staff-portal user.
The person, who must already be a contact in this organization. Not editable afterwards.
Club the instructor works at; null means every club in the organization.
The kind of coach, from GET /v1/platform/instructor-types. null leaves them unfiled, which is legal but loses the grouping every roster view reads from and the default revenue account their sessions would post to.
50Public-facing name. Defaults to " ." from the contact.
120What a member pays for an hour of one-to-one time with them. Not what the club pays the coach - that is a pay rate policy. Added to the area price unless priceIncludesArea is true.
x >= 0Who may book one-to-one sessions with them. Never outruns visibility: narrowing one pulls the other down with it, and deactivating the instructor retires this to Admin_only. The read-only isBookable is derived from this and cannot be written.
Admin_only, Member_only, Public When true, hourlyRate already covers the booked area, which is then not charged on top.
x >= 1Per-day-of-week availability schedule
Private, Member_only, Public False retires the instructor: they leave the listings and stop being bookable, and their history is kept. There is no delete.
Response
Instructor created
An instructor (coach) in the organization, as returned by the instructors resource. Reference the id as instructorId when creating a booking. Leaner than the PlatformInstructor profile embedded in class payloads: this carries only what is needed to pick and price a coach.
Public display name when set, otherwise the linked contact name.
Club the instructor belongs to; null when org-wide.
The kind of coach, from GET /v1/platform/instructor-types; null for one nobody has filed.
Added to the area price when pricing a booking with this instructor, unless priceIncludesArea is true.
When true, hourlyRate is an all-in price that already covers the booked area - the area's price is not added on top of it.
Who may book one-to-one sessions with this instructor. Admin_only means the club reserves those bookings to its own staff, so a customer-initiated booking is rejected; an API key acts for the organization and may still create one.
Admin_only, Member_only, Public Derived from bookability: true unless it is Admin_only. Prefer bookability, which distinguishes members from the public.
Per-day-of-week availability schedule
Who may see the instructor on member-facing surfaces. bookability never outruns it.
Private, Member_only, Public False for a retired instructor: hidden from the list by default, not bookable, history kept.
Was this page helpful?