# Origamy playbook spec v1. Reference: /playbooks/spec-format/
spec_version: 1
id: edtech.cohort-onboarding
title: Cohort onboarding
sector: edtech
source:
  kind: synthesized
  notes: >-
    No academy has built this with us yet. Written from the booking-reminder pattern (a booking
    form copies its slot time onto the person, an hourly run reminds them) and the volunteer
    onboarding flow (welcome, orientation, check-in). The two one-off sends use a schedule that
    runs once at a date you give, because a follow-up can wait a few days but cannot wait until
    a date. Bootcamp success teams commonly check in on learners who have not started in the
    first week.
derived_from: nonprofits.volunteer-onboarding
vocabulary:
  person: learner
  people: learners
  group: success team
summary: >-
  New learners get a welcome with the community link and a link to book an orientation session.
  Bookings are confirmed and reminded. The day before the cohort starts they get one more
  reminder, and a few days into the first week your success team gets a task to check in on
  anyone who hasn't done a lesson yet. A weekly report goes to the programme manager.
when_to_use:
  - You run a bootcamp, academy or online course that starts in cohorts, and learners drop out in the first week because nobody reached them.
  - Learners are enrolled by your team (a form) or by your app (an event).
  - You want everyone to know where the community is, when orientation is, and when classes start.

requires:
  data_in:
    - how: staff_form
      what: "The Enrolled form this playbook creates: your team fills it in when a learner pays or is admitted, with the learner's phone number. Use this if your app doesn't send an enrolment event."
    - how: app_events
      what: "Or your app sends an enrolment event for each learner, and a lesson-completed event so the first-week check-in knows who has started. Without lesson events every learner in the cohort gets a check-in task."
    - how: booking_form
      what: "The Orientation booking form this playbook creates: each booking saves its time on the learner and drives the reminders."
  channels:
    - channel: sms
      for: The welcome, orientation confirmation and reminders, and the start-date reminder
      needs: An SMS provider, or Origamy managed SMS after business verification (KYC).
    - channel: email
      for: The welcome with the community and orientation links
      needs: A verified sending domain for {{ params.email_from }}.
  events:
    - "{{ params.enrolled_event }}"
    - "{{ params.lesson_event }}"
    - onboarding.submitted
    - booking.created
    - booking.cancelled
  traits:
    - enrolment_status
    - cohort
    - orientation_at
    - week1_checkin

parameters:
  - key: org_name
    ask: true
    question: What should messages call your school or academy?
    label: School name
    type: string
    example: Northwind Academy
  - key: enrol_via
    ask: true
    question: How do learners get enrolled? Choose form if your team fills in a form, or app_events if your app sends an event.
    label: Enrolment source
    type: enum
    options: [form, app_events]
    default: form
  - key: community_url
    ask: true
    question: Where do learners join your community? (a WhatsApp, Telegram or Slack invite link)
    label: Community link
    type: url
    example: https://chat.yourschool.example/join
  - key: success_team
    ask: true
    question: Which team checks in on learners who haven't started? (one person is fine)
    label: Success team
    type: team_name
    default: Learner success
  - key: cohort_start_date
    ask: true
    question: What is the cohort's first day of class? (year-month-day)
    label: Cohort start date
    type: string
    example: "2027-01-11"
  - key: start_reminder_at
    ask: true
    question: When should the reminder go out? (the day before the first class, as year-month-dayThh:mm)
    help: For a cohort that starts on 2027-01-11, that is 2027-01-10T09:00.
    label: Start reminder time
    type: string
    example: "2027-01-10T09:00"
  - key: checkin_at
    ask: true
    question: When should the first-week check-in run? (about five days after the first class, as year-month-dayThh:mm)
    help: For a cohort that starts on 2027-01-11, that is 2027-01-16T09:00.
    label: Check-in time
    type: string
    example: "2027-01-16T09:00"
  - key: cohort_name
    label: Name of this cohort (saved on each learner)
    type: string
    default: Current cohort
  - key: enrolled_event
    label: Enrolment event your app sends
    type: event_name
    default: Enrolled
  - key: lesson_event
    label: Lesson completed event your app sends
    type: event_name
    default: Lesson Completed
  - key: office_phone
    label: Number learners call to change their orientation
    type: string
    default: "{{ workspace.phone }}"
  - key: programme_team
    label: Team that gets the weekly report
    type: team_name
    default: Programme managers
  - key: checkin_due_hours
    label: Hours the success team has to make a check-in
    type: hours
    default: 48
  - key: email_from
    label: Sender address
    type: email
    default: "{{ workspace.default_sender }}"
  - key: default_country
    label: Default phone country on the forms
    type: country_code
    default: "{{ workspace.country }}"
  - key: timezone
    label: Time zone for the schedules and report
    type: timezone
    default: "{{ workspace.timezone }}"
  - key: report_cron
    label: Weekly programme report
    type: cron
    default: "0 8 * * 1"
  - key: greeting_fallback
    label: Greeting when a first name is missing
    type: string
    default: there

timings:
  - Enrolment sends the welcome SMS and email straight away.
  - A booked orientation is confirmed by SMS at once, reminded about a day before and again about two hours before (the run is hourly).
  - "A reminder goes out at {{ params.start_reminder_at }}, the day before classes start."
  - "At {{ params.checkin_at }}, learners with no {{ params.lesson_event }} in the last 5 days become a check-in task for {{ params.success_team }}, due in {{ params.checkin_due_hours }} hours."
  - "Weekly report: {{ params.report_cron }} in {{ params.timezone }}."

customer_must:
  - "Switch the two one-off follow-ups on before their times ({{ params.start_reminder_at }} and {{ params.checkin_at }}). A one-off schedule whose time has passed can't be switched on."
  - "Install this once per cohort: the cohort name and the three dates are specific to one intake. For the next intake, run it again with new dates."
  - "If learners are enrolled from your app, send {{ params.enrolled_event }} when someone enrols and {{ params.lesson_event }} for each lesson, with the learner's phone and email as traits. If you use the Enrolled form, fill it in for each learner with the phone number they gave you."
  - Publish the Orientation booking form and set your session days and hours in the form builder (the date and time slot question).
  - Add people to {{ params.success_team }} and {{ params.programme_team }}. A task assigned to an empty team reaches nobody.
  - Connect an SMS provider or finish business verification, verify the sending domain, switch the follow-ups and report on, and test with your own phone.

objects:
  - ref: team_success
    kind: team
    about: Checks in on learners who haven't started.
    body: { name: "{{ params.success_team }}" }
  - ref: team_programme
    kind: team
    about: Gets the weekly report.
    body: { name: "{{ params.programme_team }}" }

  - ref: form_enrolled
    kind: form
    about: Staff-only form your team fills in when a learner enrols. Matched by phone; hidden fields mark the learner as enrolled in this cohort.
    when: { param: enrol_via, equals: form }
    body:
      name: Enrolled
      slug: enrolled
      description: Staff only. One entry per learner when they are enrolled in the cohort.
      draft_state:
        version: 1
        meta: { layout: classic, submit_label: Save, success_message: Saved. The learner will get a welcome message. }
        pages:
          - id: p1
            title: Enrol a learner
            description: Use the phone number the learner gave you.
            fields:
              - { id: f_phone, key: phone, type: phone, label: Learner's phone, required: true, maps_to: { mode: mapped, slot: profile.phone }, config: { country_picker: true, default_country: "{{ params.default_country }}", verify: "off" } }
              - { id: f_first, key: first_name, type: short_text, label: First name, required: true, maps_to: { mode: custom, key: first_name, trait: true } }
              - { id: f_last, key: last_name, type: short_text, label: Last name, required: false, maps_to: { mode: custom, key: last_name, trait: true } }
              - { id: f_email, key: email, type: email, label: Email address, required: false, maps_to: { mode: mapped, slot: profile.email } }
              - { id: f_status, key: enrolment_status, type: hidden, label: "", required: false, default: enrolled, maps_to: { mode: custom, key: enrolment_status, trait: true } }
              - { id: f_cohort, key: cohort, type: hidden, label: "", required: false, default: "{{ params.cohort_name }}", maps_to: { mode: custom, key: cohort, trait: true } }
      verification_settings: { required: false }
  - ref: form_orientation
    kind: form
    about: The orientation booking form. Each booking sends booking.created with the slot time.
    body:
      name: Book your orientation
      slug: book-orientation
      description: Pick a time for your orientation session.
      draft_state:
        version: 1
        meta: { layout: classic, submit_label: Book orientation, success_message: "Booked! We'll text you a confirmation." }
        pages:
          - id: p1
            title: Pick a time
            description: Choose a session that suits you.
            fields:
              - { id: f_first, key: first_name, type: short_text, label: First name, required: true, maps_to: { mode: custom, key: first_name, trait: true } }
              - { id: f_phone, key: phone, type: phone, label: Phone number you enrolled with, required: true, maps_to: { mode: mapped, slot: profile.phone }, config: { country_picker: true, default_country: "{{ params.default_country }}", verify: "off" } }
              - { id: f_slot, key: orientation_slot, type: date_slot, label: When would you like to join orientation?, required: true, maps_to: { mode: mapped, slot: booking.slot } }
      verification_settings: { required: false }

  - ref: seg_cohort
    kind: segment
    about: Learners enrolled in this cohort.
    body:
      name: Enrolled in the cohort
      description: "Enrolled, and cohort is {{ params.cohort_name }}."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: enrolment_status, cond_operator: eq, value: enrolled }
          - { type: trait, trait_key: cohort, cond_operator: eq, value: "{{ params.cohort_name }}" }
  - ref: seg_quiet
    kind: segment
    about: Enrolled in the cohort with no lesson in the last 5 days.
    body:
      name: Enrolled, no lesson in 5 days
      description: "Enrolled in the cohort and no {{ params.lesson_event }} in the last 5 days."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: enrolment_status, cond_operator: eq, value: enrolled }
          - { type: trait, trait_key: cohort, cond_operator: eq, value: "{{ params.cohort_name }}" }
          - { type: event, event_name: "{{ params.lesson_event }}", cond_operator: has_not_done, time_window: 5d }
  - ref: seg_orient_day
    kind: segment
    about: Orientation within the next day.
    body:
      name: Orientation within a day
      description: orientation_at within the next 24 hours.
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: orientation_at, cond_operator: within_next, value: 1d }
  - ref: seg_orient_hour
    kind: segment
    about: Orientation within the next two hours.
    body:
      name: Orientation within hours
      description: orientation_at within the next 2 hours.
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: orientation_at, cond_operator: within_next, value: 2h }

  - ref: tmpl_welcome_sms
    kind: template
    about: Welcome SMS with the community and orientation links.
    body:
      name: Welcome to the cohort (SMS)
      channel: sms
      category: Onboarding
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "Welcome to {{ params.org_name }}, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}! Classes start on {{ params.cohort_start_date }}. Join the community: {{ params.community_url }} Book your orientation: {{ ref.form_orientation.url }} Reply STOP to opt out."
  - ref: tmpl_welcome_email
    kind: template
    about: Welcome email with the same links and what to expect.
    body:
      name: Welcome to the cohort (email)
      channel: email
      category: Onboarding
      style: html
      status: active
      subject: "Welcome to {{ params.org_name }}, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}"
      metadata: { email: { from: "{{ params.email_from }}", preview_text: "Two things to do before class starts." } }
      variables: {}
      body: >-
        <p>Hi {{ traits.first_name | default: "{{ params.greeting_fallback }}" }},</p>
        <p>Welcome to {{ params.org_name }}! Your cohort starts on {{ params.cohort_start_date }}.
        Two things to do before then:</p>
        <ol>
        <li><a href="{{ params.community_url }}">Join the community</a>, where announcements and classmates are.</li>
        <li><a href="{{ ref.form_orientation.url }}">Book your orientation session</a>, so you know how everything works on day one.</li>
        </ol>
        <p>Questions? Just reply to this email.</p>
        <p style="font-size:12px;color:#6b6b80"><a href="{{ unsubscribe_url }}">Unsubscribe</a></p>
  - ref: tmpl_orient_confirm
    kind: template
    about: Orientation confirmation.
    body:
      name: Orientation booked (SMS)
      channel: sms
      category: Onboarding
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: your orientation is booked for {{ properties.slot_start_ts | date: \"%a %-d %b, %H:%M\" }}. Joining details will be in the community: {{ params.community_url }} To change it, call {{ params.office_phone }}. Reply STOP to opt out."
  - ref: tmpl_orient_day
    kind: template
    about: Reminder about a day before orientation.
    body:
      name: Orientation tomorrow (SMS)
      channel: sms
      category: Onboarding
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: see you at orientation on {{ traits.orientation_at | date: \"%a %-d %b, %H:%M\" }}. Joining details are in the community: {{ params.community_url }} Can't make it? Call {{ params.office_phone }}. Reply STOP to opt out."
  - ref: tmpl_orient_hour
    kind: template
    about: Short reminder a couple of hours before.
    body:
      name: Orientation soon (SMS)
      channel: sms
      category: Onboarding
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: your orientation starts at {{ traits.orientation_at | date: \"%H:%M\" }} today. Find the details in the community: {{ params.community_url }} Reply STOP to opt out."
  - ref: tmpl_start
    kind: template
    about: The day-before reminder that classes start.
    body:
      name: Classes start tomorrow (SMS)
      channel: sms
      category: Onboarding
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: classes start tomorrow, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}! Everything you need is in the community: {{ params.community_url }} We can't wait to see you. Reply STOP to opt out."

  - ref: q_enrolments
    kind: saved_question
    about: Learners enrolled in the report's period.
    body:
      name: "Report: enrolments"
      data_plane_id: default
      viz_type: number
      viz_settings: {}
      visibility: workspace
      query_ast:
        table: events
        filters:
          operator: and
          conditions:
            - { when: { param: enrol_via, equals: form }, field: event_name, op: eq, value: onboarding.submitted }
            - { when: { param: enrol_via, equals: form }, field: properties.form_id, op: eq, value: "{{ ref.form_enrolled.id }}" }
            - { when: { param: enrol_via, equals: app_events }, field: event_name, op: eq, value: "{{ params.enrolled_event }}" }
        aggregations: [{ fn: distinct, field: canonical_user_id, alias: people }]
  - ref: q_orientations
    kind: saved_question
    about: Orientation sessions booked in the report's period.
    body:
      name: "Report: orientations booked"
      data_plane_id: default
      viz_type: number
      viz_settings: {}
      visibility: workspace
      query_ast:
        table: events
        filters: { operator: and, conditions: [{ field: event_name, op: eq, value: booking.created }] }
        aggregations: [{ fn: distinct, field: canonical_user_id, alias: people }]
  - ref: report_weekly
    kind: report
    about: The week's enrolments and orientation bookings, how many learners are quiet, and the state of the check-in tasks.
    body:
      name: "Weekly: cohort onboarding"
      description: Last week's enrolments and orientation bookings, and how many learners haven't started.
      schedule_cron: "{{ params.report_cron }}"
      timezone: "{{ params.timezone }}"
      enabled: false
      spec:
        period: last_7_days
        compare: previous_period
        week_start: monday
        sections:
          - { id: enrolments, kind: saved_question, title: Learners enrolled, viz: number, params: { question_id: "{{ ref.q_enrolments.id }}" } }
          - { id: orientations, kind: saved_question, title: Orientations booked, viz: number, params: { question_id: "{{ ref.q_orientations.id }}" } }
          - id: cohort
            kind: segment_sizes
            title: In the cohort, and no lesson in 5 days
            params: { segment_ids: ["{{ ref.seg_cohort.id }}", "{{ ref.seg_quiet.id }}"] }
          - { id: tasks, kind: tasks_summary, title: First-week check-ins, params: {} }
      recipients: [{ type: team, id: "{{ ref.team_programme.id }}" }]
      channel_policy: { mode: email_only }

  - ref: wf_welcome_form
    kind: workflow
    about: When your team records an enrolment on the form, the welcome goes by SMS and email.
    when: { param: enrol_via, equals: form }
    body:
      name: "Cohort 1: welcome (form)"
      description: Welcome SMS and email when a learner is enrolled on the Enrolled form.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: onboarding.submitted, form_id: "{{ ref.form_enrolled.id }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_welcome_sms.id }}" } } } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: email, template_id: "{{ ref.tmpl_welcome_email.id }}" } } } }
      re_entry_policy: once
      re_entry_cooldown_hours: 24
      goal: {}
      exit_conditions: []
  - ref: wf_welcome_app
    kind: workflow
    about: When your app sends the enrolment event, the learner is marked as enrolled in this cohort and gets the welcome.
    when: { param: enrol_via, equals: app_events }
    body:
      name: "Cohort 1: welcome (app)"
      description: Marks the learner as enrolled in this cohort, then sends the welcome SMS and email.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: "{{ params.enrolled_event }}" }
      steps:
        - { id: s1, type: action, config: { action_type: set_trait, traits: { enrolment_status: enrolled, cohort: "{{ params.cohort_name }}" } } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_welcome_sms.id }}" } } } }
        - { id: s3, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: email, template_id: "{{ ref.tmpl_welcome_email.id }}" } } } }
      re_entry_policy: once
      re_entry_cooldown_hours: 24
      goal: {}
      exit_conditions: []
  - ref: wf_orient_booked
    kind: workflow
    about: When an orientation is booked, save its time on the learner and confirm it.
    body:
      name: "Cohort 2: orientation booked"
      description: Copies each booking's time onto the learner and sends the confirmation.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: booking.created, form_id: "{{ ref.form_orientation.id }}" }
      steps:
        - { id: s1, type: action, config: { action_type: set_trait, traits: { orientation_at: "{{ properties.slot_start_ts }}" } } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_orient_confirm.id }}" } } } }
      re_entry_policy: every_time
      re_entry_cooldown_hours: 0
      goal: {}
      exit_conditions: []
  - ref: wf_orient_cancelled
    kind: workflow
    about: A cancelled booking clears the saved time, so no reminder goes out.
    body:
      name: "Cohort 3: orientation cancelled"
      description: Clears the saved orientation time when a booking is cancelled.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: booking.cancelled, form_id: "{{ ref.form_orientation.id }}" }
      steps:
        - { id: s1, type: action, config: { action_type: set_trait, traits: { orientation_at: "" } } }
      re_entry_policy: every_time
      re_entry_cooldown_hours: 0
      goal: {}
      exit_conditions: []
  - ref: wf_orient_day
    kind: workflow
    about: Hourly run over "orientation within a day"; one reminder per orientation.
    body:
      name: "Cohort 4: orientation reminder (day before)"
      description: Every hour, a reminder to learners whose orientation is within a day.
      enabled: false
      trigger_type: schedule
      trigger_config: { segment_id: "{{ ref.seg_orient_day.id }}", rrule: FREQ=HOURLY, starts_at: "{{ workspace.tomorrow }}T07:00", timezone: "{{ params.timezone }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_orient_day.id }}" } } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 18
      goal: {}
      exit_conditions: []
  - ref: wf_orient_hour
    kind: workflow
    about: Hourly run over "orientation within hours"; one short reminder per orientation.
    body:
      name: "Cohort 5: orientation reminder (hours before)"
      description: Every hour, a short reminder to learners whose orientation is a couple of hours away.
      enabled: false
      trigger_type: schedule
      trigger_config: { segment_id: "{{ ref.seg_orient_hour.id }}", rrule: FREQ=HOURLY, starts_at: "{{ workspace.tomorrow }}T07:00", timezone: "{{ params.timezone }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_orient_hour.id }}" } } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 12
      goal: {}
      exit_conditions: []
  - ref: wf_start_reminder
    kind: workflow
    about: One run on the day before the cohort starts, over everyone enrolled in it.
    body:
      name: "Cohort 6: classes start tomorrow"
      description: A single reminder to every enrolled learner on the day before the first class.
      enabled: false
      trigger_type: schedule
      trigger_config: { segment_id: "{{ ref.seg_cohort.id }}", rrule: "FREQ=DAILY;COUNT=1", starts_at: "{{ params.start_reminder_at }}", timezone: "{{ params.timezone }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_start.id }}" } } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 720
      goal: {}
      exit_conditions: []
  - ref: wf_checkin
    kind: workflow
    about: One run in the first week; a check-in task for each learner with no lesson yet, and the outcome is saved.
    body:
      name: "Cohort 7: first-week check-in"
      description: Once, a check-in task for the success team for each learner who hasn't done a lesson.
      enabled: false
      trigger_type: schedule
      trigger_config: { segment_id: "{{ ref.seg_quiet.id }}", rrule: "FREQ=DAILY;COUNT=1", starts_at: "{{ params.checkin_at }}", timezone: "{{ params.timezone }}" }
      steps:
        - id: s1
          type: human_task
          config:
            human_task:
              title: "Check in: {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                {{ traits.first_name }} {{ traits.last_name }} ({{ traits.phone }}) enrolled in {{ params.cohort_name }} and hasn't done a lesson in the first week.

                Call or message to say hello, ask how it is going and help with whatever is in the way (access, time, payment, motivation). Choose an outcome and add a short note.
              assignee_refs: [{ type: team, id: "{{ ref.team_success.id }}" }]
              strategy: round_robin
              completion_policy: first_response_wins
              outcome_options:
                - { id: on_track, label: Started or will start, intent: positive }
                - { id: needs_help, label: Needs more help, intent: neutral }
                - { id: leaving, label: Thinking of leaving, intent: negative }
                - { id: not_reached, label: Could not reach, intent: negative }
              due_hours: "{{ params.checkin_due_hours }}"
              wait: true
              wait_timeout_hours: 168
              notify: immediate
        - { id: s2, type: action, config: { action_type: set_trait, traits: { week1_checkin: "{{ properties.task_outcome | default: \"unknown\" }}", week1_checkin_note: "{{ properties.task_note }}" } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 720
      goal: {}
      exit_conditions: []

eval:
  name: 107_edtech_cohort_onboarding
  mode: task
  vertical: ""
  prompt: >-
    We run a coding bootcamp with cohorts. When a learner is enrolled, text and email them a
    welcome with our community link and a link to book an orientation session. Confirm
    orientation bookings and remind them a day and a couple of hours before. Send everyone in
    the cohort a reminder the day before classes start on 2027-01-11, and five days in, give our
    success team a task to check on anyone who hasn't completed a lesson. Email our programme
    manager a weekly report.
  expect_artifacts: [form, segment, workflow, message_template, report]
  forbid_terms: []
