curl --request POST \
--url https://api.1club.ai/v1/platform/forms \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"createsAccount": true,
"name": "<string>",
"slug": "<string>",
"title": "<string>",
"subtitle": "<string>",
"planStepTitle": "<string>",
"classTypeStepTitle": "<string>",
"planIds": [
123
],
"classTypeIds": [
123
],
"productIds": [
123
],
"collectsDocuments": true,
"requiresAuthentication": true,
"combineSelectionAndDetails": true,
"videoUrl": "<string>",
"videoRequired": true,
"videoTitle": "<string>",
"fieldOverrides": {},
"translations": {}
}
'import requests
url = "https://api.1club.ai/v1/platform/forms"
payload = {
"createsAccount": True,
"name": "<string>",
"slug": "<string>",
"title": "<string>",
"subtitle": "<string>",
"planStepTitle": "<string>",
"classTypeStepTitle": "<string>",
"planIds": [123],
"classTypeIds": [123],
"productIds": [123],
"collectsDocuments": True,
"requiresAuthentication": True,
"combineSelectionAndDetails": True,
"videoUrl": "<string>",
"videoRequired": True,
"videoTitle": "<string>",
"fieldOverrides": {},
"translations": {}
}
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({
createsAccount: true,
name: '<string>',
slug: '<string>',
title: '<string>',
subtitle: '<string>',
planStepTitle: '<string>',
classTypeStepTitle: '<string>',
planIds: [123],
classTypeIds: [123],
productIds: [123],
collectsDocuments: true,
requiresAuthentication: true,
combineSelectionAndDetails: true,
videoUrl: '<string>',
videoRequired: true,
videoTitle: '<string>',
fieldOverrides: {},
translations: {}
})
};
fetch('https://api.1club.ai/v1/platform/forms', 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/forms",
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([
'createsAccount' => true,
'name' => '<string>',
'slug' => '<string>',
'title' => '<string>',
'subtitle' => '<string>',
'planStepTitle' => '<string>',
'classTypeStepTitle' => '<string>',
'planIds' => [
123
],
'classTypeIds' => [
123
],
'productIds' => [
123
],
'collectsDocuments' => true,
'requiresAuthentication' => true,
'combineSelectionAndDetails' => true,
'videoUrl' => '<string>',
'videoRequired' => true,
'videoTitle' => '<string>',
'fieldOverrides' => [
],
'translations' => [
]
]),
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/forms"
payload := strings.NewReader("{\n \"createsAccount\": true,\n \"name\": \"<string>\",\n \"slug\": \"<string>\",\n \"title\": \"<string>\",\n \"subtitle\": \"<string>\",\n \"planStepTitle\": \"<string>\",\n \"classTypeStepTitle\": \"<string>\",\n \"planIds\": [\n 123\n ],\n \"classTypeIds\": [\n 123\n ],\n \"productIds\": [\n 123\n ],\n \"collectsDocuments\": true,\n \"requiresAuthentication\": true,\n \"combineSelectionAndDetails\": true,\n \"videoUrl\": \"<string>\",\n \"videoRequired\": true,\n \"videoTitle\": \"<string>\",\n \"fieldOverrides\": {},\n \"translations\": {}\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/forms")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"createsAccount\": true,\n \"name\": \"<string>\",\n \"slug\": \"<string>\",\n \"title\": \"<string>\",\n \"subtitle\": \"<string>\",\n \"planStepTitle\": \"<string>\",\n \"classTypeStepTitle\": \"<string>\",\n \"planIds\": [\n 123\n ],\n \"classTypeIds\": [\n 123\n ],\n \"productIds\": [\n 123\n ],\n \"collectsDocuments\": true,\n \"requiresAuthentication\": true,\n \"combineSelectionAndDetails\": true,\n \"videoUrl\": \"<string>\",\n \"videoRequired\": true,\n \"videoTitle\": \"<string>\",\n \"fieldOverrides\": {},\n \"translations\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.1club.ai/v1/platform/forms")
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 \"createsAccount\": true,\n \"name\": \"<string>\",\n \"slug\": \"<string>\",\n \"title\": \"<string>\",\n \"subtitle\": \"<string>\",\n \"planStepTitle\": \"<string>\",\n \"classTypeStepTitle\": \"<string>\",\n \"planIds\": [\n 123\n ],\n \"classTypeIds\": [\n 123\n ],\n \"productIds\": [\n 123\n ],\n \"collectsDocuments\": true,\n \"requiresAuthentication\": true,\n \"combineSelectionAndDetails\": true,\n \"videoUrl\": \"<string>\",\n \"videoRequired\": true,\n \"videoTitle\": \"<string>\",\n \"fieldOverrides\": {},\n \"translations\": {}\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"slug": "<string>",
"createsAccount": true,
"publicUrl": "<string>",
"name": "<string>",
"title": "<string>",
"subtitle": "<string>",
"planStepTitle": "<string>",
"classTypeStepTitle": "<string>",
"planIds": [
123
],
"classTypeIds": [
123
],
"productIds": [
123
],
"registrationFor": "individual",
"signatureMethod": "draw",
"collectsDocuments": true,
"requiresAuthentication": true,
"combineSelectionAndDetails": true,
"displayMode": "page",
"videoUrl": "<string>",
"videoRequired": true,
"videoTitle": "<string>",
"fieldOverrides": {
"firstName": "required",
"lastName": "required",
"email": "required",
"phone": "required",
"dateOfBirth": "required",
"gender": "required",
"address": "required"
},
"translations": {},
"packageIds": [
123
],
"submissionCount": 123,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"questions": [
{
"id": 123,
"label": "<string>",
"type": "text",
"required": true,
"placeholder": "<string>",
"helpText": "<string>",
"sectionHeading": "<string>",
"options": [
"<string>"
],
"contactCustomFieldId": 123,
"translations": {}
}
]
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}Create a form
Creates a form. It is live immediately at the returned publicUrl: there is no draft, so confirm the content with the club before creating one. publicUrl is null when the organization has neither a member portal nor a live website: the form is created, but nothing can open it until one is switched on.
createsAccount is required and permanent. true makes a sign-up form, which registers whoever completes it and sells what it offers: completing one with a plan creates a real membership, or asks for payment when the plan is paid up front. Only active, publicly visible, non-season plans and active, publicly visible products can be offered; anything else is a 400 naming each refused id and why. false makes a fill-in form, which offers nothing, collects answers to its questions (set them with PUT /v1/platform/forms//questions), and creates a lead for a visitor it does not know.
slug is optional and derived from the name when left out. A slug you name that is already taken is a 409 rather than a quiet -2. Read the slug and publicUrl from the response. Supports the Idempotency-Key header.
Needs the forms:write scope, which is new. An API key holding the * wildcard picks it up automatically; an OAuth connection made before it shipped has to be re-authorized.
curl --request POST \
--url https://api.1club.ai/v1/platform/forms \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"createsAccount": true,
"name": "<string>",
"slug": "<string>",
"title": "<string>",
"subtitle": "<string>",
"planStepTitle": "<string>",
"classTypeStepTitle": "<string>",
"planIds": [
123
],
"classTypeIds": [
123
],
"productIds": [
123
],
"collectsDocuments": true,
"requiresAuthentication": true,
"combineSelectionAndDetails": true,
"videoUrl": "<string>",
"videoRequired": true,
"videoTitle": "<string>",
"fieldOverrides": {},
"translations": {}
}
'import requests
url = "https://api.1club.ai/v1/platform/forms"
payload = {
"createsAccount": True,
"name": "<string>",
"slug": "<string>",
"title": "<string>",
"subtitle": "<string>",
"planStepTitle": "<string>",
"classTypeStepTitle": "<string>",
"planIds": [123],
"classTypeIds": [123],
"productIds": [123],
"collectsDocuments": True,
"requiresAuthentication": True,
"combineSelectionAndDetails": True,
"videoUrl": "<string>",
"videoRequired": True,
"videoTitle": "<string>",
"fieldOverrides": {},
"translations": {}
}
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({
createsAccount: true,
name: '<string>',
slug: '<string>',
title: '<string>',
subtitle: '<string>',
planStepTitle: '<string>',
classTypeStepTitle: '<string>',
planIds: [123],
classTypeIds: [123],
productIds: [123],
collectsDocuments: true,
requiresAuthentication: true,
combineSelectionAndDetails: true,
videoUrl: '<string>',
videoRequired: true,
videoTitle: '<string>',
fieldOverrides: {},
translations: {}
})
};
fetch('https://api.1club.ai/v1/platform/forms', 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/forms",
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([
'createsAccount' => true,
'name' => '<string>',
'slug' => '<string>',
'title' => '<string>',
'subtitle' => '<string>',
'planStepTitle' => '<string>',
'classTypeStepTitle' => '<string>',
'planIds' => [
123
],
'classTypeIds' => [
123
],
'productIds' => [
123
],
'collectsDocuments' => true,
'requiresAuthentication' => true,
'combineSelectionAndDetails' => true,
'videoUrl' => '<string>',
'videoRequired' => true,
'videoTitle' => '<string>',
'fieldOverrides' => [
],
'translations' => [
]
]),
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/forms"
payload := strings.NewReader("{\n \"createsAccount\": true,\n \"name\": \"<string>\",\n \"slug\": \"<string>\",\n \"title\": \"<string>\",\n \"subtitle\": \"<string>\",\n \"planStepTitle\": \"<string>\",\n \"classTypeStepTitle\": \"<string>\",\n \"planIds\": [\n 123\n ],\n \"classTypeIds\": [\n 123\n ],\n \"productIds\": [\n 123\n ],\n \"collectsDocuments\": true,\n \"requiresAuthentication\": true,\n \"combineSelectionAndDetails\": true,\n \"videoUrl\": \"<string>\",\n \"videoRequired\": true,\n \"videoTitle\": \"<string>\",\n \"fieldOverrides\": {},\n \"translations\": {}\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/forms")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"createsAccount\": true,\n \"name\": \"<string>\",\n \"slug\": \"<string>\",\n \"title\": \"<string>\",\n \"subtitle\": \"<string>\",\n \"planStepTitle\": \"<string>\",\n \"classTypeStepTitle\": \"<string>\",\n \"planIds\": [\n 123\n ],\n \"classTypeIds\": [\n 123\n ],\n \"productIds\": [\n 123\n ],\n \"collectsDocuments\": true,\n \"requiresAuthentication\": true,\n \"combineSelectionAndDetails\": true,\n \"videoUrl\": \"<string>\",\n \"videoRequired\": true,\n \"videoTitle\": \"<string>\",\n \"fieldOverrides\": {},\n \"translations\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.1club.ai/v1/platform/forms")
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 \"createsAccount\": true,\n \"name\": \"<string>\",\n \"slug\": \"<string>\",\n \"title\": \"<string>\",\n \"subtitle\": \"<string>\",\n \"planStepTitle\": \"<string>\",\n \"classTypeStepTitle\": \"<string>\",\n \"planIds\": [\n 123\n ],\n \"classTypeIds\": [\n 123\n ],\n \"productIds\": [\n 123\n ],\n \"collectsDocuments\": true,\n \"requiresAuthentication\": true,\n \"combineSelectionAndDetails\": true,\n \"videoUrl\": \"<string>\",\n \"videoRequired\": true,\n \"videoTitle\": \"<string>\",\n \"fieldOverrides\": {},\n \"translations\": {}\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"slug": "<string>",
"createsAccount": true,
"publicUrl": "<string>",
"name": "<string>",
"title": "<string>",
"subtitle": "<string>",
"planStepTitle": "<string>",
"classTypeStepTitle": "<string>",
"planIds": [
123
],
"classTypeIds": [
123
],
"productIds": [
123
],
"registrationFor": "individual",
"signatureMethod": "draw",
"collectsDocuments": true,
"requiresAuthentication": true,
"combineSelectionAndDetails": true,
"displayMode": "page",
"videoUrl": "<string>",
"videoRequired": true,
"videoTitle": "<string>",
"fieldOverrides": {
"firstName": "required",
"lastName": "required",
"email": "required",
"phone": "required",
"dateOfBirth": "required",
"gender": "required",
"address": "required"
},
"translations": {},
"packageIds": [
123
],
"submissionCount": 123,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"questions": [
{
"id": 123,
"label": "<string>",
"type": "text",
"required": true,
"placeholder": "<string>",
"helpText": "<string>",
"sectionHeading": "<string>",
"options": [
"<string>"
],
"contactCustomFieldId": 123,
"translations": {}
}
]
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"error": "<string>",
"code": "<string>",
"details": {}
}{
"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
Fields accepted when creating a form. Unknown fields are refused, and packageIds, customFieldIds and showFamilyMembers are refused by name with the reason. Set questions afterwards with PUT /v1/platform/forms/{id}/questions.
Required and permanent. true: a sign-up form, which registers whoever completes it and can sell plans and products. false: a fill-in form, which only collects answers.
Internal name, shown to staff in the admin portal.
200Optional; derived from the name when left out. A taken slug is a 409 (FORM_SLUG_TAKEN).
120^[a-z0-9]+(-[a-z0-9]+)*$Heading at the top of the form. Blank or null falls back to the wizard's own wording.
200Sub-heading under the title. Blank or null falls back to the wizard's own wording.
200Heading above the plan picker (sign-up forms). Blank or null falls back to the wizard's own wording.
200Heading above the class-type picker (sign-up forms). Blank or null falls back to the wizard's own wording.
200Sign-up forms only. Plans the form sells, from GET /v1/platform/plans. Each must be active, publicly visible and not a season plan (FORM_PLAN_NOT_SELLABLE names each refused id and why). When any are set, the person must choose one to finish.
100Sign-up forms only. Class types offered as an interest, from GET /v1/platform/class-types. Nothing is booked.
100Sign-up forms only. Products the form sells, from GET /v1/platform/products. Each must be active, publicly visible and sold as a product (FORM_PRODUCT_NOT_SELLABLE).
100Who the form registers: the person filling it in, only their children, or both.
individual, child, both How documents are signed: drawn, typed name, or a checkbox.
draw, type, check Whether the form asks for the organization's documents and a signature. Which documents is set by the organization's document rules.
Fill-in forms only: the person must be signed in to fill it in. Refused on a sign-up form (FORM_AUTHENTICATION_FILL_IN_ONLY).
Put the "choose what to buy" step and the details step on one page. Needs a form that sells something (FORM_NOTHING_TO_COMBINE).
How a link to the form opens: as its own page, or as a dialog over the page it was opened from.
page, dialog A Cloudinary video URL from the media library, shown as a step before review. null removes the video step.
500The person cannot continue until the video has played to the end. Needs a video (FORM_VIDEO_REQUIRED_WITHOUT_VIDEO).
Heading above the video. Blank or null falls back to the wizard's own wording.
200Fill-in forms only (FORM_FIELD_OVERRIDES_FILL_IN_ONLY; an empty object is accepted and ignored on a sign-up form, which asks the organization's own sign-up fields instead). Which built-in contact fields the form asks: required asks it, hidden does not. A fill-in form cannot ask one optionally. address asks the whole postal address. On update the map is merged: the fields you send change and the rest stay as they are, so send hidden to stop asking one. null clears every override.
Show child attributes
Show child attributes
Per-locale wording, as { "es": { "title": "..." } }. Replaces the whole map. Translatable fields: title, subtitle, planStepTitle, classTypeStepTitle, videoTitle; any other key is refused (INVALID_TRANSLATIONS).
Show child attributes
Show child attributes
Response
Form created, and live
A form: a page people fill in, live at publicUrl.
The last part of publicUrl. Fixed once the form exists.
true for a sign-up form (registers the person, can sell); false for a fill-in form (collects answers). Fixed once the form exists.
Where the form is filled in, on the organization's own website or member portal. It is live: anyone with the link can use it. null when the organization has neither switched on, so nothing can open the form until one is.
Internal name, shown to staff in the admin portal.
200Heading at the top of the form. Blank or null falls back to the wizard's own wording.
200Sub-heading under the title. Blank or null falls back to the wizard's own wording.
200Heading above the plan picker (sign-up forms). Blank or null falls back to the wizard's own wording.
200Heading above the class-type picker (sign-up forms). Blank or null falls back to the wizard's own wording.
200Sign-up forms only. Plans the form sells, from GET /v1/platform/plans. Each must be active, publicly visible and not a season plan (FORM_PLAN_NOT_SELLABLE names each refused id and why). When any are set, the person must choose one to finish.
100Sign-up forms only. Class types offered as an interest, from GET /v1/platform/class-types. Nothing is booked.
100Sign-up forms only. Products the form sells, from GET /v1/platform/products. Each must be active, publicly visible and sold as a product (FORM_PRODUCT_NOT_SELLABLE).
100Who the form registers: the person filling it in, only their children, or both.
individual, child, both How documents are signed: drawn, typed name, or a checkbox.
draw, type, check Whether the form asks for the organization's documents and a signature. Which documents is set by the organization's document rules.
Fill-in forms only: the person must be signed in to fill it in. Refused on a sign-up form (FORM_AUTHENTICATION_FILL_IN_ONLY).
Put the "choose what to buy" step and the details step on one page. Needs a form that sells something (FORM_NOTHING_TO_COMBINE).
How a link to the form opens: as its own page, or as a dialog over the page it was opened from.
page, dialog A Cloudinary video URL from the media library, shown as a step before review. null removes the video step.
500The person cannot continue until the video has played to the end. Needs a video (FORM_VIDEO_REQUIRED_WITHOUT_VIDEO).
Heading above the video. Blank or null falls back to the wizard's own wording.
200Fill-in forms only (FORM_FIELD_OVERRIDES_FILL_IN_ONLY; an empty object is accepted and ignored on a sign-up form, which asks the organization's own sign-up fields instead). Which built-in contact fields the form asks: required asks it, hidden does not. A fill-in form cannot ask one optionally. address asks the whole postal address. On update the map is merged: the fields you send change and the rest stay as they are, so send hidden to stop asking one. null clears every override.
Show child attributes
Show child attributes
Per-locale wording, as { "es": { "title": "..." } }. Replaces the whole map. Translatable fields: title, subtitle, planStepTitle, classTypeStepTitle, videoTitle; any other key is refused (INVALID_TRANSLATIONS).
Show child attributes
Show child attributes
Read-only here: packages are added in the admin portal.
How many people have completed it. The answers are not served by this API.
In order. On a single-form read and on every write; not in the list.
Show child attributes
Show child attributes
Was this page helpful?