crm-integration
$
npx mdskill add manojbajaj95/claude-gtm-plugin/crm-integration<objective> Integrate with CRM platforms for sales automation workflows:
SKILL.md
.github/skills/crm-integrationView on GitHub ↗
---
name: "crm-integration"
description: "CRM integration patterns for Close CRM, HubSpot, and Salesforce. Use when: Close CRM, HubSpot, Salesforce, CRM API, lead sync, deal sync, activity logging, CRM webhook, pipeline automation, contact enrichment."
---
<objective>
Integrate with CRM platforms for sales automation workflows:
1. **Close CRM** - Daily driver for SMB sales (simplest API, best value)
2. **HubSpot** - Marketing + Sales alignment with rich ecosystem
3. **Salesforce** - Enterprise requirements and complex workflows
4. **Cross-CRM Sync** - Bidirectional sync with conflict resolution
Key deliverables:
- API client setup with proper authentication
- CRUD operations for leads, contacts, deals, activities
- Webhook handlers for real-time sync
- Pipeline automation and reporting
</objective>
## Workspace Context
Read bootstrap context before asking questions: `strategy/brand.md` for brand, audience, offer, channels, tools, constraints, and metrics; `about/me.md` for personal voice; `content/ideas.md` and `content/calendar.md` for content planning. Use legacy product-marketing context files only as fallback. Save generated drafts to `content/<platform>/drafts/YYYY-MM-DD_short-topic-slug.md`, and route durable learnings back to `strategy/brand.md`, `about/me.md`, or `content/ideas.md`.
## Operating Contract
This skill is self-contained for its frontmatter scope: use its local instructions, references, scripts, and assets as the playbook; ask only for missing task-specific inputs; hand off to adjacent skills instead of expanding scope; and return an actionable artifact, decision, plan, draft, or diagnostic.
<quick_start>
**Close CRM (API Key Auth):**
```python
import httpx
class CloseClient:
BASE_URL = "https://api.close.com/api/v1"
def __init__(self, api_key: str):
self.client = httpx.Client(
base_url=self.BASE_URL,
auth=(api_key, ""), # Basic auth, password empty
timeout=30.0,
)
def create_lead(self, data: dict) -> dict:
response = self.client.post("/lead/", json=data)
response.raise_for_status()
return response.json()
def search_leads(self, query: str) -> list:
response = self.client.post("/data/search/", json={
"query": {"type": "query_string", "value": query},
"results_limit": 100
})
return response.json()["data"]
# Usage
close = CloseClient(os.environ["CLOSE_API_KEY"])
leads = close.search_leads("company:Coperniq")
```
**HubSpot (Python SDK):**
```python
from hubspot import HubSpot
from hubspot.crm.contacts import SimplePublicObjectInputForCreate
client = HubSpot(access_token=os.environ["HUBSPOT_ACCESS_TOKEN"])
# Create contact
contact = client.crm.contacts.basic_api.create(
SimplePublicObjectInputForCreate(properties={
"email": "user@example.com",
"firstname": "Jane",
"lastname": "Smith"
})
)
print(f"Created: {contact.id}")
```
**Salesforce (JWT Bearer):**
```python
import jwt
from datetime import datetime, timedelta
class SalesforceClient:
def __init__(self, client_id: str, username: str, private_key: str):
self.auth_url = "https://login.salesforce.com"
self._authenticate(client_id, username, private_key)
def _authenticate(self, client_id, username, private_key):
payload = {
"iss": client_id,
"sub": username,
"aud": self.auth_url,
"exp": int((datetime.utcnow() + timedelta(minutes=3)).timestamp())
}
assertion = jwt.encode(payload, private_key, algorithm="RS256")
response = httpx.post(f"{self.auth_url}/services/oauth2/token", data={
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": assertion
})
self.access_token = response.json()["access_token"]
self.instance_url = response.json()["instance_url"]
```
</quick_start>
<success_criteria>
A CRM integration is successful when:
- API authentication works without errors
- CRUD operations complete for all entity types
- Rate limits are respected (Close: 100 req/10s, HubSpot: varies by tier)
- Webhooks fire and process correctly
- Data syncs bidirectionally without duplicates
</success_criteria>
<crm_comparison>
## Platform Comparison
| Feature | Close | HubSpot | Salesforce |
|---------|-------|---------|------------|
| **Auth** | API Key | OAuth 2.0 / Private App | JWT Bearer |
| **Rate Limit** | 100 req/10s | 100-200 req/10s by tier | 100k req/day |
| **Best For** | SMB sales, simplicity | Marketing + Sales | Enterprise |
| **Starting Price** | $49/user/mo | Free (limited) | $25/user/mo |
| **API Access** | All plans | Starter+ ($45+) | All plans |
| **Webhooks** | All plans | Pro+ ($800+) | All plans |
## Entity Mapping
| Concept | Close | HubSpot | Salesforce |
|---------|-------|---------|------------|
| Company | `lead` | `company` | `Account` |
| Person | `contact` | `contact` | `Contact` / `Lead` |
| Deal | `opportunity` | `deal` | `Opportunity` |
| Activity | `activity` | `engagement` | `Task` / `Event` |
| Custom Field | `custom.cf_xxx` | `properties` | `Field__c` |
## Pipeline Stage Mapping
| Stage | Close | HubSpot | Salesforce |
|-------|-------|---------|------------|
| New | `Lead` | `appointmentscheduled` | `Prospecting` |
| Qualified | `Contacted` | `qualifiedtobuy` | `Qualification` |
| Demo | `Opportunity` | `presentationscheduled` | `Needs Analysis` |
| Proposal | `Proposal` | `decisionmakerboughtin` | `Proposal/Price Quote` |
| Won | `Won` | `closedwon` | `Closed Won` |
| Lost | `Lost` | `closedlost` | `Closed Lost` |
</crm_comparison>
<close_patterns>
## Close CRM (Daily Driver)
### Query Language (for Smart Views)
```python
# Leads with no activity in 30 days
'sort:date_updated asc date_updated < "30 days ago"'
# High-value opportunities
'opportunities.value >= 50000 opportunities.status_type:active'
# Custom field filtering
'custom.cf_industry = "MEP Contractor"'
# Multiple trade types (your ICP)
'custom.cf_trades:HVAC OR custom.cf_trades:Electrical'
```
### Core Operations
```python
# Create lead with contacts
lead = close.create_lead({
"name": "ABC Mechanical",
"url": "https://abcmech.com",
"contacts": [{
"name": "John Smith",
"title": "Owner",
"emails": [{"email": "john@abcmech.com", "type": "office"}],
"phones": [{"phone": "555-1234", "type": "office"}]
}],
"custom.cf_tier": "Gold",
"custom.cf_source": "sales-agent"
})
# Create opportunity
opp = close._request("POST", "/opportunity/", json={
"lead_id": lead["id"],
"value": 50000,
"confidence": 50,
"status_id": "stat_xxx" # Pipeline stage
})
# Log activity
close._request("POST", "/activity/note/", json={
"lead_id": lead["id"],
"note": "Initial discovery call - interested in demo"
})
```
### Rate Limit Headers (RFC-compliant)
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200
```
> See `reference/close-deep-dive.md` for query language, Smart Views, sequences, and reporting.
</close_patterns>
<hubspot_patterns>
## HubSpot Integration
### Python SDK Pattern
```python
from hubspot import HubSpot
from hubspot.crm.deals import SimplePublicObjectInputForCreate
from hubspot.crm.contacts import PublicObjectSearchRequest
client = HubSpot(access_token=os.environ["HUBSPOT_ACCESS_TOKEN"])
# Create deal with association
deal = client.crm.deals.basic_api.create(
SimplePublicObjectInputForCreate(properties={
"dealname": "Enterprise Deal",
"amount": "50000",
"dealstage": "appointmentscheduled",
"pipeline": "default"
})
)
# Search contacts by email domain
search = PublicObjectSearchRequest(
filter_groups=[{
"filters": [{
"propertyName": "email",
"operator": "CONTAINS",
"value": "@example.com"
}]
}],
properties=["email", "firstname", "lastname"],
limit=50
)
results = client.crm.contacts.search_api.do_search(search)
```
### Association Types
| From | To | Type ID |
|------|-----|---------|
| Contact | Company | 1 |
| Contact | Deal | 4 |
| Company | Deal | 6 |
| Deal | Contact | 3 |
> See `reference/hubspot-patterns.md` for batch operations, custom properties, and workflows.
</hubspot_patterns>
<salesforce_patterns>
## Salesforce Integration
### SOQL Query Patterns
```sql
-- Parent-child relationship (Contacts of Account)
SELECT Id, Name, (SELECT LastName, Email FROM Contacts)
FROM Account WHERE Industry = 'Technology'
-- Child-parent relationship
SELECT Id, FirstName, Account.Name, Account.Industry
FROM Contact WHERE Account.Industry = 'Technology'
-- Semi-join (Accounts with open Opportunities)
SELECT Id, Name FROM Account
WHERE Id IN (SELECT AccountId FROM Opportunity WHERE IsClosed = false)
```
### REST API v59.0
```python
def create_opportunity(self, data: dict) -> dict:
"""Required: Name, StageName, CloseDate."""
response = self.client.post(
f"{self.instance_url}/services/data/v59.0/sobjects/Opportunity/",
headers={"Authorization": f"Bearer {self.access_token}"},
json=data
)
return response.json()
# Composite API (batch up to 200 records)
def composite_create(self, records: list) -> dict:
return self.client.post(
f"{self.instance_url}/services/data/v59.0/composite/sobjects",
json={"allOrNone": False, "records": records}
)
```
> See `reference/salesforce-patterns.md` for JWT setup, Platform Events, and bulk API.
</salesforce_patterns>
<webhook_patterns>
## Webhook Handlers
### Close Webhook (FastAPI)
```python
from fastapi import FastAPI, Request, HTTPException
import hmac, hashlib
app = FastAPI()
@app.post("/webhooks/close")
async def close_webhook(request: Request):
body = await request.body()
signature = request.headers.get("Close-Sig")
expected = hmac.new(
CLOSE_WEBHOOK_SECRET.encode(), body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
raise HTTPException(401, "Invalid signature")
data = await request.json()
event_type = data["event"]["event_type"]
handlers = {
"lead.created": handle_lead_created,
"opportunity.status_changed": handle_opp_stage_change,
}
if handler := handlers.get(event_type):
await handler(data["event"]["data"])
return {"status": "ok"}
```
### Close Webhook Events
```
lead.created, lead.updated, lead.deleted, lead.status_changed
contact.created, contact.updated
opportunity.created, opportunity.status_changed
activity.note.created, activity.call.created, activity.email.created
unsubscribed_email.created
```
</webhook_patterns>
<sync_architecture>
## Cross-CRM Sync
### Architecture
```
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Close │────▶│ Sync Layer │◀────│ HubSpot │
│ (Primary) │◀────│ (Postgres) │────▶│ (Marketing)│
└─────────────┘ └──────────────┘ └─────────────┘
```
### Sync Record Schema
```sql
CREATE TABLE crm_sync_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entity_type VARCHAR(50) NOT NULL,
close_id VARCHAR(100) UNIQUE,
hubspot_id VARCHAR(100) UNIQUE,
salesforce_id VARCHAR(100) UNIQUE,
email VARCHAR(255),
company_name VARCHAR(255),
last_synced_at TIMESTAMPTZ,
sync_source VARCHAR(50),
sync_hash VARCHAR(64)
);
CREATE INDEX idx_sync_email ON crm_sync_records(email);
```
### Conflict Resolution
```python
from enum import Enum
class ConflictStrategy(Enum):
CLOSE_WINS = "close" # Close is source of truth
LAST_WRITE_WINS = "lww" # Most recent update wins
def resolve_conflict(close_record, hubspot_record, strategy):
if strategy == ConflictStrategy.CLOSE_WINS:
merged = close_record.copy()
for key, value in hubspot_record.items():
if key not in merged or not merged[key]:
merged[key] = value
return merged
```
> See `reference/sync-patterns.md` for deduplication, migration scripts, and bulk sync.
</sync_architecture>
<file_locations>
## Reference Files
**CRM-Specific:**
- `reference/close-deep-dive.md` - Query language, Smart Views, sequences, reporting
- `reference/hubspot-patterns.md` - SDK patterns, batch operations, workflows
- `reference/salesforce-patterns.md` - JWT auth, SOQL, Platform Events, bulk API
**Operations:**
- `reference/sync-patterns.md` - Cross-CRM sync, deduplication, migration
- `reference/automation.md` - Webhook setup, sequences, workflows
**Templates:**
- `templates/close-client.py` - Full Close API client
- `templates/hubspot-client.py` - HubSpot SDK wrapper
- `templates/sync-service.py` - Cross-CRM sync service
</file_locations>
<routing>
## Request Routing
**User wants CRM integration:**
→ Ask which CRM (Close recommended for simplicity)
→ Provide auth setup + basic CRUD
**User wants Close CRM:**
→ Provide API key setup, query language
→ Reference: `reference/close-deep-dive.md`
**User wants HubSpot:**
→ Provide SDK setup, search patterns
→ Reference: `reference/hubspot-patterns.md`
**User wants Salesforce:**
→ Provide JWT auth, SOQL patterns
→ Reference: `reference/salesforce-patterns.md`
**User wants sync between CRMs:**
→ Provide sync architecture, conflict resolution
→ Reference: `reference/sync-patterns.md`
**User wants webhooks:**
→ Provide handler pattern for specified CRM
→ Include signature verification
</routing>
<env_setup>
## Environment Variables
```bash
# Close CRM
export CLOSE_API_KEY="api_xxx"
export CLOSE_WEBHOOK_SECRET="whsec_xxx"
# HubSpot
export HUBSPOT_ACCESS_TOKEN="pat-xxx"
# Salesforce
export SF_CLIENT_ID="xxx"
export SF_USERNAME="user@company.com"
export SF_PRIVATE_KEY_PATH="./salesforce.key"
export SF_INSTANCE_URL="https://yourorg.my.salesforce.com"
```
```
# Dependencies: httpx pyjwt hubspot-api-client python-dotenv
```
</env_setup>
<example_session>
## Example Session
**User:** "I need to push enriched leads from my sales-agent to Close CRM"
**Claude:**
1. Here's the pattern for pushing enriched data to Close:
```python
async def push_to_close(close_client, enriched_data: dict) -> str:
lead_data = {
"name": enriched_data["company_name"],
"url": enriched_data.get("website"),
"custom.cf_tier": enriched_data["tier"],
"custom.cf_source": "sales-agent",
"contacts": [{
"name": c["name"],
"title": c.get("title"),
"emails": [{"email": c["email"]}] if c.get("email") else []
} for c in enriched_data.get("contacts", [])]
}
result = close_client.create_lead(lead_data)
return result["id"]
```
2. Make sure you have these custom fields in Close:
- `cf_tier` (choices: Gold, Silver, Bronze)
- `cf_source` (choices: sales-agent, inbound, referral)
3. Rate limit: 100 requests per 10 seconds. Add `asyncio.sleep(0.1)` between requests for bulk imports.
</example_session>
More from manojbajaj95/claude-gtm-plugin
- ad-campaign-managementUse when creating, managing, or optimizing paid advertising campaigns across any platform. Covers ad copy generation, ad creative strategy, campaign management, and competitive intelligence. Triggers on: ad copy, ad creative, paid ads, paid advertising, Facebook ads, LinkedIn ads, Google ads, Meta ads, ad campaign, ad strategy, ad testing, competitive ads, competitor ads, ad library, ad analysis, ad creative generation, campaign management, ROAS, CPC, CTR. Use this skill whenever the user mentions ads, paid channels, ad campaigns, ad copy, ad performance, or wants to analyze competitor advertising.
- blog-writing-specialistComprehensive blog writing skill that handles technical blog posts, personal voice writing, brain dump transformation, and category-aware AEO-optimized content. Use when: (1) writing, editing, or proofreading a blog article or post, (2) transforming unstructured brain dumps into polished posts, (3) writing in specific personal voices (Jarad, Nick Nisi), (4) creating category-aware technology/company/product posts, (5) building tutorials, deep dives, postmortems, benchmarks, or architecture posts, (6) writing engineering blogs, dev blogs, programming blogs, coding tutorials, or documentation posts. Triggers: blog post, blog writing, technical blog, dev tutorial, brain dump, article, content writing, developer article, engineering blog, programming blog, coding tutorial, documentation post, technical writing, blog editing, proofreading, developer content
- brand-messaging-and-positioningComprehensive brand messaging, positioning, and value proposition development using proven frameworks including Peep Laja Message Layers, Osterwalder Value Proposition Canvas, Geoffrey Moore positioning, April Dunford's Five Components, StoryBrand SB7, Andy Raskin Strategic Narrative, and Messaging House. Use when developing brand identity, brand messaging architecture, positioning statements, value propositions, messaging hierarchies, brand pillars, taglines, one-liners, elevator pitches, brand guidelines, visual identity systems, or creating Positioning & Messaging Packs. Triggers include: messaging framework, brand positioning, value prop, messaging architecture, brand pillars, brand identity, StoryBrand, positioning statement, brand guidelines, design system, messaging house, corporate identity, brand voice, visual standards.
- challenge-funnelThis skill should be used when the user asks to "create a challenge funnel", "build a 5-day challenge", "bootcamp funnel", "challenge launch", or mentions challenges, bootcamps, or multi-day engagement funnels. Creates challenge funnels that activate prospects, build community, and convert to core offers.
- community-building|
- content-creation-and-marketingCross-channel content production from an approved idea, brief, notes, transcript, or existing asset. Use when the user wants a ready-to-review draft or a small set of adaptations saved into the bootstrap content workspace. For content strategy use content-strategy-and-planning; for specialist LinkedIn, blog, email automation, SEO, landing page, or campaign work use the dedicated skill.
- content-strategy-and-planningUnified skill for content strategy and marketing content strategy: content pillars, editorial calendars, keyword research by buyer stage, positioning, messaging hierarchy, trust-building, GEO/AI search optimization, and ROI measurement. Also handles content briefs, SEO briefs, content outlines for writers, on-page SEO optimization, meta descriptions, title tags, keyword density, content research, source discovery, expert sourcing, and information gathering. Use when planning content strategy, creating content briefs for writers, optimizing existing content for SEO, researching topics and sources, or managing editorial operations. Triggers: content strategy, marketing content strategy, content marketing strategy, content planning, editorial calendar, content pillars, messaging hierarchy, content brief, SEO brief, content outline, keyword research, buyer journey content, GEO optimization, AI search optimization, content ROI, content operations, content roadmap, brand messaging, positioning
- conversion-rate-optimizationConversion rate optimization for marketing pages and lead-capture forms. Use when the user wants to improve conversions on a homepage, landing page, pricing page, feature page, blog CTA, contact form, demo form, or campaign page. For product onboarding use user-onboarding; for lifecycle email use marketing-automation; for pricing and paywalls use pricing-strategy; for A/B testing use ab-test-setup.
- copywriting-coreExpert copywriter and copy editor combining David Ogilvy's clarity, Ann Handley's warmth, and modern conversion science. Full copy lifecycle: writing new copy and editing existing copy. Grounded in positioning-first thinking, voice-of-customer research, and the Seven Sweeps editing framework. Use when writing or editing any copy: copywriting, write copy, headlines, taglines, email copy, ad copy, landing page copy, product copy, UX writing, CTAs, value proposition, microcopy, sales copy, conversion copy, SaaS copy, startup copy, positioning, messaging, voice-of-customer, landing page, copy editing, edit copy, review copy, proofread, polish copy, tighten copy, copy sweep, copy feedback, sharpen messaging.
- customer-success-and-retentionComprehensive customer success and retention expertise combining onboarding, health scoring, churn prevention, retention strategies, and LTV maximization. Use when designing customer onboarding flows, reducing churn, building health scores, creating retention systems, designing upsells, or improving customer lifetime value. Covers proactive engagement, cancel flows, dunning, re-engagement, and the full customer lifecycle.