Create a trainee

POST/api/public/trainees

Create a new trainee for the coach. Requires name, email, and phoneNumber.

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

Header parameters
NameTypeDescription
Idempotency-Keystring

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

Request body fields
NameTypeDescription
namerequiredstring

Trainee full name

emailrequiredstring

Trainee email

phoneNumberrequiredstring

Trainee phone number

goalstring

Optional coaching goal

passwordAsPhoneNumberboolean

Set the trainee login password to their phone number instead of a random one. Israeli numbers are normalized to their local 0-prefixed form, so "+972501234567", "972-50-123-4567" and "050 123 4567" all become the password "0501234567", and the trainee can log in by typing any of those forms. Numbers from every other country are supported too and keep their international form, so "+1 415 555 2671" becomes "+14155552671"; a trainee with a non-Israeli number must include the country code when logging in, because the national form ("415-555-2671") cannot be mapped back. Send a clean number: any extra digits, such as an extension, become part of the password. The resulting password is always returned in the password field of this response and is not retrievable afterwards. Changing the phone number later with PATCH /trainees/{traineeId} does not change the password. Rejected with 400 only when phoneNumber has no digits at all, or fewer than 6 digits; error.details.reason says which. If the email belongs to an existing trainee account that is merely attached to the coach, that account keeps its current password and a warning is returned.

labelsstring[]

Optional ids of existing labels to attach. Each item must be the label id (24-character MongoDB ObjectId) as returned in the id field of GET /labels — not the label text. Label text is not accepted here and no new label is created; to attach a label by text, or to create one, use POST /trainees/{traineeId}/labels instead. Requires the labels:write scope: without it the trainee is still created and a warning is returned in the warnings array.

Maximum items: 100

sendCredentialsobject

Deliver the new trainee their app access details as part of creating them, instead of calling POST /trainees/{traineeId}/send-credentials afterwards (which would reset the password again). Only applies to a newly created account: if the email belongs to an existing trainee that is merely attached to this coach, that account keeps its current password, nothing is sent, and a warning is returned.

sendCredentials.emailboolean

Email the trainee their app access details right after creation.

sendCredentials.whatsappboolean

Also send the access details over WhatsApp, followed by the password in a separate message. Requires a connected WhatsApp account and a phone number on the trainee.

whatsappTemplatestring

Optional message wording used when sendCredentials.whatsapp is true, with the {firstName}, {email}, {password} and {appLink} placeholders. When omitted, the template saved in the dashboard is used, then a built-in default. The password is always sent in a separate follow-up message, so the template normally does not need {password}.

Request examples

cURL
curl --request POST \
  --url 'https://api.coach-platform.com/api/public/trainees' \
  --header 'Authorization: Bearer cp_live_...' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "<string>",
  "email": "<string>",
  "phoneNumber": "<string>"
}'
JavaScript (fetch)
const response = await fetch('https://api.coach-platform.com/api/public/trainees', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer cp_live_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "<string>",
    "email": "<string>",
    "phoneNumber": "<string>"
  }),
});

const data = await response.json();

Responses

201 Created

Example body (application/json)
{
  "data": {
    "id": "<string>",
    "name": "<string>",
    "email": "<string>",
    "phoneNumber": "<string>",
    "goal": "<string>",
    "profileImageUrl": "<string>",
    "personalDetails": {},
    "labels": [
      {
        "id": "<string>",
        "text": "<string>",
        "color": "<string>",
        "coachId": "<string>"
      }
    ],
    "assignedEmployees": [
      {
        "id": "<string>",
        "name": "<string>"
      }
    ],
    "activeCoachDetails": {
      "activeEscort": "<string>",
      "pendingCoach": "<string>",
      "pendingCoachDate": "<date-time>"
    },
    "createdAt": "<date-time>",
    "password": "<string>",
    "credentialsSent": {
      "email": true,
      "whatsapp": true
    }
  },
  "warnings": [
    {
      "field": "<string>",
      "message": "<string>"
    }
  ]
}
Response fields (28)
Response fields
NameTypeDescription
dataobject
data.idstring
data.namestring
data.emailstring
data.phoneNumberstring
data.goalstring
data.profileImageUrlstring
data.personalDetailsobject
data.labelsobject[]
data.labels[].idstring
data.labels[].textstring
data.labels[].colorstring
data.labels[].coachIdstring
data.assignedEmployeesobject[]
data.assignedEmployees[].idstring
data.assignedEmployees[].namestring
data.activeCoachDetailsobject
data.activeCoachDetails.activeEscortstring | null

Id of the escort currently in use for this trainee. Fetch the full escort with GET /escorts/{escortId}.

data.activeCoachDetails.pendingCoachstring | null

Coach id this trainee is pending approval for. Set when the trainee was invited but has not been approved yet; null once approved. Use ?status=pending on GET /trainees to list only these.

data.activeCoachDetails.pendingCoachDatestring<date-time> | null

When the trainee entered the pending state. Use it to measure how long approval has been waiting.

data.createdAtstring<date-time>
data.passwordstring

Login password, returned only in this create response and never retrievable again. Randomly generated, or the normalized phone number when passwordAsPhoneNumber was sent as true. Present only when a brand-new trainee account was created; absent when an existing trainee was attached to the coach. Calling POST /trainees/{traineeId}/send-credentials resets it.

data.credentialsSentobject

Present only when sendCredentials was supplied. Reports which channels the access details were handed off to for delivery. A false value is always explained by an entry in warnings. WhatsApp delivery happens in the background and is spaced out to protect the account, so true means the message was queued, not that it has already arrived.

data.credentialsSent.emailboolean
data.credentialsSent.whatsappboolean
warningsobject[]

Non-fatal problems with follow-up writes. The resource was created, but each listed field was not applied.

warnings[].fieldstring
warnings[].messagestring

Error responses

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 409 Conflict
  • 429 Too Many Requests
  • 500 Internal Server Error

These statuses share the same response body.

Example body (application/json)
{
  "error": {
    "code": "<string>",
    "message": "<string>",
    "fix": "<string>",
    "details": {},
    "retryAfterMs": 123
  }
}
Error fields (6)
Error fields
NameTypeDescription
errorobject
error.codestring
error.messagestring
error.fixstring | null
error.detailsobject | null
error.retryAfterMsnumber | null

This page is also available as Markdown. Browse it in the interactive explorer or download the OpenAPI spec.