# Origamy playbook spec v1. Reference: /playbooks/spec-format/
spec_version: 1
id: base.first-contact-follow-up
title: First-contact follow-up with call tasks
sector: base
source:
  kind: engagement
  date: 2026-10-31
  ref: eng-2026-10-church-a
  notes: >-
    The call-task half of a real visitor-to-member build, in neutral words. The task, its
    reminders, the escalation ladder and saving the outcome on the person are copied as built.
derived_from: church.first-timer-to-member
vocabulary:
  person: contact
  people: contacts
  group: team
summary: >-
  Every new contact gets a welcome message and a person from your team calls them. The call
  is a task with a due time, reminders and an escalation if nobody does it, and the outcome
  and note are saved on the contact's profile.
when_to_use:
  - New people reach you through a form (an enquiry, a sign-up, a walk-in card) and someone should call each one.
  - You want to see who was called, who couldn't be reached, and which calls were missed.
  - A manager should hear about calls that weren't made in time.

requires:
  data_in:
    - how: public_form
      what: "The Contact form this playbook creates, filled by the person or typed in by your team. Or point the follow-ups at a form you already have."
  channels:
    - channel: email
      for: The welcome email
      needs: A verified sending domain for {{ params.email_from }}, or Origamy's test sender while you try it.
    - channel: sms
      for: The welcome SMS (optional)
      needs: An SMS provider, or Origamy managed SMS after business verification (KYC).
  events:
    - onboarding.submitted
  traits:
    - lifecycle_status
    - follow_up_call
    - follow_up_note

parameters:
  - key: org_name
    ask: true
    question: What should messages call your organisation?
    label: Organisation name
    type: string
    example: Northwind Learning
  - key: caller_team
    ask: true
    question: Which team calls new contacts? (you add the people later)
    label: Caller team
    type: team_name
    default: Follow-up team
  - key: escalation_team
    ask: true
    question: Who should hear if a call isn't made in time?
    label: Escalation team
    type: team_name
    default: Managers
  - key: call_due_hours
    ask: true
    question: How many hours does the team have to make the call?
    label: Hours to call
    type: hours
    default: 48
  - key: send_sms
    ask: true
    question: Should new contacts also get a welcome SMS?
    label: Welcome SMS
    type: boolean
    default: false
  - key: email_from
    ask: true
    question: Which address should emails come from?
    label: Sender address
    type: email
    example: hello@yourcompany.example
  - key: default_country
    label: Default phone country on the form
    type: country_code
    default: "{{ workspace.country }}"
  - key: greeting_fallback
    label: Greeting when a first name is missing
    type: string
    default: there
  - key: welcome_delay
    label: Welcome message after the form
    type: duration
    default: 2h
  - key: call_delay
    label: Call task created after the form
    type: duration
    default: 1d
  - key: escalate_after_hours
    label: Hours after which an unmade call escalates
    type: hours
    default: 48

timings:
  - Welcome email {{ params.welcome_delay }} after the form (and SMS straight after it, if switched on).
  - Call task {{ params.call_delay }} after the form, due in {{ params.call_due_hours }} hours. Origamy reminds the team halfway to the due time and at the due time.
  - If nobody has responded after {{ params.escalate_after_hours }} hours, the task escalates to {{ params.escalation_team }}.
  - The run waits up to 7 days for an outcome, then saves "unknown".

customer_must:
  - Add the people who make calls to the {{ params.caller_team }} team, and managers to {{ params.escalation_team }} (Settings → Collaborators → Teams). A task assigned to an empty team reaches nobody.
  - Verify the sending domain for {{ params.email_from }}. For SMS, connect a provider or finish business verification.
  - Review the Contact form and publish it, or point the follow-ups at a form you already have.
  - Switch on the two follow-ups (a follow-up only runs once it's on), send one test contact through with your own phone and email, check the welcome and the task, then erase the test contact.

objects:
  - ref: team_callers
    kind: team
    about: Gets the call tasks.
    body: { name: "{{ params.caller_team }}" }
  - ref: team_escalation
    kind: team
    about: Gets calls that weren't made in time.
    body: { name: "{{ params.escalation_team }}" }

  - ref: form_contact
    kind: form
    about: The first-contact form. A hidden field marks the person as a new contact.
    body:
      name: Contact form
      slug: contact
      description: New contacts fill this in, or your team types it in for them.
      draft_state:
        version: 1
        meta: { layout: classic, submit_label: Send, success_message: "Thank you! Someone from {{ params.org_name }} will be in touch soon." }
        pages:
          - id: p1
            title: Get in touch
            description: Tell us how to reach 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_last, key: last_name, type: short_text, label: Last name, required: true, maps_to: { mode: custom, key: last_name, trait: true } }
              - { id: f_phone, key: phone, type: phone, label: Phone number, required: true, maps_to: { mode: mapped, slot: profile.phone }, config: { country_picker: true, default_country: "{{ params.default_country }}", verify: "off" } }
              - { id: f_email, key: email, type: email, label: Email address, required: false, maps_to: { mode: mapped, slot: profile.email } }
              - { id: f_interest, key: interest, type: long_text, label: What can we help you with?, required: false, maps_to: { mode: custom, key: interest } }
              - { id: f_stage, key: stage, type: hidden, label: "", required: false, default: new_contact, maps_to: { mode: custom, key: lifecycle_status, trait: true } }
      verification_settings: { required: false }

  - ref: seg_new_contacts
    kind: segment
    about: Everyone marked as a new contact.
    body:
      name: New contacts
      description: Stage is new_contact (set by the Contact form).
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: lifecycle_status, cond_operator: eq, value: new_contact }
  - ref: seg_not_reached
    kind: segment
    about: Contacts whose last call didn't reach them, for a second try.
    body:
      name: Contacts we couldn't reach
      description: The last follow-up call was marked Could not reach.
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: follow_up_call, cond_operator: eq, value: not_reached }

  - ref: tmpl_welcome_email
    kind: template
    about: Welcome email to a new contact.
    body:
      name: New contact welcome (email)
      channel: email
      category: Onboarding
      style: html
      status: active
      subject: "Thanks for getting in touch, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}"
      metadata: { email: { from: "{{ params.email_from }}", preview_text: "" } }
      variables: {}
      body: >-
        <p>Hi {{ traits.first_name | default: "{{ params.greeting_fallback }}" }},</p>
        <p>Thanks for getting in touch with {{ params.org_name }}. Someone from our team will call
        you soon. If it's easier, just reply to this email with a good time to talk.</p>
        <p>{{ params.org_name }}</p>
        <p style="font-size:12px;color:#6b6b80"><a href="{{ unsubscribe_url }}">Unsubscribe</a></p>
  - ref: tmpl_welcome_sms
    kind: template
    when: { param: send_sms, equals: true }
    about: Welcome SMS to a new contact.
    body:
      name: New contact welcome (SMS)
      channel: sms
      category: Onboarding
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "Hi {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}, thanks for contacting {{ params.org_name }}. We'll call you soon. Reply STOP to opt out."

  - ref: wf_welcome
    kind: workflow
    about: Welcome email (and SMS) after the form.
    body:
      name: "New contact: welcome"
      description: When the Contact form is submitted, a welcome email, and an SMS if switched on.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: onboarding.submitted, form_id: "{{ ref.form_contact.id }}" }
      steps:
        - { id: s1, type: delay, config: { duration: "{{ params.welcome_delay }}" } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: email, template_id: "{{ ref.tmpl_welcome_email.id }}" } } } }
        - { id: s3, type: action, when: { param: send_sms, equals: true }, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_welcome_sms.id }}" } } } }
      re_entry_policy: once
      re_entry_cooldown_hours: 24
      goal: {}
      exit_conditions: []
  - ref: wf_call
    kind: workflow
    about: Call task with reminders and escalation; saves the outcome and note on the contact.
    body:
      name: "New contact: follow-up call"
      description: A call task for the caller team. The outcome and note are saved on the contact.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: onboarding.submitted, form_id: "{{ ref.form_contact.id }}" }
      steps:
        - { id: s1, type: delay, config: { duration: "{{ params.call_delay }}" } }
        - id: s2
          type: human_task
          config:
            human_task:
              title: "Call {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                Please call {{ traits.first_name }} {{ traits.last_name }} on {{ traits.phone }}.
                They wrote: {{ properties.interest | default: "nothing" }}

                When done, choose Called or Could not reach and add a short note on how it went.
              assignee_refs: [{ type: team, id: "{{ ref.team_callers.id }}" }]
              strategy: all
              completion_policy: first_response_wins
              outcome_options:
                - { id: called, label: Called, intent: positive }
                - { id: not_reached, label: Could not reach, intent: negative }
              due_hours: "{{ params.call_due_hours }}"
              wait: true
              wait_timeout_hours: 168
              notify: immediate
              escalation_ladder:
                - { after_hours: "{{ params.escalate_after_hours }}", to: [{ type: team, id: "{{ ref.team_escalation.id }}" }] }
        - { id: s3, type: action, config: { action_type: set_trait, traits: { follow_up_call: "{{ properties.task_outcome | default: \"unknown\" }}", follow_up_note: "{{ properties.task_note }}" } } }
      re_entry_policy: once
      re_entry_cooldown_hours: 24
      goal: {}
      exit_conditions: []

eval:
  name: 59_base_first_contact_call_tasks
  mode: task
  vertical: ""
  prompt: >-
    When someone fills in our contact form, send them a welcome email a couple of hours later
    and give our sales team a task to call them within 2 days. They should mark whether they
    reached the person and leave a note. If the call isn't done in 2 days, escalate it to the
    sales managers.
  expect_artifacts: [workflow, message_template]
  forbid_terms: []
