Skip to content

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, 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, and capacity.
  • 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 pp_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": 42,
    "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": { "id": 42, "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 name and it inherits the template's name; omit the scheduling fields and they stay empty. The new scheduled track still snapshots the template's items.
  • capacity is 0 for unlimited. When set, is_capacity_full flips to true once assignments reach it.
  • location_id is the location's numeric id (the same id you see in an event series' location), not a public_id.
  • 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 pp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "swapped_events": [
      {
        "old_event": "e1111111-…",
        "new_event": "e6666666-…",
        "enrollment_action": "move"
      }
    ]
  }'
  • old_event is the public_id of 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_event is the public_id of the concrete event to drop into that slot.
  • enrollment_action decides 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).

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.

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 pp_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. 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 pp_your_token_here"

When Slack isn't configured for the account, the create call returns 400 slack_not_configured.

4. Read and list scheduled tracks

# One scheduled track
curl https://acme.plusplus.app/api/v2/scheduled-tracks/f1e2d3c4-…/ \
  -H "Authorization: Bearer pp_your_token_here"

# All of them, newest first
curl "https://acme.plusplus.app/api/v2/scheduled-tracks/?ordering=-starts_at" \
  -H "Authorization: Bearer pp_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 and reading per-learner progress work exactly as they do for tracks — see Building learning paths and Assignments & completion.

Common pitfalls

  • Swapping an event that isn't in the track. old_event must reference a slot currently in this scheduled track, otherwise you get 400 event_not_in_track. List the track's items first if you're unsure which slots exist.
  • Pointing new_event at an event series. new_event must 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 swap-events action.
  • Expecting keep to enroll people in the new event. keep deliberately leaves existing attendees on the old event. Use move to bring them across.