curl -X POST "https://api.fitsociety.io/public/v1/forms" \
-H "Authorization: Bearer <access_token>" \
-H "Idempotency-Key: form-management-20260909-001" \
-H "Content-Type: application/json" \
-d '{"name":{"en":"Initial intake","nl":"Eerste intake"},"defaultLanguage":"en","formType":"intake","sections":[{"title":{"en":"General","nl":"Algemeen"},"questions":[{"name":{"en":"What would you like to achieve?","nl":"Wat wil je bereiken?"},"type":"Open Question","required":true}]}]}'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({name: {}, defaultLanguage: '<string>'})
};
fetch('https://api.fitsociety.io/public/v1/forms', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.fitsociety.io/public/v1/forms"
payload = {
"name": {},
"defaultLanguage": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"form": {
"formId": "66f7b8b1e13c8d25f4d3d90a",
"name": {},
"description": {},
"defaultLanguage": "string",
"formType": "string",
"sectionCount": 1,
"questionCount": 1,
"hasObjectives": true,
"hasMeasurements": true,
"createdAt": "2026-07-14T10:00:00.000Z",
"updatedAt": "2026-07-14T10:00:00.000Z",
"sections": [
{
"sectionId": "66f7b8b1e13c8d25f4d3d90a",
"title": {},
"description": {},
"questions": [
{
"questionId": "66f7b8b1e13c8d25f4d3d90a",
"name": {},
"type": "string",
"required": true,
"options": [
{
"optionId": "66f7b8b1e13c8d25f4d3d90a",
"label": {}
}
],
"linearScale": {
"startValue": 1,
"endValue": 1,
"startLabel": {},
"endLabel": {}
},
"ratingScale": 1,
"maxFiles": 1,
"order": 1
}
]
}
]
}
},
"meta": {
"requestId": "4f849d7d-f4f1-45cc-b4b7-3984a3d17f83",
"rateLimit": {
"limit": 10,
"remaining": 9,
"resetSeconds": 1
},
"idempotency": {
"replayed": false
}
}
}Create form
Requires forms:write and an OAuth client with a coach attribution context. Creates a company form template with localized text maps, a required defaultLanguage and optional sections/questions. Choice options have localized labels and receive stable option IDs. Internal apps continue using plain default-language text. Company and coach ownership are derived from the token. Unknown fields, client answers, objectives, measurements, media URLs and AI metadata are not accepted. Omit section, question and option IDs when creating a form.
curl -X POST "https://api.fitsociety.io/public/v1/forms" \
-H "Authorization: Bearer <access_token>" \
-H "Idempotency-Key: form-management-20260909-001" \
-H "Content-Type: application/json" \
-d '{"name":{"en":"Initial intake","nl":"Eerste intake"},"defaultLanguage":"en","formType":"intake","sections":[{"title":{"en":"General","nl":"Algemeen"},"questions":[{"name":{"en":"What would you like to achieve?","nl":"Wat wil je bereiken?"},"type":"Open Question","required":true}]}]}'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({name: {}, defaultLanguage: '<string>'})
};
fetch('https://api.fitsociety.io/public/v1/forms', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.fitsociety.io/public/v1/forms"
payload = {
"name": {},
"defaultLanguage": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"form": {
"formId": "66f7b8b1e13c8d25f4d3d90a",
"name": {},
"description": {},
"defaultLanguage": "string",
"formType": "string",
"sectionCount": 1,
"questionCount": 1,
"hasObjectives": true,
"hasMeasurements": true,
"createdAt": "2026-07-14T10:00:00.000Z",
"updatedAt": "2026-07-14T10:00:00.000Z",
"sections": [
{
"sectionId": "66f7b8b1e13c8d25f4d3d90a",
"title": {},
"description": {},
"questions": [
{
"questionId": "66f7b8b1e13c8d25f4d3d90a",
"name": {},
"type": "string",
"required": true,
"options": [
{
"optionId": "66f7b8b1e13c8d25f4d3d90a",
"label": {}
}
],
"linearScale": {
"startValue": 1,
"endValue": 1,
"startLabel": {},
"endLabel": {}
},
"ratingScale": 1,
"maxFiles": 1,
"order": 1
}
]
}
]
}
},
"meta": {
"requestId": "4f849d7d-f4f1-45cc-b4b7-3984a3d17f83",
"rateLimit": {
"limit": 10,
"remaining": 9,
"resetSeconds": 1
},
"idempotency": {
"replayed": false
}
}
}Authorizations
Public API access token issued by /public/v1/oauth/token. Example: Authorization: Bearer fspt_.... Each resource request rechecks the token company's current provider access. Disabling access blocks existing tokens with 403 auth.provider_unavailable.
Headers
Required for Public API write requests. Reusing the same key with the same method, path, and body replays the stored successful response; reusing it with a different request returns 409 idempotency.conflict.
1 - 200"booking-create-20260714-001"
Body
Up to 25 languages per field. Valid BCP 47 keys are canonicalized; duplicate canonical keys and und are rejected. Every non-empty map must contain defaultLanguage. Required text cannot be empty. A supplied map replaces all languages for that field; {} clears an optional field. Null maps, null values and plain strings are rejected.
Show child attributes
Show child attributes
Required on create and on the first Public API update of an internal form. A canonical BCP 47 language tag, such as nl or en-GB; und is reserved for unknown-language responses. Every non-empty text map must include this language. When changing it, all retained maps must contain the new language. Declaring the language of a legacy form assigns its existing source text to that language without translating it.
80Cannot be changed after creation.
intake, checkup Up to 25 languages per field. Valid BCP 47 keys are canonicalized; duplicate canonical keys and und are rejected. Every non-empty map must contain defaultLanguage. Required text cannot be empty. A supplied map replaces all languages for that field; {} clears an optional field. Null maps, null values and plain strings are rejected.
Show child attributes
Show child attributes
On update, replaces the complete sections array. Omit to preserve sections. Existing section/question IDs may be retained; duplicate or foreign IDs are rejected.
Show child attributes
Show child attributes