salesforce-crm-ops
npx machina-cli add skill evalops/open-associate-skills/salesforce-crm-ops --openclawSalesforce CRM ops
When to use
Use this skill when you need to:
- Create or update Leads/Accounts/Opportunities for dealflow
- Log meetings, diligence calls, and next steps as Activities/Tasks
- Query Salesforce for pipeline, duplicates, and coverage
- Automate basic CRM hygiene via scripts
- Validate your field mappings before running in production
Compatibility / assumptions
This skill assumes:
- You have Salesforce REST API access and a Connected App.
- You can obtain an access token via OAuth (client credentials or another flow).
- You know your instance base URL (e.g.,
https://yourdomain.my.salesforce.com).
Required environment variables
Set:
SF_BASE_URL(e.g.,https://yourdomain.my.salesforce.com)SF_CLIENT_IDSF_CLIENT_SECRET
Optional:
SF_API_VERSION(defaultv59.0if omitted)SF_ACCESS_TOKEN(if you already have one)
Configuration files (REQUIRED for production use)
config/field_map.yaml
Every org has different field names and custom objects. Before running any create/update operations, create this file:
# Field mappings: skill field name -> Salesforce API field name
lead:
email: Email
first_name: FirstName
last_name: LastName
company: Company
title: Title
website: Website
status: Status
source: LeadSource
thesis_tag: Thesis_Tag__c # Custom field - adjust to your org
signal_score: Signal_Score__c # Custom field - adjust to your org
must_be_true: Must_Be_True__c # Custom field - adjust to your org
pass_reason: Pass_Reason__c # Custom field - adjust to your org
account:
name: Name
website: Website
description: Description
thesis_tag: Thesis_Tag__c
opportunity:
name: Name
stage: StageName
close_date: CloseDate
amount: Amount
next_step: NextStep
probability: Probability
task:
subject: Subject
due_date: ActivityDate
status: Status
priority: Priority
what_id: WhatId
who_id: WhoId
activity:
subject: Subject
description: Description
what_id: WhatId
who_id: WhoId
config/stages.yaml
Map your firm's deal stages to Salesforce picklist values:
# Stage mappings: logical stage -> Salesforce StageName picklist value
opportunity_stages:
sourced: "Sourced"
first_meeting: "First Meeting"
diligence: "Diligence"
ic_scheduled: "IC Scheduled"
term_sheet: "Term Sheet"
closed_won: "Closed Won"
passed: "Passed"
lead_statuses:
new: "Open - Not Contacted"
contacted: "Working - Contacted"
qualified: "Qualified"
meeting_scheduled: "Meeting Scheduled"
passed: "Closed - Not Converted"
task_statuses:
not_started: "Not Started"
in_progress: "In Progress"
completed: "Completed"
waiting: "Waiting on someone else"
config/required_fields.yaml
Document required fields for your org to prevent API errors:
# Required fields per object (discover these with schema command)
lead:
- LastName
- Company
- Status
opportunity:
- Name
- StageName
- CloseDate
task:
- Subject
Core workflows
0) Schema discovery (RUN THIS FIRST)
Before creating any records, discover your org's schema:
# Describe Lead object - shows all fields, required fields, picklist values
python3 scripts/sf_describe.py Lead
# Describe Opportunity object
python3 scripts/sf_describe.py Opportunity
# Describe all relevant objects and save to file
python3 scripts/sf_describe.py Lead Account Opportunity Task Event --output config/schema.json
This will show:
- All available fields
- Required fields
- Picklist values (for Status, StageName, etc.)
- Field types and constraints
Use this output to populate your config/*.yaml files.
1) Dry-run mode (ALWAYS USE FIRST)
Every write operation supports --dry-run flag:
# See what would be created without actually creating
python3 scripts/sf_upsert_lead.py --dry-run \
--email founder@company.com \
--first "Ada" --last "Lovelace" \
--company "ExampleAI"
# Validate opportunity creation
python3 scripts/sf_create_opportunity.py --dry-run \
--name "ExampleAI Seed" \
--stage "Qualification" \
--close-date "2026-03-31"
Dry-run will:
- Validate all required fields are present
- Check picklist values against schema
- Show the exact API request that would be made
- NOT create or modify any records
2) Get an access token (OAuth client credentials)
Preferred for server-to-server integrations:
- POST to:
https://<mydomain>.my.salesforce.com/services/oauth2/token - Use grant type
client_credentials(or whatever your org allows)
If your org uses a different OAuth flow, adapt accordingly.
3) Query Salesforce (SOQL)
Use the Query resource:
GET /services/data/vXX.X/query/?q=<SOQL>
Reminder: encode spaces as + or %20 in URLs.
4) Upsert by external ID (PREFERRED)
To avoid duplicates, upsert by a stable external ID rather than creating blind:
# Upsert by email (if Email is an external ID field)
python3 scripts/sf_upsert_lead.py --external-id Email \
--email founder@company.com \
--first "Ada" --last "Lovelace" \
--company "ExampleAI"
# Upsert by custom external ID field
python3 scripts/sf_upsert_lead.py --external-id External_ID__c \
--external-id-value "company-123" \
--first "Ada" --last "Lovelace" \
--company "ExampleAI"
5) Create an Opportunity for a deal
Minimum fields depend on your org (check config/required_fields.yaml):
python3 scripts/sf_create_opportunity.py \
--name "ExampleAI Seed" \
--stage "First Meeting" \
--close-date "2026-03-31" \
--account-id "001XXXXXXXXXXXX"
6) Log Activities / Tasks
Create a Task with next step:
python3 scripts/sf_create_task.py \
--subject "Follow up: send customer intro" \
--due-date "2026-02-05" \
--what-id "006XXXXXXXXXXXX" \
--status "Not Started"
Scripts
Core scripts
scripts/sf_oauth_client_credentials.py- Get access tokenscripts/sf_query.py- Run SOQL queriesscripts/sf_describe.py- Schema discovery (NEW)scripts/sf_upsert_lead.py- Create/update Leadscripts/sf_create_opportunity.py- Create Opportunityscripts/sf_create_task.py- Create Task
Script flags (all scripts support these)
--dry-run- Validate without executing--config- Path to config directory (default:./config)--verbose- Show detailed API requests/responses
Safety / hygiene rules
- Always run
--dry-runfirst when testing new operations. - Run schema discovery before configuring a new org.
- Prefer upsert by stable identifiers (email, domain) to avoid duplicates.
- Keep notes factual (assume audits).
- Record next step with a due date; otherwise the CRM is dead.
- Every pass must have a pass reason and "what would change our mind."
Examples
First-time setup for a new org
# 1. Get token
python3 scripts/sf_oauth_client_credentials.py
# 2. Discover schema
python3 scripts/sf_describe.py Lead Account Opportunity Task --output config/schema.json
# 3. Review output and create config files
# Edit config/field_map.yaml, config/stages.yaml, config/required_fields.yaml
# 4. Test with dry-run
python3 scripts/sf_upsert_lead.py --dry-run --email test@example.com --first "Test" --last "User" --company "TestCo"
# 5. Run for real
python3 scripts/sf_upsert_lead.py --email test@example.com --first "Test" --last "User" --company "TestCo"
Query leads by email
python3 scripts/sf_query.py "SELECT Id, Name, Email, Company FROM Lead WHERE Email='founder@company.com' LIMIT 5"
Upsert a lead with thesis tagging
python3 scripts/sf_upsert_lead.py \
--email founder@company.com \
--first "Ada" --last "Lovelace" \
--company "ExampleAI" \
--title "CEO" \
--website "https://example.ai" \
--status "Open - Not Contacted" \
--thesis-tag "AI Security" \
--signal-score 4 \
--must-be-true "Enterprise buyers will pay for automated security posture management"
Create an opportunity with full context
python3 scripts/sf_create_opportunity.py \
--name "ExampleAI Seed" \
--stage "First Meeting" \
--close-date "2026-03-31" \
--next-step "Schedule diligence calls" \
--amount 2000000
Troubleshooting
"Required field missing" error
Run schema discovery to see what's required:
python3 scripts/sf_describe.py Lead | grep -A5 "required"
"Invalid picklist value" error
Run schema discovery to see valid picklist values:
python3 scripts/sf_describe.py Opportunity | grep -A20 "StageName"
"Duplicate detected" error
Use upsert with external ID instead of create:
python3 scripts/sf_upsert_lead.py --external-id Email --email founder@company.com ...
Source
git clone https://github.com/evalops/open-associate-skills/blob/main/salesforce-crm-ops/SKILL.mdView on GitHub Overview
Operate Salesforce as a VC associate CRM: authenticate via OAuth, query with SOQL, upsert Leads/Accounts, create Opportunities, and log Activities/Tasks for dealflow and portfolio support. Includes field mapping configuration and a dry-run mode to validate changes before production.
How This Skill Works
The skill uses Salesforce REST API with OAuth to obtain an access token and perform create/update operations across Lead, Account, Opportunity, Task, and Activity objects. Field mappings in config/field_map.yaml translate generic skill fields to Salesforce API fields, while config/stages.yaml maps deal stages to Salesforce picklist values; a dry-run option simulates actions before execution.
When to Use It
- Create or update Leads/Accounts/Opportunities for dealflow
- Log meetings, diligence calls, and next steps as Activities/Tasks
- Query Salesforce for pipeline, duplicates, and coverage
- Automate basic CRM hygiene via scripts
- Validate your field mappings before running in production
Quick Start
- Step 1: Set up credentials and environment variables (SF_BASE_URL, SF_CLIENT_ID, SF_CLIENT_SECRET; optionally SF_ACCESS_TOKEN).
- Step 2: Create config/field_map.yaml, config/stages.yaml, and config/required_fields.yaml for your org.
- Step 3: Discover schema and run a dry-run before production with provided scripts (sf_describe.py, etc.).
Best Practices
- Define and pin field_map.yaml per org before any runs
- Run schema discovery first to understand required fields and picklists
- Use dry-run mode to validate behavior before live writes
- Validate required fields in config/required_fields.yaml to prevent API errors
- Map stages and statuses via config/stages.yaml to avoid mismatched values
Example Use Cases
- Upsert a new Lead with email, name, and source and associate to an Account if it exists
- Create an Opportunity when a Lead converts and set StageName via stages.yaml
- Log a diligence call as an Activity linked to the Lead/Opportunity
- Identify potential duplicates by querying pipeline with SOQL and adjust records
- Dry-run a batch of Lead/Account/Opportunity upserts to validate field mappings