Send a notification
POST/api/public/notifications
Send an in-app notification to one trainee, immediately or on a schedule. Requires the notifications:send scope, which is separate from messaging:send (WhatsApp). Each trainee accepts at most 10 API-sent notifications per day; over that the endpoint returns 429. High-risk — anyone with the key can deliver push notifications to your trainees.
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 | |
titlerequired | string | Short notification title |
messagerequired | string | Body text |
type | string | Delivery style. "push" (default) is a standard push notification, "popup" shows an in-app popup card, "both" sends a push and shows the popup. Never sends WhatsApp. |
popupData | object | Content of the in-app popup card. Only used when type is "popup" or "both". |
popupData.title | string | |
popupData.description | string | |
popupData.videoLink | string | |
popupData.actionLink | string | URL opened when the call-to-action button is pressed |
popupData.buttonText | string | Call-to-action button label |
popupData.imageUrl | string | |
popupData.dismissible | boolean | Whether the trainee can dismiss the popup. Defaults to true. |
scheduledAt | string | Shorthand for a single future delivery: an ISO date-time. Use schedule instead for recurring sends. Cannot be combined with schedule. |
schedule | object | Deliver later, optionally repeating. Omit both schedule and scheduledAt to send immediately. Cannot be combined with scheduledAt. |
schedule.startDaterequired | string | First send date, ISO-8601. Either a date ("2026-06-15") or a full date-time. |
schedule.timeOfDayrequired | string | Local time of day in 24h "HH:mm" format, e.g. "09:30" |
schedule.frequency | string | Defaults to "once" (a single future send). Any other value repeats until endDate. |
schedule.weeklyDays | number[] | 0=Sunday..6=Saturday. Required for weekly, biweekly and triweekly. |
schedule.monthlyMode | string | For monthly: byDate uses monthlyDays, byWeekday uses monthlyWeekday plus monthlyWeekOfMonth. Defaults to byDate. |
schedule.monthlyDays | number[] | Days of the month for monthly byDate |
schedule.monthlyWeekday | number | 0=Sunday..6=Saturday for monthly byWeekday |
schedule.monthlyWeekOfMonth | number | 1-4, or -1 for the last week of the month |
schedule.endDate | string | Optional ISO date that stops a recurring schedule |
schedule.timezone | string | IANA timezone. Defaults to Asia/Jerusalem. |
Request examples
curl --request POST \
--url 'https://api.coach-platform.com/api/public/notifications' \
--header 'Authorization: Bearer cp_live_...' \
--header 'Content-Type: application/json' \
--data '{
"traineeId": "<string>",
"title": "<string>",
"message": "<string>"
}'const response = await fetch('https://api.coach-platform.com/api/public/notifications', {
method: 'POST',
headers: {
Authorization: 'Bearer cp_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"traineeId": "<string>",
"title": "<string>",
"message": "<string>"
}),
});
const data = await response.json();Responses
201 Created
{
"data": {
"id": "<string>",
"type": "push",
"status": "sent",
"recipients": 123,
"scheduledFor": "<string>"
},
"warnings": [
{
"field": "<string>",
"message": "<string>"
}
]
}Response fields (9)
| Name | Type | Description |
|---|---|---|
data | object | |
data.id | string | null | |
data.type | string | |
data.status | string | |
data.recipients | number | How many trainees the notification was delivered to |
data.scheduledFor | string | null | ISO date-time of the first delivery when scheduled |
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 Notifications endpoints
This page is also available as Markdown. Browse it in the interactive explorer or download the OpenAPI spec.