← All 12 CRMs · Make guide · Zapier guide
Connection type: Native. OAuth to the org that holds settled clients. Do not include Leads. This engine is for existing clients only.
Contact by default. If rates live on a custom Loan / Opportunity, keep Contact as the person the broker email is about and look the loan fields up, or filter a Report that already joins them. Person Accounts: create the five fields on Account and map those instead.
Setup → Object Manager → Contact → Fields & Relationships → New. Use the API names in the table. Exclude DoNotCall = true in the search as well as in the gate.
Fields for this CRM
Create the ones marked YES. Map the ones you already store. Do not invent a second current-rate field.
| Purpose | Canonical | This CRM | Required | Create? |
|---|---|---|---|---|
| Contact ID | contact_id | Id | Yes | No |
| First name | first_name | FirstName | Yes | No |
| Last name | last_name | LastName | No | No |
email | Email | Yes | No | |
| Mobile | mobile | MobilePhone | Recommended | No |
| Current rate | tbt_current_rate | tbt_current_rate__c | Yes | Map or create |
| New-money rate | tbt_new_money_rate | tbt_new_money_rate__c | Yes | YES - create or maintain |
| Loyalty gap (bps) | tbt_loyalty_gap_bps | tbt_loyalty_gap_bps__c | Yes | YES - create / compute |
| Lender | lender | Lender__c | Recommended | No |
| Product name | product_name | Loan_Product__c | Recommended | No |
| Existing client flag | existing_client | tbt_existing_client__c or Account record type | Yes | Map |
| Email opt-out | email_optout | HasOptedOutOfEmail | Yes | No |
| Loyalty state | tbt_loyalty_state | tbt_loyalty_state__c | Yes | YES - create this |
| Last loyalty touch | tbt_last_loyalty_touch | tbt_last_loyalty_touch__c | Yes | YES - create this |
| Broker owner | broker_owner | OwnerId | Recommended | No |
| Broker email | broker_email | set in watch mapper | Yes | Set in the watch |
Full translation and notes: 05-templates/CRM-Field-Map.csv. Gap is in basis points. Example: current 6.40 and new-money 5.90 is a 50 bps gap. Default watch threshold is 25 bps.
Make — watch file to import
Import 02-make-blueprints/01b-TBT-Loyalty-Tax-Watch-Salesforce.blueprint.json.
Import this file as the watch scenario. Sign in. Confirm the SOQL object is the one that has the rates (Contact by default). Then import the outreach blueprint.
Everyone also imports 02-make-blueprints/02-TBT-Loyalty-Tax-Outreach.blueprint.json. Import outreach first so you have the webhook URL. Walkthrough: MAKE-Setup-Guide.html.
Make — last-step swap
On the outreach scenario, modules 7, 8 and 9 are HTTP placeholders labelled SWAP. Replace them. The middle (consent, gap gate ≥ 25 bps, OpenAI, broker Gmail) does not change.
| Outreach module | Swap for |
|---|---|
| 7 · Create the broker task — SWAP | Salesforce → Create a Task. Title {{3.broker_task_title}}, body {{3.broker_task_body}}, WhoId = Contact Id, due today. |
| 8 · Write the CRM note — SWAP | Salesforce → Create a Record (Task with Type = Note, or ContentNote linked to the Contact Id). Body {{3.crm_note}}. |
| 9 · Stamp loyalty touch — SWAP | Salesforce → Update a Record (Contact). Set tbt_loyalty_state__c = touched and tbt_last_loyalty_touch__c = today. Do not skip. |
Stamp body (use this CRM’s field names from the table):
{
"tbt_loyalty_state": "touched",
"tbt_last_loyalty_touch": "{{formatDate(now; \"YYYY-MM-DD\")}}"
}
Zapier — first module and last three
Zapier cannot import a Make blueprint. Build two Zaps by hand. Full click-path: ZAPIER-Setup-Guide.html. This page is the CRM-specific first and last steps only.
| Where | What |
|---|---|
| Zap A · first module | Schedule by Zapier (daily 09:00) + Salesforce Find Record / Find Records (Contact). Filter tbt_loyalty_gap_bps__c ≥ 25, email exists, existing client, state is not opt_out or touched. Then Webhooks by Zapier → POST the canonical payload to Zap B’s Catch Hook. Field names from the table below. |
| Zap B step | Module |
|---|---|
| B6 Task | Salesforce → Create Task. Subject from broker_task_title, description from broker_task_body, WhoId = contact_id, due today. |
| B7 Note | Salesforce → Create Record (Note or ContentNote) on that Contact. Body from crm_note. |
| B8 Stamp | Salesforce → Update Record (Contact). tbt_loyalty_state__c = touched, tbt_last_loyalty_touch__c = today. |
Zap A then POSTs the canonical keys (see the field-map Canonical column) to Zap B’s Catch Hook. Leave auto_email_client as false. Replace [YOUR BROKER EMAIL], [YOUR FIRST NAME], [YOUR BUSINESS NAME].
- Create the
__cfields before you import. Red modules after import almost always mean the API names do not exist yet. - If you already store the current variable rate under another number field, map that field. Do not invent a second source of truth.
- Leave
auto_email_clientasfalse. Default is email the broker, not the client.
01e)
If Salesforce OAuth is blocked (sandbox only, missing licences, licensee policy), export settled clients with current rate, new-money rate and gap into the Google Sheet and import 01e-TBT-Loyalty-Tax-Watch-GoogleSheets-Bridge.blueprint.json the same week.
T Test on yourself before any live send
- Create yourself as a settled existing-client test record with a real email you read, a current variable rate, a new-money / retention reference, and
tbt_loyalty_gap_bpsat or above 25. Statedueorqueued. Last touch empty. - Run the watch (Make scenario 01, or Zap A) with a limit of 1, filtered to only this record.
- Confirm the broker email arrives. Confirm you did not get a client email.
auto_email_clientstaysfalse. - Confirm the broker task, the CRM note (or sheet write-back), and the stamp: state
touched, last touch = today. - Run again the same day. The engine must not draft you a second time. Then set state to
opt_outand run again — must not draft.
If the stamp fails, stop. Fix it before anyone else is in the set. After the loop is proven on you, you may flip auto_email_client — not before. Read 04-compliance first.