# Origamy playbook spec v1. Reference: /playbooks/spec-format/
spec_version: 1
id: lending.loan-application-follow-up
title: Loan application follow-up
sector: lending
source:
  kind: synthesized
  notes: >-
    No lender has built this with us yet. Written from the base call-task pattern (a form
    starts a call task that escalates), the staff-form pattern used for decisions (a trigger
    filter on the form's answer picks the message), and the KYC nudge rules: documents only
    through the form, and no message ever asks for a PIN, password or code. The wording follows
    fair-lending practice: respectful, no reasons given by message, no amounts.
derived_from: base.first-contact-follow-up
vocabulary:
  person: applicant
  people: applicants
  group: loan officers
summary: >-
  Takes a loan application from the form to a decision. Each application gets an acknowledgement
  and a call task for a loan officer, escalated to the branch manager if nobody calls. When
  your staff record a decision (approved, declined or needs more information), the applicant
  gets the right message. If documents are still missing three days later, an officer is asked
  to call.
when_to_use:
  - You are a microfinance bank, a lending cooperative or an asset-financing business, and people apply by form or at a branch.
  - Applications wait too long for a first call, and applicants don't know what happens next.
  - A loan officer or committee decides by hand and you want every applicant told the same day.

requires:
  data_in:
    - how: public_form
      what: "The Loan Application form and the Upload Documents form this playbook creates. Put the Application link on your website, WhatsApp status and branch QR code. Staff can also fill it in with the applicant at the counter."
    - how: staff_form
      what: "A staff-only Loan Decision form your officers or credit committee fill in for each applicant, matched by phone number."
  channels:
    - channel: sms
      for: Every message to applicants
      needs: An SMS provider, or Origamy managed SMS after business verification (KYC).
  events:
    - onboarding.submitted
  traits:
    - loan_stage
    - loan_decision

parameters:
  - key: org_name
    ask: true
    question: What should messages call your company?
    label: Company name
    type: string
    example: Northwind Credit
  - key: loan_team
    ask: true
    question: Which team calls applicants and reviews applications? (can be one person)
    label: Loan officers
    type: team_name
    default: Loan officers
  - key: manager_team
    ask: true
    question: Who should hear if an application call isn't made in time? (for example the branch manager)
    label: Branch managers
    type: team_name
    default: Branch managers
  - key: amount_ranges
    ask: true
    question: What loan amount ranges should the application form offer? (write each with your currency)
    help: One range per line, for example "Up to 100,000" then "100,000 to 500,000". The form shows these as choices.
    label: Amount ranges
    type: list
    default: [Smaller loan, Medium loan, Larger loan]
  - key: lender_phone
    ask: true
    question: Which number should applicants call about their application?
    label: Company phone
    type: string
    default: "{{ workspace.phone }}"
  - key: call_due_hours
    label: Hours a loan officer has to make a call
    type: hours
    default: 24
  - key: escalate_after_hours
    label: Hours after which an unmade first call escalates
    type: hours
    default: 48
  - key: info_wait_days
    label: Days to wait for documents before an officer calls
    type: days
    default: 3
  - key: default_country
    label: Default phone country on the forms
    type: country_code
    default: "{{ workspace.country }}"
  - key: greeting_fallback
    label: Greeting when a first name is missing
    type: string
    default: there

timings:
  - A new application gets an SMS straight away, and a call task for {{ params.loan_team }} due in {{ params.call_due_hours }} hours, shared out in turn. If nobody has responded after {{ params.escalate_after_hours }} hours, it escalates to {{ params.manager_team }}.
  - A decision recorded on the Loan Decision form sends the matching SMS straight away.
  - "Needs more information: the applicant gets the upload link straight away. If they have not uploaded anything {{ params.info_wait_days }} days later, a call task is created for {{ params.loan_team }}, due in {{ params.call_due_hours }} hours."
  - New documents uploaded through the Upload Documents form get an SMS straight away and a review task for {{ params.loan_team }}.

customer_must:
  - Add your loan officers to the {{ params.loan_team }} team and managers to {{ params.manager_team }} (Settings → Collaborators → Teams). A task assigned to an empty team reaches nobody.
  - "Limit who can open submitted forms. The application holds ID and bank statement files: give access only to the staff who review applications, and check how long your data-protection rules let you keep them."
  - Have officers or your credit committee fill in the Loan Decision form for every applicant as soon as a decision is made. It matches the applicant by phone number, so use the number on their application. The internal note is never sent, but keep it factual.
  - Tell applicants, on the form and in your branch, that documents go only through the form and that you will never ask for a PIN, password or code by SMS, call or WhatsApp. No message in this playbook asks for one, and none shows an amount.
  - Check the wording with your regulator's rules for lending advertising and customer notices. The playbook never gives reasons for a decline in a message.
  - Publish the three forms, connect an SMS provider or finish business verification, switch the follow-ups on and test with your own phone and a fake application.

objects:
  - ref: team_officers
    kind: team
    about: Gets the call tasks and the document reviews.
    body: { name: "{{ params.loan_team }}" }
  - ref: team_managers
    kind: team
    about: Hears about application calls not made in time.
    body: { name: "{{ params.manager_team }}" }

  - ref: form_apply
    kind: form
    about: The loan application form, with ID and bank statement uploads. A hidden field moves the applicant to the applied stage.
    body:
      name: Loan Application
      slug: loan-application
      description: Apply for a loan. We will never ask for your PIN, password or any code.
      draft_state:
        version: 1
        meta: { layout: classic, submit_label: Submit application, success_message: "Thank you! We've received your application and a loan officer will be in touch." }
        pages:
          - id: p1
            title: Your details
            description: Tell us who you are and 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: Surname, 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: p2
            title: Your loan
            description: Tell us what you need.
            fields:
              - id: f_business
                key: business_type
                type: single_select
                label: What kind of business or work do you do?
                required: true
                maps_to: { mode: custom, key: business_type }
                options:
                  - { value: trading, label: Trading or retail }
                  - { value: farming, label: Farming or agribusiness }
                  - { value: services, label: Services }
                  - { value: making, label: Making or manufacturing }
                  - { value: transport, label: Transport }
                  - { value: other, label: Other }
              - id: f_amount
                key: amount_range
                type: single_select
                label: How much would you like to borrow?
                required: true
                maps_to: { mode: custom, key: amount_range }
                options:
                  $each: params.amount_ranges
                  $value: { value: "{{ item }}", label: "{{ item }}" }
              - { id: f_purpose, key: purpose, type: long_text, label: What will the loan be used for?, required: true, maps_to: { mode: custom, key: purpose } }
          - id: p3
            title: Your documents
            description: Upload clear photos or PDFs. Only upload them here, never send them by message.
            fields:
              - { id: f_id_doc, key: id_document, type: file, label: A valid ID, required: true, maps_to: { mode: custom, key: id_document }, validation: { mime_types: [image/jpeg, image/png, application/pdf], max_size_mb: 5 } }
              - { id: f_statement, key: bank_statement, type: file, label: Your recent bank statement, required: true, maps_to: { mode: custom, key: bank_statement }, validation: { mime_types: [image/jpeg, image/png, application/pdf], max_size_mb: 10 } }
              - { id: f_consent, key: consent_to_review, type: boolean, label: I agree that my details and documents may be used to assess this application, required: true, maps_to: { mode: custom, key: consent_to_review } }
              - { id: f_stage, key: stage, type: hidden, label: "", required: false, default: applied, maps_to: { mode: custom, key: loan_stage, trait: true } }
      verification_settings: { required: false }
  - ref: form_docs
    kind: form
    about: A short form for applicants to upload missing documents. A hidden field marks the stage as documents received.
    body:
      name: Upload Documents
      slug: upload-documents
      description: Send us the documents we asked for. We will never ask for your PIN, password or any code.
      draft_state:
        version: 1
        meta: { layout: classic, submit_label: Send documents, success_message: "Thank you! Our team will review your documents." }
        pages:
          - id: p1
            title: Upload your documents
            description: Use the same phone number you applied with.
            fields:
              - { id: f_phone, key: phone, type: phone, label: Phone number you applied with, required: true, maps_to: { mode: mapped, slot: profile.phone }, config: { country_picker: true, default_country: "{{ params.default_country }}", verify: "off" } }
              - { id: f_doc1, key: document_one, type: file, label: Document, required: true, maps_to: { mode: custom, key: document_one }, validation: { mime_types: [image/jpeg, image/png, application/pdf], max_size_mb: 10 } }
              - { id: f_doc2, key: document_two, type: file, label: Another document (if asked), required: false, maps_to: { mode: custom, key: document_two }, validation: { mime_types: [image/jpeg, image/png, application/pdf], max_size_mb: 10 } }
              - { id: f_stage, key: stage, type: hidden, label: "", required: false, default: documents_received, maps_to: { mode: custom, key: loan_stage, trait: true } }
      verification_settings: { required: false }
  - ref: form_decision
    kind: form
    about: Staff-only form where officers record a decision. Matched to the applicant by phone.
    body:
      name: Loan Decision
      slug: loan-decision
      description: Loan officers and credit committee only. One entry per applicant when a decision is made.
      draft_state:
        version: 1
        meta: { layout: classic, submit_label: Save decision, success_message: Saved. The applicant will be told by SMS. }
        pages:
          - id: p1
            title: Loan decision
            description: Use the phone number from the application.
            fields:
              - { id: f_phone, key: phone, type: phone, label: Applicant's phone number, required: true, maps_to: { mode: mapped, slot: profile.phone }, config: { country_picker: true, default_country: "{{ params.default_country }}", verify: "off" } }
              - id: f_decision
                key: loan_decision
                type: multiple_choice
                label: Decision
                required: true
                maps_to: { mode: custom, key: loan_decision, trait: true }
                options:
                  - { value: approved, label: Approved }
                  - { value: declined, label: Declined }
                  - { value: needs_info, label: Needs more information }
              - { id: f_by, key: decided_by, type: short_text, label: Your name, required: true, maps_to: { mode: custom, key: decided_by } }
              - { id: f_note, key: decision_note, type: long_text, label: Internal note (never sent to the applicant), required: false, maps_to: { mode: custom, key: decision_note } }
      verification_settings: { required: false }

  - ref: seg_applied
    kind: segment
    about: Applicants waiting for a first call or a decision.
    body:
      name: Applied, awaiting a decision
      description: Loan stage is applied (set by the Loan Application form).
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: loan_stage, cond_operator: eq, value: applied }
  - ref: seg_needs_info
    kind: segment
    about: Applicants who were asked for more information and haven't sent it.
    body:
      name: Needs more information
      description: Loan stage is needs_info (set when the decision is recorded).
      segment_type: dynamic
      status: active
      definition:
        operator: and
        conditions:
          - { type: trait, trait_key: loan_stage, cond_operator: eq, value: needs_info }

  - ref: tmpl_ack
    kind: template
    about: Acknowledgement when an application comes in.
    body:
      name: Application received (SMS)
      channel: sms
      category: Loans
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: thank you, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}. We've received your loan application and a loan officer will call you soon. We will never ask for your PIN, password or any code. Reply STOP to opt out."
  - ref: tmpl_approved
    kind: template
    about: Message when an application is approved. Next steps, no amounts.
    body:
      name: Loan approved (SMS)
      channel: sms
      category: Loans
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: good news, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}. Your loan application has been approved. A loan officer will call you to agree the next steps. We will never ask for your PIN, password or any code. Reply STOP to opt out."
  - ref: tmpl_declined
    kind: template
    about: Respectful message when an application can't be approved. No reasons.
    body:
      name: Loan not approved (SMS)
      channel: sms
      category: Loans
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: thank you for applying, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}. We're not able to approve your application this time. You're welcome to apply again in future, and you can call {{ params.lender_phone }} if you'd like to talk it through. Reply STOP to opt out."
  - ref: tmpl_needs_info
    kind: template
    about: Message with the secure upload link when more information is needed.
    body:
      name: More information needed (SMS)
      channel: sms
      category: Loans
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: hello {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}, we need a little more to finish your application. Please upload the documents we asked for here: {{ ref.form_docs.url }} Not sure what's needed? Call {{ params.lender_phone }}. We will never ask for your PIN, password or any code. Reply STOP to opt out."
  - ref: tmpl_docs_received
    kind: template
    about: Acknowledgement when documents are uploaded.
    body:
      name: Documents received (SMS)
      channel: sms
      category: Loans
      style: text
      status: active
      subject: ""
      metadata: {}
      variables: {}
      body: "{{ params.org_name }}: thank you, {{ traits.first_name | default: \"{{ params.greeting_fallback }}\" }}. We've received your documents and our team will review them. Reply STOP to opt out."

  - ref: wf_application
    kind: workflow
    about: An application gets an acknowledgement and a call task for a loan officer, escalated to the branch manager if nobody calls.
    body:
      name: "Loan 1: new application"
      description: SMS acknowledgement and a call task for the loan officers; the outcome and note are saved on the applicant.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: onboarding.submitted, form_id: "{{ ref.form_apply.id }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_ack.id }}" } } } }
        - id: s2
          type: human_task
          config:
            human_task:
              title: "Call applicant: {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                {{ traits.first_name }} {{ traits.last_name }} ({{ traits.phone }}) applied for a loan.
                Business: {{ properties.business_type | default: "not given" }}. Range: {{ properties.amount_range | default: "not given" }}. Purpose: {{ properties.purpose | default: "not given" }}.

                Open the application and check the ID and bank statement. Call the applicant, confirm the details and tell them what happens next. Never ask for a PIN, password or code. Choose an outcome and add a short note.
              assignee_refs: [{ type: team, id: "{{ ref.team_officers.id }}" }]
              strategy: round_robin
              completion_policy: first_response_wins
              outcome_options:
                - { id: ready, label: Ready for a decision, intent: positive }
                - { id: needs_info, label: Needs more information, intent: neutral }
                - { id: withdrew, label: Withdrew the application, 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
              escalation_ladder:
                - { after_hours: "{{ params.escalate_after_hours }}", to: [{ type: team, id: "{{ ref.team_managers.id }}" }] }
        - { id: s3, type: action, config: { action_type: set_trait, traits: { loan_call: "{{ properties.task_outcome | default: \"unknown\" }}", loan_call_note: "{{ properties.task_note }}" } } }
      re_entry_policy: every_time
      re_entry_cooldown_hours: 0
      goal: {}
      exit_conditions: []
  - ref: wf_approved
    kind: workflow
    about: 'Officers record "approved": the applicant gets the next-steps message.'
    body:
      name: "Loan 2: approved"
      description: When a loan is approved, the stage is saved and the applicant gets an SMS.
      enabled: false
      trigger_type: event
      trigger_config:
        event_name: onboarding.submitted
        form_id: "{{ ref.form_decision.id }}"
        property_filters: [{ property: loan_decision, cond_operator: eq, value: approved }]
        properties: { $origamy_property_filters: true }
      steps:
        - { id: s1, type: action, config: { action_type: set_trait, traits: { loan_stage: approved } } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_approved.id }}" } } } }
      re_entry_policy: every_time
      re_entry_cooldown_hours: 0
      goal: {}
      exit_conditions: []
  - ref: wf_declined
    kind: workflow
    about: 'Officers record "declined": the applicant gets a respectful message with no reasons.'
    body:
      name: "Loan 3: declined"
      description: When a loan is declined, the stage is saved and the applicant gets a respectful SMS.
      enabled: false
      trigger_type: event
      trigger_config:
        event_name: onboarding.submitted
        form_id: "{{ ref.form_decision.id }}"
        property_filters: [{ property: loan_decision, cond_operator: eq, value: declined }]
        properties: { $origamy_property_filters: true }
      steps:
        - { id: s1, type: action, config: { action_type: set_trait, traits: { loan_stage: declined } } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_declined.id }}" } } } }
      re_entry_policy: every_time
      re_entry_cooldown_hours: 0
      goal: {}
      exit_conditions: []
  - ref: wf_needs_info
    kind: workflow
    about: 'Officers record "needs more information": the applicant gets the upload link, and an officer calls if nothing arrives.'
    body:
      name: "Loan 4: needs more information"
      description: The applicant gets the upload link; if nothing has been uploaded some days later, a call task is created for a loan officer.
      enabled: false
      trigger_type: event
      trigger_config:
        event_name: onboarding.submitted
        form_id: "{{ ref.form_decision.id }}"
        property_filters: [{ property: loan_decision, cond_operator: eq, value: needs_info }]
        properties: { $origamy_property_filters: true }
      steps:
        - { id: s1, type: action, config: { action_type: set_trait, traits: { loan_stage: needs_info } } }
        - { id: s2, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_needs_info.id }}" } } } }
        - { id: s3, type: delay, config: { duration: "{{ params.info_wait_days }}d" } }
        - { id: s4, type: filter, config: { condition: { type: trait, trait_key: loan_stage, cond_operator: eq, value: needs_info } } }
        - id: s5
          type: human_task
          config:
            human_task:
              title: "Documents still missing: {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                {{ traits.first_name }} {{ traits.last_name }} ({{ traits.phone }}) was asked for more information and hasn't uploaded anything.

                Call the applicant, ask if they need help, and remind them to use the upload link sent by SMS. Never ask them to send documents by message, or for a PIN, password or code. Choose an outcome and add a short note.
              assignee_refs: [{ type: team, id: "{{ ref.team_officers.id }}" }]
              strategy: round_robin
              completion_policy: first_response_wins
              outcome_options:
                - { id: will_send, label: Will upload soon, intent: positive }
                - { id: cannot_provide, label: Cannot provide them, intent: neutral }
                - { id: withdrew, label: Withdrew the application, 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: s6, type: action, config: { action_type: set_trait, traits: { info_call: "{{ properties.task_outcome | default: \"unknown\" }}", info_call_note: "{{ properties.task_note }}" } } }
      re_entry_policy: every_time
      re_entry_cooldown_hours: 0
      goal: {}
      exit_conditions: []
  - ref: wf_docs
    kind: workflow
    about: Uploaded documents get an acknowledgement and a review task for a loan officer.
    body:
      name: "Loan 5: documents received"
      description: SMS acknowledgement and a review task when the Upload Documents form is submitted.
      enabled: false
      trigger_type: event
      trigger_config: { event_name: onboarding.submitted, form_id: "{{ ref.form_docs.id }}" }
      steps:
        - { id: s1, type: action, config: { action_type: send_message, channel_config: { mode: single, single: { channel: sms, template_id: "{{ ref.tmpl_docs_received.id }}" } } } }
        - id: s2
          type: human_task
          config:
            human_task:
              title: "Review documents: {{ traits.first_name }} {{ traits.last_name }}"
              instructions: |-
                {{ traits.first_name }} {{ traits.last_name }} ({{ traits.phone }}) uploaded documents.

                Review them against what was asked. If everything is there, record a decision on the Loan Decision form. If something is still missing, record Needs more information again. Choose an outcome and add a short note.
              assignee_refs: [{ type: team, id: "{{ ref.team_officers.id }}" }]
              strategy: round_robin
              completion_policy: first_response_wins
              outcome_options:
                - { id: complete, label: Everything is there, intent: positive }
                - { id: still_missing, label: Something is still missing, intent: neutral }
              due_hours: "{{ params.call_due_hours }}"
              wait: true
              wait_timeout_hours: 168
              notify: immediate
        - { id: s3, type: action, config: { action_type: set_trait, traits: { document_review: "{{ properties.task_outcome | default: \"unknown\" }}", document_review_note: "{{ properties.task_note }}" } } }
      re_entry_policy: every_time
      re_entry_cooldown_hours: 0
      goal: {}
      exit_conditions: []

eval:
  name: 98_lending_loan_application_follow_up
  mode: task
  vertical: ""
  prompt: >-
    We're a microfinance company. Make a loan application form that collects the applicant's
    details, business type, a loan amount range, the purpose, and uploads of their ID and bank
    statement. Each application should send them a thank-you SMS and give our loan officers a
    task to call within a day, escalating to the branch manager after two days. When we record
    a decision as approved, declined or needs more information, text the applicant the right
    message; for needs more information, include a link to upload documents, and if nothing
    arrives in three days give an officer a task to call. Never ask for PINs or codes.
  expect_artifacts: [form, segment, workflow, message_template]
  forbid_terms: []
