# Origamy playbook spec v1. Reference: /playbooks/spec-format/
spec_version: 1
id: lending.repayment-reminders
title: Repayment reminders
sector: lending
source:
  kind: synthesized
  notes: >-
    No lender has built this with us yet. Written from the base payment reminder, in lending
    words: a respectful reminder before and on the due date, and a collections call (with an
    escalation) once a repayment is late. Tone rules follow fair-collection practice.
derived_from: base.payment-reminder
vocabulary:
  person: borrower
  people: borrowers
  group: collections team
summary: >-
  Reminds borrowers three days before a repayment, on the day, and the day after a missed
  one, then gives your collections team a call task that escalates to a supervisor if nobody
  calls. Reminders stop when the repayment is recorded.
when_to_use:
  - Borrowers repay on a schedule your loan system knows.
  - Your system can keep the next repayment date and whether it's paid on each profile, and send an event when a repayment is received.

requires:
  data_in:
    - how: sheet_sync
      what: "Your loan book in Google Sheets or Excel, synced every hour as People: phone, next_repayment_due, repayment_status."
    - how: app_events
      what: "Or your loan system updates the traits and sends the repayment event."
  channels:
    - channel: sms
      for: All three reminders
      needs: An SMS provider, or Origamy managed SMS after business verification (KYC).
    - channel: email
      for: The early reminder
      needs: A verified sending domain for {{ params.email_from }}.
  events:
    - "{{ params.repaid_event }}"
  traits:
    - "{{ params.due_trait }}"
    - repayment_status

parameters:
  - key: org_name
    ask: true
    question: What should messages call your company?
    label: Company name
    type: string
    example: Northwind Credit
  - key: due_trait
    ask: true
    question: Which trait holds the next repayment date?
    label: Due-date trait
    type: trait_key
    default: next_repayment_due
  - key: repaid_event
    ask: true
    question: Which event means a repayment was received?
    label: Repayment event
    type: event_name
    default: Repayment Received
  - key: collections_team
    ask: true
    question: Which team calls when a repayment is late?
    label: Collections team
    type: team_name
    default: Collections
  - key: supervisor_team
    ask: true
    question: Who should hear if a late-repayment call isn't made in time?
    label: Supervisors
    type: team_name
    default: Collections supervisors
  - key: pay_url
    ask: true
    question: Where do borrowers repay? (your app or a payment page)
    label: Repayment link
    type: url
    example: https://app.yourcompany.example/repay
  - key: email_from
    label: Sender address
    type: email
    default: "{{ workspace.default_sender }}"
  - key: send_time
    label: Daily send time
    type: time
    default: "09:00"
  - key: timezone
    label: Time zone for the daily run
    type: timezone
    default: "{{ workspace.timezone }}"

timings:
  - Every day at {{ params.send_time }} ({{ params.timezone }}).
  - Email and SMS 3 days before, SMS on the due day, SMS the day after a missed repayment, then a call task due in 24 hours that escalates to {{ params.supervisor_team }} after 24.
  - The missed-repayment run stops when {{ params.repaid_event }} arrives.

customer_must:
  - "Fill repayment_status for everyone (paid or due). A blank status never matches, so that person gets no reminder."
  - "Keep {{ params.due_trait }} (ISO date) and repayment_status (paid or due) current on each borrower's profile, and send {{ params.repaid_event }} when a repayment lands."
  - Add people to the {{ params.collections_team }} and {{ params.supervisor_team }} teams.
  - Review the wording against your regulator's rules for collections. Reminders stay factual; never threaten, shame or contact third parties.
  - Connect an SMS provider or finish business verification, then switch the follow-ups on and test with your own number.

objects:
  - ref: team_collections
    kind: team
    about: Calls borrowers with a late repayment.
    body: { name: "{{ params.collections_team }}" }
  - ref: team_supervisors
    kind: team
    about: Hears about late-repayment calls not made in time.
    body: { name: "{{ params.supervisor_team }}" }
  - ref: seg_due_3d
    kind: segment
    about: Repayment due within 3 days, unpaid.
    body:
      name: Repayment due in 3 days
      description: "{{ params.due_trait }} within 3 days, repayment_status not paid."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: "{{ params.due_trait }}", cond_operator: within_next, value: 3d }
          - { type: trait, trait_key: repayment_status, cond_operator: neq, value: paid }
  - ref: seg_due_today
    kind: segment
    about: Repayment due today, unpaid.
    body:
      name: Repayment due today
      description: "{{ params.due_trait }} today, not paid."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: "{{ params.due_trait }}", cond_operator: within_last, value: 1d }
          - { type: trait, trait_key: repayment_status, cond_operator: neq, value: paid }
  - ref: seg_missed
    kind: segment
    about: Repayment missed in the last 30 days.
    body:
      name: Repayment missed
      description: "{{ params.due_trait }} in the last 30 days, not paid."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: "{{ params.due_trait }}", cond_operator: older_than, value: 1d }
          - { type: trait, trait_key: "{{ params.due_trait }}", cond_operator: within_last, value: 30d }
          - { type: trait, trait_key: repayment_status, cond_operator: neq, value: paid }
  - ref: tmpl_soon_sms
    kind: template
    about: SMS 3 days before.
    body:
      name: Repayment due soon (SMS)
      channel: sms
      category: Repayments
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: a friendly reminder that your next repayment is due in 3 days. Pay in the app or at {{ params.pay_url }} Reply STOP to opt out."
  - ref: tmpl_soon_email
    kind: template
    about: Email 3 days before.
    body:
      name: Repayment due soon (email)
      channel: email
      category: Repayments
      style: html
      status: active
      subject: Your repayment is due in 3 days
      metadata: { email: { from: "{{ params.email_from }}", preview_text: "" } }
      variables: {}
      body: >-
        <p>Hi {{ traits.first_name | default: "there" }},</p>
        <p>This is a reminder that your next repayment to {{ params.org_name }} is due in 3 days.
        Paying on time keeps your account in good standing.</p>
        <p><a href="{{ params.pay_url }}">Make a repayment</a></p>
        <p>Having trouble? Reply to this email and we'll talk through your options.</p>
        <p style="font-size:12px;color:#6b6b80"><a href="{{ unsubscribe_url }}">Unsubscribe</a></p>
  - ref: tmpl_today_sms
    kind: template
    about: SMS on the due day.
    body:
      name: Repayment due today (SMS)
      channel: sms
      category: Repayments
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: your repayment is due today. Pay in the app or at {{ params.pay_url }} If you've paid, thank you. Reply STOP to opt out."
  - ref: tmpl_missed_sms
    kind: template
    about: SMS after a missed repayment.
    body:
      name: Repayment missed (SMS)
      channel: sms
      category: Repayments
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: we haven't received your repayment yet. If something's wrong, reply or call us and we'll help. Pay: {{ params.pay_url }} Reply STOP to opt out."
  - ref: wf_soon
    kind: workflow
    about: Daily run over "due in 3 days".
    body:
      name: "Repayment 1: due in 3 days"
      description: Every day, an email and SMS to borrowers due in 3 days.
      enabled: false
      trigger_type: schedule
      trigger_config: { segment_id: "{{ ref.seg_due_3d.id }}", rrule: FREQ=DAILY, starts_at: "{{ workspace.tomorrow }}T{{ params.send_time }}", timezone: "{{ params.timezone }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: email, template_id: "{{ ref.tmpl_soon_email.id }}" } } } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_soon_sms.id }}" } } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 96
      goal: {}
      exit_conditions: []
  - ref: wf_today
    kind: workflow
    about: Daily run over "due today".
    body:
      name: "Repayment 2: due today"
      description: Every day, an SMS to borrowers due today.
      enabled: false
      trigger_type: schedule
      trigger_config: { segment_id: "{{ ref.seg_due_today.id }}", rrule: FREQ=DAILY, starts_at: "{{ workspace.tomorrow }}T{{ params.send_time }}", timezone: "{{ params.timezone }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_today_sms.id }}" } } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 48
      goal: {}
      exit_conditions: []
  - ref: wf_missed
    kind: workflow
    about: 'Daily run over "missed": an SMS, then a collections call with escalation. Exits on repayment.'
    body:
      name: "Repayment 3: missed"
      description: An SMS, then a collections call with escalation; stops when the repayment arrives.
      enabled: false
      trigger_type: schedule
      trigger_config: { segment_id: "{{ ref.seg_missed.id }}", rrule: FREQ=DAILY, starts_at: "{{ workspace.tomorrow }}T{{ params.send_time }}", timezone: "{{ params.timezone }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_missed_sms.id }}" } } } }
        - { id: s2, type: delay, config: { duration: 1d } }
        - id: s3
          type: human_task
          config:
            human_task:
              title: "Missed repayment: {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                {{ traits.first_name }} {{ traits.last_name }} ({{ traits.phone }}) missed a repayment.
                Call the borrower only. Be respectful, find out what happened, and agree a date or refer them for a plan. Note what was agreed.
              assignee_refs: [{ type: team, id: "{{ ref.team_collections.id }}" }]
              strategy: round_robin
              completion_policy: first_response_wins
              outcome_options:
                - { id: promised, label: Promised a date, intent: positive }
                - { id: plan, label: Needs a plan, intent: neutral }
                - { id: not_reached, label: Could not reach, intent: negative }
              due_hours: 24
              wait: true
              wait_timeout_hours: 168
              notify: immediate
              escalation_ladder:
                - { after_hours: 24, to: [{ type: team, id: "{{ ref.team_supervisors.id }}" }] }
        - { id: s4, type: action, config: { action_type: set_trait, traits: { collections_call: "{{ properties.task_outcome | default: \"unknown\" }}", collections_note: "{{ properties.task_note }}" } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 168
      goal: { event_name: "{{ params.repaid_event }}", attribution_window_hours: 720 }
      exit_conditions: [{ event_name: "{{ params.repaid_event }}" }]

eval:
  name: 73_lending_repayment_reminders
  mode: task
  vertical: ""
  prompt: >-
    Remind borrowers three days before each repayment and on the due date by SMS. If they miss
    it, text them the next day and have collections call; if collections doesn't call within a
    day, escalate to a supervisor. Stop when the repayment comes in.
  expect_artifacts: [segment, workflow, message_template]
  forbid_terms: []
