RecipesSales & CRM

Recipes: sales & CRM

Three pipelines that turn CRM data into timely action: reply to leads first, walk into every meeting prepared, and never let a deal go quiet.


Speed-to-lead

The second a lead comes in, create the CRM contact, draft a personalized first-touch email, and drop it in the rep’s Slack to send — responding first wins the deal.

The shape:

lead (webhook)
  ├─ lead-contact (hubspot)
  └─ lead-draft (claude-api) → notify-rep (slack)
# jobs/sales/lead.yaml — your form POSTs { email, name, company, message } here
id: lead
name: Inbound lead
group: sales
type: webhook
hookId: inbound_lead_0123456789abcdef
# jobs/sales/lead-contact.yaml
id: lead-contact
name: Create CRM contact
group: sales
type: hubspot
credential: hubspot
operation: updateContact
contactId: '{{ $parent.json.email }}'
idProperty: email
email: '{{ $parent.json.email }}'
firstname: '{{ $parent.json.name }}'
company: '{{ $parent.json.company }}'
schedule:
  - { kind: afterParent, parent: lead, on: succeeded }
# jobs/sales/draft.yaml
id: lead-draft
name: Draft first-touch
group: sales
type: claude-api
credential: anthropic
maxTokens: 400
system: You write short, warm, non-pushy B2B first-touch emails. Sign off as {{ $vars.repName }}.
prompt: |
  Write a first-touch email to {{ $parent.json.name }} at {{ $parent.json.company }}.
  They said: "{{ $parent.json.message }}". Keep it to 5 sentences and end with a soft call booking.
schedule:
  - { kind: afterParent, parent: lead, on: succeeded }
# jobs/sales/notify-rep.yaml
id: notify-rep
name: Notify rep
group: sales
type: slack
credential: slack_bot
channel: '{{ $vars.repSlackChannel }}'
message: |
  :zap: New lead — draft ready to send:
 
  {{ $parent.text }}
schedule:
  - { kind: afterParent, parent: lead-draft, on: succeeded }

Why not auto-send? A human sending the first reply protects your domain reputation and lets the rep add a personal line. Flip it to a gmail sendMessage once you trust the drafts.


Meeting-ready briefs

~30 minutes before each meeting, generate a quick prep — likely purpose, smart questions, what to have ready — and send it to you.

The shape:

upcoming-meeting (calendar-watch) → meeting-brief (claude-api) → brief-send (slack)

A calendar-watch trigger fires as each event approaches (its pollSeconds must be shorter than leadMinutes × 60).

# jobs/prep/upcoming-meeting.yaml
id: upcoming-meeting
name: Upcoming meeting
group: prep
type: calendar-watch
credential: google_sa
calendarId: '{{ $vars.calendarId }}'
leadMinutes: 30
pollSeconds: 300
schedule: []
# jobs/prep/brief.yaml
id: meeting-brief
name: Meeting brief
group: prep
type: claude-api
credential: anthropic
maxTokens: 400
system: You are an executive assistant. Be concise and practical.
prompt: |
  My next meeting: "{{ $parent.json.summary }}" at {{ $parent.json.start }} ({{ $parent.json.location }}).
  Give me a 5-line prep: likely purpose, 3 smart questions to ask, and anything to have ready.
schedule:
  - { kind: afterParent, parent: upcoming-meeting, on: succeeded }
# jobs/prep/brief-send.yaml
id: brief-send
name: Send brief
group: prep
type: slack
credential: slack_bot
channel: '{{ $vars.mySlackChannel }}'
message: |
  :calendar: Prep for *{{ $parent.json.summary }}*
 
  {{ $parent.text }}
schedule:
  - { kind: afterParent, parent: meeting-brief, on: succeeded }

Enrich it. Insert a hubspot/salesforce lookup between the trigger and the brief (search by the meeting title) so the brief includes the live CRM record.


Stale-deal nudger

Each morning, find open deals with no recent activity and draft a re-engagement nudge for each — pipeline hygiene without the manual combing. Shown with Salesforce; HubSpot and Pipedrive work the same via their search APIs.

The shape:

stale-deals (salesforce, daily) → stale-nudges (claude-api) → nudge-digest (slack)
# jobs/sales/stale-deals.yaml
id: stale-deals
name: Stale deals
group: sales
type: salesforce
credential: salesforce
operation: query
soql: >
  SELECT Id, Name, Amount, StageName, LastActivityDate FROM Opportunity
  WHERE IsClosed = false AND (LastActivityDate < LAST_N_DAYS:7 OR LastActivityDate = null)
  ORDER BY Amount DESC LIMIT 25
schedule:
  - { kind: daily, time: '08:00' }
# jobs/sales/nudges.yaml
id: stale-nudges
name: Draft nudges
group: sales
type: claude-api
credential: anthropic
maxTokens: 600
system: You are a sales coach. For each deal, write ONE short, specific re-engagement line.
prompt: |
  These open deals have gone quiet. Draft a one-line nudge for each (name it):
  {{ $parent.json.records }}
schedule:
  - { kind: afterParent, parent: stale-deals, on: succeeded }
# jobs/sales/nudge-digest.yaml
id: nudge-digest
name: Nudge digest
group: sales
type: slack
credential: slack_bot
channel: '{{ $vars.aeSlackChannel }}'
message: |
  :wave: Deals worth a nudge today:
 
  {{ $parent.text }}
schedule:
  - { kind: afterParent, parent: stale-nudges, on: succeeded }