# Origamy playbook spec v1. Reference: /playbooks/spec-format/
spec_version: 1
id: base.win-back
title: Win-back
sector: base
source:
  kind: synthesized
  notes: >-
    No customer has built a win-back with us yet. Written from the base patterns: an
    "active before, quiet since" segment on two event conditions, a short message sequence,
    a call task for the most valuable people, and an exit the moment they come back.
derived_from: null
vocabulary:
  person: person
  people: people
  group: team
summary: >-
  Reaches people who used to be active and have gone quiet: a "we miss you" email, a second
  message a few days later, and an optional call from your team. Anyone who comes back leaves
  the sequence straight away.
when_to_use:
  - You can tell someone is active from one event (an order, a login, a lesson, a visit).
  - You want to reach people before they're gone for good, without messaging active people.

requires:
  data_in:
    - how: app_events
      what: "Your store or app sends the activity event."
    - how: csv_import
      what: "Or import your orders or visits as events from a CSV or a synced sheet, one row per visit."
  channels:
    - channel: email
      for: The two win-back emails
      needs: A verified sending domain for {{ params.email_from }}.
  events:
    - "{{ params.active_event }}"
  traits: []

parameters:
  - key: org_name
    ask: true
    question: What should messages call your organisation?
    label: Organisation name
    type: string
    example: Northwind
  - key: active_event
    ask: true
    question: Which event means someone is active? (order, login, visit…)
    label: Activity event
    type: event_name
    default: Order Completed
  - key: quiet_days
    ask: true
    question: After how long without it is someone "gone quiet"?
    label: Quiet after
    type: duration
    default: 60d
  - key: come_back_url
    ask: true
    question: Where should the "come back" button go?
    label: Come-back link
    type: url
    example: https://yourcompany.example
  - key: call_task
    ask: true
    question: Should your team also call people who don't respond?
    label: Call task
    type: boolean
    default: false
  - key: email_from
    label: Sender address
    type: email
    default: "{{ workspace.default_sender }}"
  - key: active_window
    label: Counts as "used to be active" within
    type: duration
    default: 365d
  - key: caller_team
    label: Team that calls
    type: team_name
    default: Customer success
  - key: second_message_delay
    label: Second email after the first
    type: duration
    default: 5d
  - key: greeting_fallback
    label: Greeting when a first name is missing
    type: string
    default: there

timings:
  - People enter the sequence when they've had no {{ params.active_event }} for {{ params.quiet_days }}.
  - First email straight away, second email {{ params.second_message_delay }} later, then (if switched on) a call task due in 3 days.
  - Re-entry at most once every 90 days.

customer_must:
  - Send {{ params.active_event }} from your app or store each time someone is active.
  - Verify the sending domain.
  - If you switch the call task on, add people to the {{ params.caller_team }} team.
  - "Note: a quiet-since rule only counts from when the segment is saved, so the first people enter after {{ params.quiet_days }}."
  - Check the segment's size before switching the follow-up on.

objects:
  - ref: team_callers
    kind: team
    when: { param: call_task, equals: true }
    about: Calls people who didn't respond.
    body: { name: "{{ params.caller_team }}" }
  - ref: seg_lapsed
    kind: segment
    about: Active within the last year, nothing in the quiet window.
    body:
      name: Gone quiet
      description: "{{ params.active_event }} in the last {{ params.active_window }}, but not in the last {{ params.quiet_days }}."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: event, event_name: "{{ params.active_event }}", cond_operator: has_done, time_window: "{{ params.active_window }}" }
          - { type: event, event_name: "{{ params.active_event }}", cond_operator: has_not_done, time_window: "{{ params.quiet_days }}" }
  - ref: tmpl_miss_you
    kind: template
    about: First win-back email.
    body:
      name: We miss you (email)
      channel: email
      category: Win-back
      style: html
      status: active
      subject: "We miss you, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}"
      metadata: { email: { from: "{{ params.email_from }}", preview_text: "It's been a while." } }
      variables: {}
      body: >-
        <p>Hi {{ traits.first_name | default: "{{ params.greeting_fallback }}" }},</p>
        <p>It's been a while since we've seen you at {{ params.org_name }}, and we wanted to say
        hello. Here's what's new since your last visit.</p>
        <p><a href="{{ params.come_back_url }}">Take a look</a></p>
        <p>If something didn't work for you, reply and tell us. We read every answer.</p>
        <p style="font-size:12px;color:#6b6b80"><a href="{{ unsubscribe_url }}">Unsubscribe</a></p>
  - ref: tmpl_last_call
    kind: template
    about: Second win-back email.
    body:
      name: Still there? (email)
      channel: email
      category: Win-back
      style: html
      status: active
      subject: "Should we keep in touch, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}?"
      metadata: { email: { from: "{{ params.email_from }}", preview_text: "One click to come back." } }
      variables: {}
      body: >-
        <p>Hi {{ traits.first_name | default: "{{ params.greeting_fallback }}" }},</p>
        <p>We'd love to have you back at {{ params.org_name }}. If now isn't the time, no problem:
        you can change what we send you below.</p>
        <p><a href="{{ params.come_back_url }}">Come back</a></p>
        <p style="font-size:12px;color:#6b6b80"><a href="{{ unsubscribe_url }}">Unsubscribe</a></p>
  - ref: wf_winback
    kind: workflow
    about: Two emails and an optional call; leaves as soon as they're active again.
    body:
      name: Win-back
      description: For people who've gone quiet; exits when they're active again.
      enabled: false
      trigger_type: segment_enter
      trigger_config: { segment_id: "{{ ref.seg_lapsed.id }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: email, template_id: "{{ ref.tmpl_miss_you.id }}" } } } }
        - { id: s2, type: delay, config: { duration: "{{ params.second_message_delay }}" } }
        - { id: s3, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: email, template_id: "{{ ref.tmpl_last_call.id }}" } } } }
        - id: s4
          when: { param: call_task, equals: true }
          type: human_task
          config:
            human_task:
              title: "Win-back call: {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                {{ traits.first_name }} {{ traits.last_name }} ({{ traits.phone }}) used to be active and hasn't been back, even after two emails.
                Call to ask how things are and whether anything put them off. Note what they say.
              assignee_refs: [{ type: team, id: "{{ ref.team_callers.id }}" }]
              strategy: round_robin
              completion_policy: first_response_wins
              outcome_options:
                - { id: coming_back, label: Coming back, intent: positive }
                - { id: not_interested, label: Not interested, intent: neutral }
                - { id: not_reached, label: Could not reach, intent: negative }
              due_hours: 72
              wait: true
              wait_timeout_hours: 168
              notify: immediate
        - { id: s5, when: { param: call_task, equals: true }, type: action, config: { action_type: set_trait, traits: { winback_call: "{{ properties.task_outcome | default: \"unknown\" }}", winback_note: "{{ properties.task_note }}" } } }
      re_entry_policy: cooldown
      re_entry_cooldown_hours: 2160
      goal: { event_name: "{{ params.active_event }}", attribution_window_hours: 720 }
      exit_conditions: [{ event_name: "{{ params.active_event }}" }]

eval:
  name: 64_base_win_back
  mode: task
  vertical: ""
  prompt: >-
    Win back customers who used to order from us but haven't in 60 days. Send a we-miss-you
    email, then another one five days later, and stop as soon as they order again.
  expect_artifacts: [segment, workflow, message_template]
  forbid_terms: []
