# Origamy playbook spec v1. Reference: /playbooks/spec-format/
spec_version: 1
id: fintech.activation
title: First transaction activation
sector: fintech
source:
  kind: synthesized
  notes: >-
    No fintech has built this with us yet. Written from the KYC nudge and the trial-to-paid
    playbook: a few days of helpful tips after verification, a longer how-to email, and a call
    only for the customers worth the time, all stopping the moment the first transaction
    happens. The messages follow common fintech safety practice: they only point to the app and
    never ask for a PIN, one-time code, password or card number.
derived_from: lending.kyc-nudge
vocabulary:
  person: customer
  people: customers
  group: team
summary: >-
  Customers who passed verification but haven't made a first transaction get a tip the next
  day, a how-to email three days after that, and, if they are high-value, a call from your
  support team. It all stops as soon as they transact. A weekly report shows how many signed
  up, verified and transacted.
when_to_use:
  - You run a wallet, neobank, savings or payments app, and many customers verify but never fund the account or send money.
  - Your app sends events when someone signs up, passes verification and makes a first transaction.
  - A support or onboarding team can spend time on your most valuable new customers.

requires:
  data_in:
    - how: app_events
      what: "Your app sends the sign-up, verification-approved and first-transaction events."
    - how: csv_import
      what: "No developer yet? A daily CSV of those three events can be uploaded as Events. Reminders then start from the day the file is uploaded, so they arrive later than with live events."
  channels:
    - channel: sms
      for: The first tip
      needs: An SMS provider, or Origamy managed SMS after business verification (KYC).
    - channel: push
      for: The first tip, instead of SMS (optional)
      needs: Your app set up for push notifications.
    - channel: email
      for: The how-to email
      needs: A verified sending domain for {{ params.email_from }}.
  events:
    - "{{ params.signup_event }}"
    - "{{ params.kyc_event }}"
    - "{{ params.first_transaction_event }}"
  traits:
    - "{{ params.tier_trait }}"
    - activation_call

parameters:
  - key: org_name
    ask: true
    question: What should messages call your company?
    label: Company name
    type: string
    example: Northwind Pay
  - key: app_url
    ask: true
    question: Where should messages send people? (your app or a deep link)
    label: App link
    type: url
    example: https://app.yourcompany.example
  - key: support_team
    ask: true
    question: Which team calls high-value customers who haven't transacted?
    label: Support team
    type: team_name
    default: Customer support
  - key: high_value_tier
    ask: true
    question: Which value of your customer tier marks the high-value customers who should get a call?
    help: Your app sets a tier on each customer (see the tier trait below). Only this tier gets a call.
    label: High-value tier
    type: string
    default: premium
  - key: first_transaction_event
    ask: true
    question: Which event means a customer made their first transaction?
    label: First transaction event
    type: event_name
    default: First Transaction
  - key: signup_event
    label: Sign-up event
    type: event_name
    default: Signed Up
  - key: kyc_event
    label: Verification approved event
    type: event_name
    default: KYC Approved
  - key: tier_trait
    label: Trait that holds the customer tier
    type: trait_key
    default: tier
  - key: use_push
    label: Send the first tip as a push notification instead of SMS
    type: boolean
    default: false
  - key: call_due_hours
    label: Hours the support team has to make the call
    type: hours
    default: 48
  - key: email_from
    label: Sender address
    type: email
    default: "{{ workspace.default_sender }}"
  - key: timezone
    label: Time zone for the report
    type: timezone
    default: "{{ workspace.timezone }}"
  - key: report_cron
    label: Weekly activation report
    type: cron
    default: "0 8 * * 1"
  - key: greeting_fallback
    label: Greeting when a first name is missing
    type: string
    default: there

timings:
  - "Day 1 after verification: a tip by SMS (or push), if they haven't transacted."
  - "Day 3: a how-to email, if they still haven't."
  - "Day 7: a call task for {{ params.support_team }}, due in {{ params.call_due_hours }} hours, only for customers whose tier is {{ params.high_value_tier }}."
  - Everything stops as soon as {{ params.first_transaction_event }} arrives.
  - "Weekly activation report: {{ params.report_cron }} in {{ params.timezone }}."

customer_must:
  - "Send {{ params.signup_event }}, {{ params.kyc_event }} and {{ params.first_transaction_event }} from your backend, with the customer's phone number and email as traits."
  - "Set the {{ params.tier_trait }} trait on each customer with an identify call (for example standard or {{ params.high_value_tier }}). Without it no call tasks are created."
  - "Never ask for a PIN, one-time code, password or card number in a message or on a call. None of these messages does, and each says so. Tell your support team the same, and publish on your site and in the app that you never ask for them, so customers can spot fakes."
  - Add people to the {{ params.support_team }} team.
  - If you use push, send the app's push token to Origamy through your SDK. Otherwise connect an SMS provider or finish business verification.
  - Verify the sending domain, switch the follow-up and report on, and test with your own account.

objects:
  - ref: team_support
    kind: team
    about: Calls high-value customers who haven't transacted; gets the weekly report.
    body: { name: "{{ params.support_team }}" }

  - ref: seg_waiting
    kind: segment
    about: Verified in the last 14 days, no first transaction yet.
    body:
      name: Verified, no first transaction
      description: "{{ params.kyc_event }} in the last 14 days and no {{ params.first_transaction_event }}."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: event, event_name: "{{ params.kyc_event }}", cond_operator: has_done, time_window: 14d }
          - { type: event, event_name: "{{ params.first_transaction_event }}", cond_operator: has_not_done, time_window: 14d }
  - ref: seg_activated
    kind: segment
    about: Verified in the last 14 days and made a first transaction.
    body:
      name: Verified and transacted
      description: "{{ params.kyc_event }} and {{ params.first_transaction_event }} in the last 14 days."
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: event, event_name: "{{ params.kyc_event }}", cond_operator: has_done, time_window: 14d }
          - { type: event, event_name: "{{ params.first_transaction_event }}", cond_operator: has_done, time_window: 14d }

  - ref: tmpl_tip_sms
    kind: template
    about: First tip by SMS. No amounts.
    when: { param: use_push, equals: false }
    body:
      name: Make your first transaction (SMS)
      channel: sms
      category: Activation
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: you're all set, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}. Add money or make your first transfer in the app: {{ params.app_url }} We will never ask for your PIN or one-time codes. Reply STOP to opt out."
  - ref: tmpl_tip_push
    kind: template
    about: First tip as a push notification.
    when: { param: use_push, equals: true }
    body:
      name: Make your first transaction (push)
      channel: push
      category: Activation
      style: text
      status: active
      subject: "You're all set on {{ params.org_name }}"
      metadata: { push: { url: "{{ params.app_url }}" } }
      variables: {}
      body: "Add money or make your first transfer in a minute. We will never ask for your PIN or one-time codes."
  - ref: tmpl_howto
    kind: template
    about: How-to email three days later.
    body:
      name: Getting started (email)
      channel: email
      category: Activation
      style: html
      status: active
      subject: "Three easy ways to start with {{ params.org_name }}"
      metadata: { email: { from: "{{ params.email_from }}", preview_text: "It takes about a minute." } }
      variables: {}
      body: >-
        <p>Hi {{ traits.first_name | default: "{{ params.greeting_fallback }}" }},</p>
        <p>Your {{ params.org_name }} account is verified and ready. Most people start in one of
        three ways, and each takes about a minute in the app:</p>
        <ul>
        <li>Add money to your account</li>
        <li>Send your first transfer</li>
        <li>Pay a bill or buy airtime</li>
        </ul>
        <p><a href="{{ params.app_url }}">Open the app</a></p>
        <p>Stuck? Reply to this email and a real person will help.</p>
        <p>We will never ask for your PIN, password, card number or one-time codes by email, SMS or phone.</p>
        <p style="font-size:12px;color:#6b6b80"><a href="{{ unsubscribe_url }}">Unsubscribe</a></p>

  - ref: q_signups
    kind: saved_question
    about: Sign-ups in the report's period.
    body:
      name: "Report: sign-ups"
      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: "{{ params.signup_event }}" }] }
        aggregations: [{ fn: distinct, field: canonical_user_id, alias: people }]
  - ref: q_verified
    kind: saved_question
    about: Verifications approved in the report's period.
    body:
      name: "Report: verified"
      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: "{{ params.kyc_event }}" }] }
        aggregations: [{ fn: distinct, field: canonical_user_id, alias: people }]
  - ref: q_transacted
    kind: saved_question
    about: First transactions in the report's period.
    body:
      name: "Report: first transactions"
      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: "{{ params.first_transaction_event }}" }] }
        aggregations: [{ fn: distinct, field: canonical_user_id, alias: people }]
  - ref: funnel_activation
    kind: saved_funnel
    about: Sign-up → verified → first transaction, within 30 days.
    body:
      name: Activation funnel
      data_plane_id: default
      visibility: workspace
      definition:
        range: 90d
        window: 2592000
        view: steps
        breakdown: ""
        interval: ""
        compare: false
        steps:
          - { type: track, name: "{{ params.signup_event }}", filters: [] }
          - { type: track, name: "{{ params.kyc_event }}", filters: [] }
          - { type: track, name: "{{ params.first_transaction_event }}", filters: [] }
  - ref: dash_activation
    kind: dashboard
    about: The activation funnel and this period's numbers.
    body:
      name: Activation
      description: Sign-ups, verifications and first transactions.
      visibility: workspace
      cards:
        - { saved_funnel_id: "{{ ref.funnel_activation.id }}", x: 0, y: 0, w: 12, h: 6 }
        - { saved_question_id: "{{ ref.q_signups.id }}", viz_type: number, x: 0, y: 6, w: 4, h: 3 }
        - { saved_question_id: "{{ ref.q_verified.id }}", viz_type: number, x: 4, y: 6, w: 4, h: 3 }
        - { saved_question_id: "{{ ref.q_transacted.id }}", viz_type: number, x: 8, y: 6, w: 4, h: 3 }
  - ref: report_weekly
    kind: report
    about: The week's sign-ups, verifications and first transactions, and who is still waiting, sent to the support team.
    body:
      name: "Weekly: activation"
      description: Last week's sign-ups, verifications and first transactions, compared with the week before.
      schedule_cron: "{{ params.report_cron }}"
      timezone: "{{ params.timezone }}"
      enabled: false
      spec:
        period: last_7_days
        compare: previous_period
        week_start: monday
        sections:
          - { id: signups, kind: saved_question, title: Sign-ups, viz: number, params: { question_id: "{{ ref.q_signups.id }}" } }
          - { id: verified, kind: saved_question, title: Verified, viz: number, params: { question_id: "{{ ref.q_verified.id }}" } }
          - { id: transacted, kind: saved_question, title: Made a first transaction, viz: number, params: { question_id: "{{ ref.q_transacted.id }}" } }
          - id: waiting
            kind: segment_sizes
            title: Verified in the last 14 days
            params: { segment_ids: ["{{ ref.seg_waiting.id }}", "{{ ref.seg_activated.id }}"] }
          - { id: tasks, kind: tasks_summary, title: Activation calls, params: {} }
          - { id: link, kind: text, params: { body: "Funnel: {{ ref.dash_activation.url }}" } }
      recipients: [{ type: team, id: "{{ ref.team_support.id }}" }]
      channel_policy: { mode: email_only }

  - ref: wf_activation
    kind: workflow
    about: Tip, how-to email, then a call for high-value customers; each step skipped once they transact.
    body:
      name: First transaction activation
      description: Helps newly verified customers make a first transaction; stops on the first transaction.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: "{{ params.kyc_event }}" }
      steps:
        - { id: s1, type: delay, config: { duration: 1d } }
        - { id: s2, type: filter, config: { condition: { type: segment, cond_operator: member_of, value: "{{ ref.seg_waiting.id }}" } } }
        - { id: s3, when: { param: use_push, equals: false }, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_tip_sms.id }}" } } } }
        - { id: s4, when: { param: use_push, equals: true }, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: push, template_id: "{{ ref.tmpl_tip_push.id }}" } } } }
        - { id: s5, type: delay, config: { duration: 2d } }
        - { id: s6, type: filter, config: { condition: { type: segment, cond_operator: member_of, value: "{{ ref.seg_waiting.id }}" } } }
        - { id: s7, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: email, template_id: "{{ ref.tmpl_howto.id }}" } } } }
        - { id: s8, type: delay, config: { duration: 4d } }
        - { id: s9, type: filter, config: { condition: { type: segment, cond_operator: member_of, value: "{{ ref.seg_waiting.id }}" } } }
        - { id: s10, type: filter, config: { condition: { type: trait, trait_key: "{{ params.tier_trait }}", cond_operator: eq, value: "{{ params.high_value_tier }}" } } }
        - id: s11
          type: human_task
          config:
            human_task:
              title: "Help with first transaction: {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                {{ traits.first_name }} {{ traits.last_name }} ({{ traits.phone }}) was verified a week ago and hasn't made a first transaction.

                Call to ask if they need help and walk them through it in the app. Never ask for a PIN, one-time code, password or card number, and never ask them to read out a code. Choose an outcome and add a short note.
              assignee_refs: [{ type: team, id: "{{ ref.team_support.id }}" }]
              strategy: round_robin
              completion_policy: first_response_wins
              outcome_options:
                - { id: helped, label: Helped them get started, intent: positive }
                - { id: not_interested, label: Not interested, intent: neutral }
                - { id: not_reached, label: Could not reach, intent: negative }
              due_hours: "{{ params.call_due_hours }}"
              wait: true
              wait_timeout_hours: 168
              notify: immediate
        - { id: s12, type: action, config: { action_type: set_trait, traits: { activation_call: "{{ properties.task_outcome | default: \"unknown\" }}", activation_call_note: "{{ properties.task_note }}" } } }
      re_entry_policy: once
      re_entry_cooldown_hours: 24
      goal: { event_name: "{{ params.first_transaction_event }}", attribution_window_hours: 720 }
      exit_conditions: [{ event_name: "{{ params.first_transaction_event }}" }]

eval:
  name: 105_fintech_activation
  mode: task
  vertical: ""
  prompt: >-
    We run a payments app. Lots of people sign up and pass KYC but never make a first
    transaction. The day after their KYC is approved, text them a tip to fund their account or
    make a transfer. Three days later send an email explaining how to get started. If they are
    on our premium tier and still haven't transacted after a week, give our support team a task
    to call them. Stop when they make their first transaction, and send our team a weekly
    activation report. Never ask for PINs or OTPs.
  expect_artifacts: [segment, workflow, message_template, report]
  forbid_terms: []
