Scheduling track cohorts (Scheduled Tracks)¶
A scheduled track is a time-bound instance of a track, run for a specific cohort. Where a track is a reusable template — "Engineering Onboarding" — a scheduled track is one delivery of it: "Engineering Onboarding, Q2 2025", with real dates, a location, a capacity, and concrete event sessions in place of the template's event-series placeholders.
This guide creates a scheduled track from a template, picks the events for it, adjusts its schedule and items, and (optionally) links a Slack channel.
How it differs from a track¶
- A scheduled track is created from a template track and keeps a read-only reference to it (
original_track). - It snapshots the template's items at creation time. Editing the template afterward does not change an existing scheduled track.
- It carries scheduling fields a plain track doesn't:
starts_at,ends_at,location,is_online,is_local, andcapacity. - Template tracks usually contain event-series placeholders ("Weekly Sync" as a type). A scheduled track replaces each placeholder with a concrete event — a specific session on a specific date — via the swap-events action.
1. Schedule a track¶
Turn a template track into a cohort instance with the schedule action on the source track:
curl -X POST https://acme.plusplus.app/api/v2/tracks/7a8b9c0d-…/schedule/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Engineering Onboarding (Q2 2025)",
"starts_at": "2025-04-01T09:00:00Z",
"ends_at": "2025-04-30T17:00:00Z",
"location_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"is_online": true,
"is_local": true,
"capacity": 20
}'
{
"public_id": "f1e2d3c4-…",
"name": "Engineering Onboarding (Q2 2025)",
"content_type": "scheduled_track",
"original_track": { "public_id": "7a8b9c0d-…", "name": "Engineering Onboarding" },
"starts_at": "2025-04-01T09:00:00Z",
"ends_at": "2025-04-30T17:00:00Z",
"location": { "public_id": "6ba7b810-…", "name": "San Francisco HQ", "timezone": "America/Los_Angeles" },
"capacity": 20,
"is_capacity_full": false,
"track_items_count": 6,
"slack_channel": null
}
A few things to know:
- Every field is optional. Omit
nameand it inherits the template's name; omit the scheduling fields and they stay empty. The new scheduled track still snapshots the template's items. capacityis0for unlimited. When set,is_capacity_fullflips totrueonce assignments reach it.location_idis the location'spublic_id(the same id you see in an event series'location).- The template is untouched. Scheduling reads the template's items and copies them onto the new scheduled track.
2. Pick events for each event-series slot¶
Right after scheduling, any event-series placeholder in the track is still a type, not a real session. Use swap-events to choose the concrete event for each slot:
curl -X POST https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/swap-events/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{
"swapped_events": [
{
"old_event": "e1111111-…",
"new_event": "e6666666-…",
"enrollment_action": "move"
}
]
}'
old_eventis thepublic_idof the slot you're filling — either the event-series placeholder currently in the track (first-time scheduling) or a concrete event already in it (re-pointing the cohort at a new date).new_eventis thepublic_idof the concrete event to drop into that slot.-
enrollment_actiondecides what happens to people already enrolled on the old slot:keep— leave their enrollments on the old event; don't enroll them in the new one.move— migrate their enrollments to the new event, preserving each attendee's method (in person vs. online).
When the slot is an event-series placeholder, nobody is enrolled on it yet, so the action decides about the cohort's assignees instead:
moveenrolls them in the new event (online when it offers both formats), andkeepenrolls no one.
You can swap several slots in one call. The response is the updated scheduled track.
Running the next cohort
To stand up the next cohort, schedule the template again and swap in that cohort's event dates. The first cohort's scheduled track keeps its own events — the two cohorts are fully independent.
3. Change the cohort's schedule¶
Dates slip and rooms change. Update the scheduling fields — and any other track field, such as name or is_hidden — with a partial update:
curl -X PATCH https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-H 'If-Match: W/"a1b2c3"' \
-d '{
"starts_at": "2025-04-08T09:00:00Z",
"ends_at": "2025-05-07T17:00:00Z",
"location_id": null,
"capacity": 25
}'
- Only the fields you send change. Send
nullto clearstarts_at,ends_atorlocation_id. If-Matchis optional but recommended. Pass theETagfrom your last read and the update is rejected with412if someone changed the cohort since.- The template is fixed. A scheduled track always points at the track it was scheduled from.
capacitycounts every assignment, including dropped ones, so lowering it below the number of assignments makesis_capacity_fullreadtrue.
4. Adjust the cohort's items¶
A scheduled track has the same /track-items/ and /sections/ sub-resources as a track, and they behave the same way — see Building learning paths. For example, to add a welcome article at the top (on a cohort with sections, also pass the section_id it goes in):
curl -X POST https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/track-items/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{ "items": [{ "content_item_id": "a9b8c7d6-…", "position": 0 }] }'
What is specific to a scheduled track:
- It keeps at least one item (
422 empty_scheduled_track) and cannot contain another scheduled track (422 nested_scheduled_track). - Adding an item reaches people already assigned. Each assignee gets an assignment on the new item, and an enrollment when the item is an event. This happens in the background, shortly after the response.
- Removing a concrete event marks its attendees as not going, without notifying them. To move attendees to a different session instead, swap the event (step 2) with
enrollment_action: "move". - An event item's due date is always the event's end. An
assignment_due_datesent for an event item is ignored.
5. Read progress and the cohort's events¶
A learner's progress on the cohort, measured over its required items:
curl "https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/progress/?user=0a1b2c3d-…" \
-H "Authorization: Bearer your_token_here"
The cohort's concrete events, soonest first — the sessions attendees will actually go to. Event-series slots still waiting for an event (step 2) are not listed:
curl https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/events/ \
-H "Authorization: Bearer your_token_here"
6. Link a Slack channel (optional)¶
If your account has Slack connected, you can attach a channel that everyone assigned to the cohort is auto-invited to:
curl -X POST https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/slack-channel/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{ "name": "q2-2025-onboarding" }'
{ "id": "C0123456789", "name": "q2-2025-onboarding", "url": "https://acme.slack.com/archives/C0123456789" }
Posting a channel that doesn't exist creates it; posting an existing one links it. When a channel is already linked, posting a different name renames that channel in Slack for all its members. To remove the link without deleting the channel:
curl -X DELETE https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/slack-channel/ \
-H "Authorization: Bearer your_token_here"
When Slack isn't configured for the account, the create call returns 400 slack_not_configured.
7. Read and list scheduled tracks¶
# One scheduled track
curl https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/ \
-H "Authorization: Bearer your_token_here"
# All of them, newest first
curl "https://acme.plusplus.app/api/v2/scheduled-tracks/?ordering=-starts_at" \
-H "Authorization: Bearer your_token_here"
The list supports search (by name), is_archived, and is_hidden filters, and ordering by name, created, modified, starts_at, or ends_at.
Assigning a scheduled track works exactly as it does for a track — see Building learning paths and Assignments & completion.
Common pitfalls¶
- Scheduling a template whose Equivalent Content holds an option that can no longer be added. The cohort gets its own copy of each Equivalent Content step, so every option is checked again, the same way as when you edit that step. An archived option, or one you can no longer add, returns
400 validation_error. Remove the option from the template first. - Swapping an event that isn't in the track.
old_eventmust reference a slot currently in this scheduled track, otherwise you get400 event_not_in_track. List the track's items first if you're unsure which slots exist. - Pointing
new_eventat an event series.new_eventmust be a concrete event, not an event series — the series is the placeholder you're replacing. - Editing the template to change a running cohort. It won't. A scheduled track is a snapshot; change the cohort through its own update, items and swap-events endpoints.
- Removing an event to reschedule it. Removing an event marks its attendees as not going. Swap the event for the new session with
moveinstead, and attendees follow it. - Expecting
keepto enroll people in the new event.keepdeliberately leaves existing attendees on the old event, and on a placeholder it enrolls none of the cohort's assignees. Usemoveto bring them across.