Start a coaching period
POST/api/public/escorts
Start a new coaching period (escort) for a trainee. The plan ids required depend on escortType: "Training" needs trainingPlanId, "Nutrition" needs nutritionMenuId, "Nutrition and training" needs both, and "Other" needs neither. Create the plan or menu first (POST /training-plans, POST /nutrition-menus) and pass its id here. A trainee may only have one active escort at a time.
Authentication
Requires an API key, sent as Authorization: Bearer cp_live_....
API keys carry scopes. Get the catalog of scopes and webhook events lists every scope.
Header parameters
| Name | Type | Description |
|---|---|---|
Idempotency-Key | string | Optional. Send a unique key (e.g. a UUID) to make this POST safe to retry. The same key within 24h returns the original result instead of creating a duplicate. |
Request body
application/json, required
| Name | Type | Description |
|---|---|---|
traineeIdrequired | string | |
escortTyperequired | string | Determines which plan ids are required: Training -> trainingPlanId, Nutrition -> nutritionMenuId, Nutrition and training -> both, Other -> none. |
startDaterequired | string | ISO date. |
endDate | string | ISO date. Provide this or months. |
months | number | Duration in months. Provide this or endDate. |
goal | string | |
price | number | |
paymentDate | string | |
paymentMethod | string | |
paymentNumber | number | |
escortMeetingType | string | |
onlineEscort | string | |
inPersonEscort | string | |
notes | string | Optional first note on the coaching period. |
cardioDays | number | |
cardioTime | number | |
trainingDays | number | |
dailyStepsTarget | number | |
trainingPlanId | string | Assign an existing training plan. Required when escortType is "Training" or "Nutrition and training". |
nutritionMenuId | string | Assign an existing nutrition menu. Required when escortType is "Nutrition" or "Nutrition and training". |
skipRegistrationForms | boolean | true: the trainee is not asked to fill registration forms in the app during this coaching period. false: the app asks the trainee to fill every active registration form aimed at them that they have not filled yet. Defaults to true when omitted on create. This is the same switch as the registration-forms toggle on the trainee profile. It belongs to this coaching period only, and a link to a specific form sent to the trainee keeps working either way. |
Request examples
curl --request POST \
--url 'https://api.coach-platform.com/api/public/escorts' \
--header 'Authorization: Bearer cp_live_...' \
--header 'Content-Type: application/json' \
--data '{
"traineeId": "<string>",
"escortType": "Nutrition",
"startDate": "<string>"
}'const response = await fetch('https://api.coach-platform.com/api/public/escorts', {
method: 'POST',
headers: {
Authorization: 'Bearer cp_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"traineeId": "<string>",
"escortType": "Nutrition",
"startDate": "<string>"
}),
});
const data = await response.json();Responses
201 Created
{
"data": {
"id": "<string>",
"coach": "<string>",
"trainee": {
"id": "<string>",
"name": "<string>",
"email": "<string>",
"phoneNumber": "<string>"
},
"status": "pending",
"escortType": "<string>",
"startDate": "<date-time>",
"endDate": "<date-time>",
"months": 123,
"isUnlimited": true,
"training": {
"activeTrainingPlan": {
"id": "<string>",
"title": "<string>",
"level": "<string>"
},
"trainingDays": null
},
"nutrition": {
"activeNutritionPlan": {
"id": "<string>",
"title": "<string>",
"level": "<string>"
}
},
"skipRegistrationForms": true,
"createdAt": "<date-time>"
},
"warnings": [
{
"field": "<string>",
"message": "<string>"
}
]
}Response fields (30)
| Name | Type | Description |
|---|---|---|
data | object | |
data.id | string | |
data.coach | string | |
data.trainee | object | The escorted trainee. email and phoneNumber are returned only when the key also has the trainees:read scope. |
data.trainee.id | string | |
data.trainee.name | string | |
data.trainee.email | string | |
data.trainee.phoneNumber | string | |
data.status | string | |
data.escortType | string | |
data.startDate | string<date-time> | |
data.endDate | string<date-time> | null | When the coaching period ends. null when the escort runs with no time limit, in which case isUnlimited is true. |
data.months | integer | null | Duration in months. null when the escort runs with no time limit. |
data.isUnlimited | boolean | True when the escort has no end date. |
data.training | object | |
data.training.activeTrainingPlan | object | null | |
data.training.activeTrainingPlan.id | string | |
data.training.activeTrainingPlan.title | string | |
data.training.activeTrainingPlan.level | string | |
data.training.trainingDays | any | |
data.nutrition | object | |
data.nutrition.activeNutritionPlan | object | null | |
data.nutrition.activeNutritionPlan.id | string | |
data.nutrition.activeNutritionPlan.title | string | |
data.nutrition.activeNutritionPlan.level | string | |
data.skipRegistrationForms | boolean | true when the trainee is not asked to fill registration forms in the app during this coaching period. Change it with |
data.createdAt | string<date-time> | |
warnings | object[] | Non-fatal problems with follow-up writes. The resource was created, but each listed field was not applied. |
warnings[].field | string | |
warnings[].message | string |
Error responses
400Bad Request401Unauthorized403Forbidden404Not Found409Conflict429Too Many Requests500Internal Server Error
These statuses share the same response body.
{
"error": {
"code": "<string>",
"message": "<string>",
"fix": "<string>",
"details": {},
"retryAfterMs": 123
}
}Error fields (6)
| Name | Type | Description |
|---|---|---|
error | object | |
error.code | string | |
error.message | string | |
error.fix | string | null | |
error.details | object | null | |
error.retryAfterMs | number | null |
More Escorts endpoints
This page is also available as Markdown. Browse it in the interactive explorer or download the OpenAPI spec.