Merge pull request #70 from coreyhaines31/feature/new-connector-tools

Add 10 new CLI tools and integration guides
This commit is contained in:
Corey Haines 2026-03-04 12:51:35 -08:00 committed by GitHub
commit 89fd57a8b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 3648 additions and 4 deletions

View file

@ -167,7 +167,7 @@ This repository includes a tools registry for agent-compatible marketing tools.
- **Tool discovery**: Read `tools/REGISTRY.md` to see available tools and their capabilities - **Tool discovery**: Read `tools/REGISTRY.md` to see available tools and their capabilities
- **Integration details**: See `tools/integrations/{tool}.md` for API endpoints, auth, and common operations - **Integration details**: See `tools/integrations/{tool}.md` for API endpoints, auth, and common operations
- **MCP-enabled tools**: ga4, stripe, mailchimp, google-ads, resend, zapier - **MCP-enabled tools**: ga4, stripe, mailchimp, google-ads, resend, zapier, zoominfo, clay, supermetrics, coupler, outreach, crossbeam
### Registry Structure ### Registry Structure

View file

@ -28,8 +28,13 @@ Quick reference for AI agents to discover tool capabilities and integration meth
| keywords-everywhere | SEO | ✓ | - | [](clis/keywords-everywhere.js) | - | [keywords-everywhere.md](integrations/keywords-everywhere.md) | | keywords-everywhere | SEO | ✓ | - | [](clis/keywords-everywhere.js) | - | [keywords-everywhere.md](integrations/keywords-everywhere.md) |
| clearbit | Data Enrichment | ✓ | - | [](clis/clearbit.js) | ✓ | [clearbit.md](integrations/clearbit.md) | | clearbit | Data Enrichment | ✓ | - | [](clis/clearbit.js) | ✓ | [clearbit.md](integrations/clearbit.md) |
| apollo | Data Enrichment | ✓ | - | [](clis/apollo.js) | - | [apollo.md](integrations/apollo.md) | | apollo | Data Enrichment | ✓ | - | [](clis/apollo.js) | - | [apollo.md](integrations/apollo.md) |
| zoominfo | Data Enrichment | ✓ | ✓ | [](clis/zoominfo.js) | - | [zoominfo.md](integrations/zoominfo.md) |
| clay | Data Enrichment | ✓ | ✓ | [](clis/clay.js) | - | [clay.md](integrations/clay.md) |
| supermetrics | Data Aggregation | ✓ | ✓ | [](clis/supermetrics.js) | - | [supermetrics.md](integrations/supermetrics.md) |
| coupler | Data Aggregation | ✓ | ✓ | [](clis/coupler.js) | - | [coupler.md](integrations/coupler.md) |
| hubspot | CRM | ✓ | - | ✓ | ✓ | [hubspot.md](integrations/hubspot.md) | | hubspot | CRM | ✓ | - | ✓ | ✓ | [hubspot.md](integrations/hubspot.md) |
| salesforce | CRM | ✓ | - | ✓ | ✓ | [salesforce.md](integrations/salesforce.md) | | salesforce | CRM | ✓ | - | ✓ | ✓ | [salesforce.md](integrations/salesforce.md) |
| close | CRM | ✓ | - | [](clis/close.js) | - | [close.md](integrations/close.md) |
| stripe | Payments | ✓ | ✓ | ✓ | ✓ | [stripe.md](integrations/stripe.md) | | stripe | Payments | ✓ | ✓ | ✓ | ✓ | [stripe.md](integrations/stripe.md) |
| paddle | Payments | ✓ | - | [](clis/paddle.js) | ✓ | [paddle.md](integrations/paddle.md) | | paddle | Payments | ✓ | - | [](clis/paddle.js) | ✓ | [paddle.md](integrations/paddle.md) |
| rewardful | Referral | ✓ | - | [](clis/rewardful.js) | - | [rewardful.md](integrations/rewardful.md) | | rewardful | Referral | ✓ | - | [](clis/rewardful.js) | - | [rewardful.md](integrations/rewardful.md) |
@ -62,6 +67,11 @@ Quick reference for AI agents to discover tool capabilities and integration meth
| savvycal | Scheduling | ✓ | - | [](clis/savvycal.js) | - | [savvycal.md](integrations/savvycal.md) | | savvycal | Scheduling | ✓ | - | [](clis/savvycal.js) | - | [savvycal.md](integrations/savvycal.md) |
| typeform | Forms | ✓ | - | [](clis/typeform.js) | ✓ | [typeform.md](integrations/typeform.md) | | typeform | Forms | ✓ | - | [](clis/typeform.js) | ✓ | [typeform.md](integrations/typeform.md) |
| intercom | Messaging | ✓ | - | [](clis/intercom.js) | ✓ | [intercom.md](integrations/intercom.md) | | intercom | Messaging | ✓ | - | [](clis/intercom.js) | ✓ | [intercom.md](integrations/intercom.md) |
| outreach | Sales Engagement | ✓ | ✓ | [](clis/outreach.js) | - | [outreach.md](integrations/outreach.md) |
| crossbeam | Partner Ecosystem | ✓ | ✓ | [](clis/crossbeam.js) | - | [crossbeam.md](integrations/crossbeam.md) |
| pendo | Product Analytics | ✓ | - | [](clis/pendo.js) | - | [pendo.md](integrations/pendo.md) |
| similarweb | Competitive Intelligence | ✓ | - | [](clis/similarweb.js) | - | [similarweb.md](integrations/similarweb.md) |
| airops | AI Content | ✓ | - | [](clis/airops.js) | - | [airops.md](integrations/airops.md) |
| buffer | Social | ✓ | - | [](clis/buffer.js) | - | [buffer.md](integrations/buffer.md) | | buffer | Social | ✓ | - | [](clis/buffer.js) | - | [buffer.md](integrations/buffer.md) |
| wistia | Video | ✓ | - | [](clis/wistia.js) | - | [wistia.md](integrations/wistia.md) | | wistia | Video | ✓ | - | [](clis/wistia.js) | - | [wistia.md](integrations/wistia.md) |
| trustpilot | Reviews | ✓ | - | [](clis/trustpilot.js) | - | [trustpilot.md](integrations/trustpilot.md) | | trustpilot | Reviews | ✓ | - | [](clis/trustpilot.js) | - | [trustpilot.md](integrations/trustpilot.md) |
@ -115,8 +125,9 @@ Customer relationship management and sales tools.
|------|----------|:-------------:| |------|----------|:-------------:|
| **hubspot** | SMB, marketing + sales alignment | ✓ | | **hubspot** | SMB, marketing + sales alignment | ✓ |
| **salesforce** | Enterprise, complex sales processes | ✓ | | **salesforce** | Enterprise, complex sales processes | ✓ |
| **close** | SMB, high-velocity sales | [](clis/close.js) |
**Agent recommendation**: HubSpot for startups/SMBs, Salesforce for enterprise. **Agent recommendation**: HubSpot for startups/SMBs. Close for high-velocity inside sales. Salesforce for enterprise.
### Payments ### Payments
@ -125,7 +136,6 @@ Payment processing and subscription management.
| Tool | Best For | MCP Available | | Tool | Best For | MCP Available |
|------|----------|:-------------:| |------|----------|:-------------:|
| **stripe** | SaaS subscriptions, developer-friendly | ✓ | | **stripe** | SaaS subscriptions, developer-friendly | ✓ |
| **paddle** | SaaS billing with tax handling | - | | **paddle** | SaaS billing with tax handling | - |
**Agent recommendation**: Stripe is the default for SaaS. Paddle for built-in tax compliance. **Agent recommendation**: Stripe is the default for SaaS. Paddle for built-in tax compliance.
@ -256,8 +266,10 @@ Company and person data enrichment for sales and marketing.
|------|----------|-------| |------|----------|-------|
| **clearbit** | Company/person enrichment | Now HubSpot Breeze | | **clearbit** | Company/person enrichment | Now HubSpot Breeze |
| **apollo** | B2B prospecting, email finding | Large database | | **apollo** | B2B prospecting, email finding | Large database |
| **zoominfo** | B2B contacts, intent data | Enterprise-grade |
| **clay** | Waterfall enrichment, outbound | 75+ data providers |
**Agent recommendation**: Clearbit for enrichment. Apollo for prospecting and outbound. **Agent recommendation**: Clearbit for enrichment. Apollo for prospecting and outbound. ZoomInfo for enterprise B2B data with intent signals. Clay for waterfall enrichment across multiple providers.
### Reviews ### Reviews
@ -291,6 +303,56 @@ Webinar and virtual event platforms.
**Agent recommendation**: Demio for marketing-focused webinars. Livestorm for full event engagement. **Agent recommendation**: Demio for marketing-focused webinars. Livestorm for full event engagement.
### Sales Engagement
Sales engagement and outreach automation platforms.
| Tool | Best For | Notes |
|------|----------|-------|
| **outreach** | Enterprise sales engagement | Sequences, tasks, analytics |
**Agent recommendation**: Outreach for enterprise sales teams managing multi-touch sequences at scale.
### Product Analytics
Product analytics, feature adoption tracking, and in-app guidance.
| Tool | Best For | Notes |
|------|----------|-------|
| **pendo** | Feature adoption, in-app guides | Product-led growth |
**Agent recommendation**: Pendo for tracking feature adoption and delivering targeted in-app guidance.
### Competitive Intelligence
Traffic analytics, competitor benchmarking, and market research.
| Tool | Best For | Notes |
|------|----------|-------|
| **similarweb** | Website traffic, competitor analysis | Traffic sources, keywords |
**Agent recommendation**: Similarweb for competitor traffic analysis and market benchmarking.
### AI Content
AI-powered content generation and optimization platforms.
| Tool | Best For | Notes |
|------|----------|-------|
| **airops** | AI content workflows, SEO content | Flow-based automation |
**Agent recommendation**: AirOps for building AI content workflows that generate SEO-optimized content at scale.
### Partner Ecosystem
Partner data sharing, co-sell, and ecosystem management.
| Tool | Best For | Notes |
|------|----------|-------|
| **crossbeam** | Account overlaps, co-sell | Now part of Reveal |
**Agent recommendation**: Crossbeam for identifying partner account overlaps and co-sell opportunities.
### Email Outreach ### Email Outreach
Cold email outreach and email finding tools for link building and sales prospecting. Cold email outreach and email finding tools for link building and sales prospecting.
@ -304,6 +366,17 @@ Cold email outreach and email finding tools for link building and sales prospect
**Agent recommendation**: Hunter for finding emails. Lemlist or Instantly for sending cold email campaigns. Snov for combined finding + outreach. **Agent recommendation**: Hunter for finding emails. Lemlist or Instantly for sending cold email campaigns. Snov for combined finding + outreach.
### Data Aggregation
Marketing data pipeline tools that connect multiple platforms for unified reporting.
| Tool | Best For | Notes |
|------|----------|-------|
| **supermetrics** | Cross-platform data pulling | 200+ connectors |
| **coupler** | Automated data flows to sheets/BI | Scheduled pipelines |
**Agent recommendation**: Supermetrics for pulling data from multiple marketing platforms into unified reports. Coupler.io for automated data flows to spreadsheets and BI tools.
### Commerce & CMS ### Commerce & CMS
E-commerce platforms and content management systems. E-commerce platforms and content management systems.
@ -340,6 +413,12 @@ These tools have Model Context Protocol servers available, enabling direct agent
- **google-ads** - Ad campaign management - **google-ads** - Ad campaign management
- **resend** - Transactional email sending - **resend** - Transactional email sending
- **zapier** - Workflow automation - **zapier** - Workflow automation
- **zoominfo** - B2B contacts and intent data
- **clay** - Data enrichment and outbound automation
- **supermetrics** - Cross-platform marketing data
- **coupler** - Marketing data pipelines
- **outreach** - Sales engagement sequences
- **crossbeam** - Partner ecosystem data
To use MCP tools, ensure the appropriate MCP server is configured in your environment. To use MCP tools, ensure the appropriate MCP server is configured in your environment.

163
tools/clis/airops.js Executable file
View file

@ -0,0 +1,163 @@
#!/usr/bin/env node
const API_KEY = process.env.AIROPS_API_KEY
const WORKSPACE_ID = process.env.AIROPS_WORKSPACE_ID
const BASE_URL = 'https://api.airops.com/public_api/v1'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'AIROPS_API_KEY environment variable required' }))
process.exit(1)
}
if (!WORKSPACE_ID) {
console.error(JSON.stringify({ error: 'AIROPS_WORKSPACE_ID environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
const url = `${BASE_URL}${path}`
if (args['dry-run']) {
return { _dry_run: true, method, url, headers: { 'Authorization': 'Bearer ***', 'Content-Type': 'application/json' }, body: body || undefined }
}
const res = await fetch(url, {
method,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'flows':
switch (sub) {
case 'list': {
result = await api('GET', `/workspaces/${WORKSPACE_ID}/flows`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/workspaces/${WORKSPACE_ID}/flows/${id}`)
break
}
case 'execute': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
const inputs = args.inputs
let parsedInputs = {}
if (inputs) {
try {
parsedInputs = JSON.parse(inputs)
} catch {
result = { error: '--inputs must be valid JSON' }
break
}
}
result = await api('POST', `/workspaces/${WORKSPACE_ID}/flows/${id}/execute`, { inputs: parsedInputs })
break
}
case 'runs': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/workspaces/${WORKSPACE_ID}/flows/${id}/runs`)
break
}
case 'run-status': {
const runId = args['run-id']
if (!runId) { result = { error: '--run-id required' }; break }
result = await api('GET', `/workspaces/${WORKSPACE_ID}/runs/${runId}`)
break
}
default:
result = { error: 'Unknown flows subcommand. Use: list, get, execute, runs, run-status' }
}
break
case 'workflows':
switch (sub) {
case 'list': {
result = await api('GET', `/workspaces/${WORKSPACE_ID}/workflows`)
break
}
case 'execute': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
const inputs = args.inputs
let parsedInputs = {}
if (inputs) {
try {
parsedInputs = JSON.parse(inputs)
} catch {
result = { error: '--inputs must be valid JSON' }
break
}
}
result = await api('POST', `/workspaces/${WORKSPACE_ID}/workflows/${id}/execute`, { inputs: parsedInputs })
break
}
default:
result = { error: 'Unknown workflows subcommand. Use: list, execute' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
flows: {
list: 'flows list',
get: 'flows get --id <id>',
execute: 'flows execute --id <id> --inputs <json>',
runs: 'flows runs --id <id>',
'run-status': 'flows run-status --run-id <id>',
},
workflows: {
list: 'workflows list',
execute: 'workflows execute --id <id> --inputs <json>',
},
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

159
tools/clis/clay.js Executable file
View file

@ -0,0 +1,159 @@
#!/usr/bin/env node
const API_KEY = process.env.CLAY_API_KEY
const BASE_URL = 'https://api.clay.com/v3'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'CLAY_API_KEY environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
if (args['dry-run']) {
return { _dry_run: true, method, url: `${BASE_URL}${path}`, headers: { 'Authorization': 'Bearer ***', 'Content-Type': 'application/json' }, body: body || undefined }
}
const options = {
method,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
}
if (body) options.body = JSON.stringify(body)
const res = await fetch(`${BASE_URL}${path}`, options)
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
const page = args.page ? Number(args.page) : 1
const perPage = args['per-page'] ? Number(args['per-page']) : 25
switch (cmd) {
case 'tables':
switch (sub) {
case 'list': {
result = await api('GET', `/tables?page=${page}&per_page=${perPage}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/tables/${id}`)
break
}
case 'rows': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/tables/${id}/rows?page=${page}&per_page=${perPage}`)
break
}
case 'add-row': {
const id = args.id
const data = args.data
if (!id) { result = { error: '--id required' }; break }
if (!data) { result = { error: '--data required (JSON string)' }; break }
let parsed
try {
parsed = JSON.parse(data)
} catch {
result = { error: 'Invalid JSON in --data' }
break
}
result = await api('POST', `/tables/${id}/rows`, parsed)
break
}
default:
result = { error: 'Unknown tables subcommand. Use: list, get, rows, add-row' }
}
break
case 'people':
switch (sub) {
case 'enrich': {
const body = {}
if (args.email) body.email = args.email
if (args.linkedin) body.linkedin_url = args.linkedin
if (args['first-name']) body.first_name = args['first-name']
if (args['last-name']) body.last_name = args['last-name']
if (args['first-name'] && args['last-name'] && args.domain) body.domain = args.domain
if (!args.email && !args.linkedin && !(args['first-name'] && args['last-name'] && args.domain)) {
result = { error: '--email or --linkedin required (or --first-name + --last-name + --domain)' }
break
}
result = await api('POST', '/people/enrich', body)
break
}
default:
result = { error: 'Unknown people subcommand. Use: enrich' }
}
break
case 'companies':
switch (sub) {
case 'enrich': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
result = await api('POST', '/companies/enrich', { domain })
break
}
default:
result = { error: 'Unknown companies subcommand. Use: enrich' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
tables: {
list: 'tables list [--page <n>] [--per-page <n>]',
get: 'tables get --id <table_id>',
rows: 'tables rows --id <table_id> [--page <n>] [--per-page <n>]',
'add-row': 'tables add-row --id <table_id> --data <json>',
},
people: {
enrich: 'people enrich --email <email> | --linkedin <url> | --first-name <n> --last-name <n> --domain <d>',
},
companies: {
enrich: 'companies enrich --domain <domain>',
},
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

232
tools/clis/close.js Executable file
View file

@ -0,0 +1,232 @@
#!/usr/bin/env node
const API_KEY = process.env.CLOSE_API_KEY
const BASE_URL = 'https://api.close.com/api/v1'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'CLOSE_API_KEY environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
const url = `${BASE_URL}${path}`
if (args['dry-run']) {
return { _dry_run: true, method, url, headers: { 'Content-Type': 'application/json', 'Authorization': 'Basic ***' }, body: body || undefined }
}
const res = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Basic ${btoa(API_KEY + ':')}`,
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'leads':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args.query) params.set('query', args.query)
if (args.page) params.set('_skip', (parseInt(args.page) - 1) * 100)
const qs = params.toString()
result = await api('GET', `/lead/${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/lead/${id}/`)
break
}
case 'create': {
const name = args.name
if (!name) { result = { error: '--name required' }; break }
const body = { name }
if (args.url) body.url = args.url
if (args.description) body.description = args.description
result = await api('POST', '/lead/', body)
break
}
default:
result = { error: 'Unknown leads subcommand. Use: list, get, create' }
}
break
case 'contacts':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args['lead-id']) params.set('lead_id', args['lead-id'])
const qs = params.toString()
result = await api('GET', `/contact/${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/contact/${id}/`)
break
}
case 'create': {
const leadId = args['lead-id']
const name = args.name
if (!leadId) { result = { error: '--lead-id required' }; break }
if (!name) { result = { error: '--name required' }; break }
const body = { lead_id: leadId, name }
if (args.email) {
body.emails = [{ email: args.email, type: 'office' }]
}
if (args.phone) {
body.phones = [{ phone: args.phone, type: 'office' }]
}
result = await api('POST', '/contact/', body)
break
}
default:
result = { error: 'Unknown contacts subcommand. Use: list, get, create' }
}
break
case 'opportunities':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args.status) params.set('status', args.status)
const qs = params.toString()
result = await api('GET', `/opportunity/${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/opportunity/${id}/`)
break
}
case 'create': {
const leadId = args['lead-id']
const value = args.value
if (!leadId) { result = { error: '--lead-id required' }; break }
if (!value) { result = { error: '--value required (in cents)' }; break }
const body = { lead_id: leadId, value: parseInt(value) }
if (args.status) body.status_type = args.status
result = await api('POST', '/opportunity/', body)
break
}
default:
result = { error: 'Unknown opportunities subcommand. Use: list, get, create' }
}
break
case 'activities':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args['lead-id']) params.set('lead_id', args['lead-id'])
if (args.type) params.set('_type__type', args.type)
const qs = params.toString()
result = await api('GET', `/activity/${qs ? '?' + qs : ''}`)
break
}
default:
result = { error: 'Unknown activities subcommand. Use: list' }
}
break
case 'tasks':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args['assigned-to']) params.set('assigned_to', args['assigned-to'])
if (args['is-complete']) params.set('is_complete', args['is-complete'])
const qs = params.toString()
result = await api('GET', `/task/${qs ? '?' + qs : ''}`)
break
}
case 'create': {
const leadId = args['lead-id']
const text = args.text
if (!leadId) { result = { error: '--lead-id required' }; break }
if (!text) { result = { error: '--text required' }; break }
const body = { lead_id: leadId, text, _type: 'lead' }
if (args['assigned-to']) body.assigned_to = args['assigned-to']
if (args.date) body.date = args.date
result = await api('POST', '/task/', body)
break
}
default:
result = { error: 'Unknown tasks subcommand. Use: list, create' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
leads: {
list: 'leads list [--query <q>] [--page <n>]',
get: 'leads get --id <id>',
create: 'leads create --name <name> [--url <url>] [--description <desc>]',
},
contacts: {
list: 'contacts list [--lead-id <id>]',
get: 'contacts get --id <id>',
create: 'contacts create --lead-id <id> --name <name> [--email <email>] [--phone <phone>]',
},
opportunities: {
list: 'opportunities list [--status <status>]',
get: 'opportunities get --id <id>',
create: 'opportunities create --lead-id <id> --value <cents> [--status <status>]',
},
activities: {
list: 'activities list [--lead-id <id>] [--type <type>]',
},
tasks: {
list: 'tasks list [--assigned-to <user-id>] [--is-complete <bool>]',
create: 'tasks create --lead-id <id> --text <text> [--assigned-to <user-id>] [--date <date>]',
},
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

173
tools/clis/coupler.js Executable file
View file

@ -0,0 +1,173 @@
#!/usr/bin/env node
const API_KEY = process.env.COUPLER_API_KEY
const BASE_URL = 'https://api.coupler.io/v1'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'COUPLER_API_KEY environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
if (args['dry-run']) {
return { _dry_run: true, method, url: `${BASE_URL}${path}`, headers: { 'Authorization': 'Bearer ***', 'Content-Type': 'application/json' }, body: body || undefined }
}
const res = await fetch(`${BASE_URL}${path}`, {
method,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'importers':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args.limit) params.set('limit', args.limit)
if (args.offset) params.set('offset', args.offset)
const qs = params.toString()
result = await api('GET', `/importers${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/importers/${id}`)
break
}
case 'run': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('POST', `/importers/${id}/run`)
break
}
case 'create': {
const source = args.source
const destination = args.destination
const name = args.name
if (!source) { result = { error: '--source required' }; break }
if (!destination) { result = { error: '--destination required' }; break }
if (!name) { result = { error: '--name required' }; break }
const body = { source_type: source, destination_type: destination, name }
if (args.schedule) body.schedule = args.schedule
result = await api('POST', '/importers', body)
break
}
case 'delete': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('DELETE', `/importers/${id}`)
break
}
default:
result = { error: 'Unknown importers subcommand. Use: list, get, run, create, delete' }
}
break
case 'runs':
switch (sub) {
case 'list': {
const importerId = args['importer-id']
if (!importerId) { result = { error: '--importer-id required' }; break }
const params = new URLSearchParams()
if (args.limit) params.set('limit', args.limit)
if (args.offset) params.set('offset', args.offset)
const qs = params.toString()
result = await api('GET', `/importers/${importerId}/runs${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/runs/${id}`)
break
}
default:
result = { error: 'Unknown runs subcommand. Use: list, get' }
}
break
case 'sources':
switch (sub) {
case 'list':
result = await api('GET', '/sources')
break
default:
result = { error: 'Unknown sources subcommand. Use: list' }
}
break
case 'destinations':
switch (sub) {
case 'list':
result = await api('GET', '/destinations')
break
default:
result = { error: 'Unknown destinations subcommand. Use: list' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
importers: {
list: 'importers list [--limit <n>] [--offset <n>]',
get: 'importers get --id <id>',
run: 'importers run --id <id>',
create: 'importers create --source <source-type> --destination <dest-type> --name <name> [--schedule <schedule>]',
delete: 'importers delete --id <id>',
},
runs: {
list: 'runs list --importer-id <id> [--limit <n>] [--offset <n>]',
get: 'runs get --id <id>',
},
sources: 'sources list',
destinations: 'destinations list',
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

193
tools/clis/crossbeam.js Executable file
View file

@ -0,0 +1,193 @@
#!/usr/bin/env node
const API_KEY = process.env.CROSSBEAM_API_KEY
const BASE_URL = 'https://api.crossbeam.com/v1'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'CROSSBEAM_API_KEY environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
const url = `${BASE_URL}${path}`
if (args['dry-run']) {
return { _dry_run: true, method, url, headers: { 'Authorization': 'Bearer ***', 'Content-Type': 'application/json' }, body: body || undefined }
}
const res = await fetch(url, {
method,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'partners':
switch (sub) {
case 'list': {
result = await api('GET', '/partners')
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/partners/${id}`)
break
}
default:
result = { error: 'Unknown partners subcommand. Use: list, get' }
}
break
case 'populations':
switch (sub) {
case 'list': {
result = await api('GET', '/populations')
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/populations/${id}`)
break
}
default:
result = { error: 'Unknown populations subcommand. Use: list, get' }
}
break
case 'overlaps':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args['partner-id']) params.set('partner_id', args['partner-id'])
if (args['population-id']) params.set('population_id', args['population-id'])
const qs = params.toString()
result = await api('GET', `/overlaps${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/overlaps/${id}`)
break
}
default:
result = { error: 'Unknown overlaps subcommand. Use: list, get' }
}
break
case 'reports':
switch (sub) {
case 'list': {
result = await api('GET', '/reports')
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/reports/${id}`)
break
}
default:
result = { error: 'Unknown reports subcommand. Use: list, get' }
}
break
case 'threads':
switch (sub) {
case 'list': {
result = await api('GET', '/threads')
break
}
default:
result = { error: 'Unknown threads subcommand. Use: list' }
}
break
case 'accounts':
switch (sub) {
case 'search': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
const params = new URLSearchParams({ domain })
result = await api('GET', `/accounts/search?${params.toString()}`)
break
}
default:
result = { error: 'Unknown accounts subcommand. Use: search' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
partners: {
list: 'partners list',
get: 'partners get --id <id>',
},
populations: {
list: 'populations list',
get: 'populations get --id <id>',
},
overlaps: {
list: 'overlaps list [--partner-id <id>] [--population-id <id>]',
get: 'overlaps get --id <id>',
},
reports: {
list: 'reports list',
get: 'reports get --id <id>',
},
threads: {
list: 'threads list',
},
accounts: {
search: 'accounts search --domain <domain>',
},
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

213
tools/clis/outreach.js Executable file
View file

@ -0,0 +1,213 @@
#!/usr/bin/env node
const ACCESS_TOKEN = process.env.OUTREACH_ACCESS_TOKEN
const BASE_URL = 'https://api.outreach.io/api/v2'
if (!ACCESS_TOKEN) {
console.error(JSON.stringify({ error: 'OUTREACH_ACCESS_TOKEN environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
const url = `${BASE_URL}${path}`
if (args['dry-run']) {
return { _dry_run: true, method, url, headers: { 'Authorization': 'Bearer ***', 'Content-Type': 'application/vnd.api+json', 'Accept': 'application/vnd.api+json' }, body: body || undefined }
}
const res = await fetch(url, {
method,
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/vnd.api+json',
'Accept': 'application/vnd.api+json',
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'prospects':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args.page) params.set('page[number]', args.page)
if (args['per-page']) params.set('page[size]', args['per-page'])
const qs = params.toString()
result = await api('GET', `/prospects${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/prospects/${id}`)
break
}
case 'create': {
const email = args.email
if (!email) { result = { error: '--email required' }; break }
const attributes = { emails: [email] }
if (args['first-name']) attributes.firstName = args['first-name']
if (args['last-name']) attributes.lastName = args['last-name']
const body = { data: { type: 'prospect', attributes } }
result = await api('POST', '/prospects', body)
break
}
default:
result = { error: 'Unknown prospects subcommand. Use: list, get, create' }
}
break
case 'sequences':
switch (sub) {
case 'list': {
result = await api('GET', '/sequences')
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/sequences/${id}`)
break
}
default:
result = { error: 'Unknown sequences subcommand. Use: list, get' }
}
break
case 'sequence-states':
switch (sub) {
case 'create': {
const sequenceId = args['sequence-id']
const prospectId = args['prospect-id']
if (!sequenceId) { result = { error: '--sequence-id required' }; break }
if (!prospectId) { result = { error: '--prospect-id required' }; break }
const body = {
data: {
type: 'sequenceState',
relationships: {
prospect: { data: { type: 'prospect', id: prospectId } },
sequence: { data: { type: 'sequence', id: sequenceId } },
},
},
}
result = await api('POST', '/sequenceStates', body)
break
}
default:
result = { error: 'Unknown sequence-states subcommand. Use: create' }
}
break
case 'mailings':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args['sequence-id']) params.set('filter[sequence][id]', args['sequence-id'])
const qs = params.toString()
result = await api('GET', `/mailings${qs ? '?' + qs : ''}`)
break
}
default:
result = { error: 'Unknown mailings subcommand. Use: list' }
}
break
case 'accounts':
switch (sub) {
case 'list': {
result = await api('GET', '/accounts')
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/accounts/${id}`)
break
}
default:
result = { error: 'Unknown accounts subcommand. Use: list, get' }
}
break
case 'tasks':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args.status) params.set('filter[status]', args.status)
const qs = params.toString()
result = await api('GET', `/tasks${qs ? '?' + qs : ''}`)
break
}
default:
result = { error: 'Unknown tasks subcommand. Use: list' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
prospects: {
list: 'prospects list [--page <n>] [--per-page <n>]',
get: 'prospects get --id <id>',
create: 'prospects create --email <email> [--first-name <name>] [--last-name <name>]',
},
sequences: {
list: 'sequences list',
get: 'sequences get --id <id>',
},
'sequence-states': {
create: 'sequence-states create --sequence-id <id> --prospect-id <id>',
},
mailings: {
list: 'mailings list [--sequence-id <id>]',
},
accounts: {
list: 'accounts list',
get: 'accounts get --id <id>',
},
tasks: {
list: 'tasks list [--status <status>]',
},
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

221
tools/clis/pendo.js Executable file
View file

@ -0,0 +1,221 @@
#!/usr/bin/env node
const API_KEY = process.env.PENDO_INTEGRATION_KEY
const BASE_URL = 'https://app.pendo.io/api/v1'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'PENDO_INTEGRATION_KEY environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
const url = `${BASE_URL}${path}`
if (args['dry-run']) {
return { _dry_run: true, method, url, headers: { 'Content-Type': 'application/json', 'x-pendo-integration-key': '***' }, body: body || undefined }
}
const res = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'x-pendo-integration-key': API_KEY,
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'features':
switch (sub) {
case 'list':
result = await api('GET', '/feature')
break
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/feature/${id}`)
break
}
default:
result = { error: 'Unknown features subcommand. Use: list, get' }
}
break
case 'pages':
switch (sub) {
case 'list':
result = await api('GET', '/page')
break
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/page/${id}`)
break
}
default:
result = { error: 'Unknown pages subcommand. Use: list, get' }
}
break
case 'guides':
switch (sub) {
case 'list': {
const params = new URLSearchParams()
if (args.state) params.set('state', args.state)
const qs = params.toString()
result = await api('GET', `/guide${qs ? '?' + qs : ''}`)
break
}
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/guide/${id}`)
break
}
default:
result = { error: 'Unknown guides subcommand. Use: list, get' }
}
break
case 'visitors':
switch (sub) {
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/visitor/${id}`)
break
}
case 'search': {
const query = args.query
if (!query) { result = { error: '--query <json> required' }; break }
let body
try { body = JSON.parse(query) } catch { result = { error: 'Invalid JSON in --query' }; break }
result = await api('POST', '/aggregation', body)
break
}
default:
result = { error: 'Unknown visitors subcommand. Use: get, search' }
}
break
case 'accounts':
switch (sub) {
case 'get': {
const id = args.id
if (!id) { result = { error: '--id required' }; break }
result = await api('GET', `/account/${id}`)
break
}
case 'search': {
const query = args.query
if (!query) { result = { error: '--query <json> required' }; break }
let body
try { body = JSON.parse(query) } catch { result = { error: 'Invalid JSON in --query' }; break }
result = await api('POST', '/aggregation', body)
break
}
default:
result = { error: 'Unknown accounts subcommand. Use: get, search' }
}
break
case 'reports':
switch (sub) {
case 'funnel': {
const pipeline = args.pipeline
if (!pipeline) { result = { error: '--pipeline <json> required' }; break }
let body
try { body = JSON.parse(pipeline) } catch { result = { error: 'Invalid JSON in --pipeline' }; break }
result = await api('POST', '/aggregation', body)
break
}
default:
result = { error: 'Unknown reports subcommand. Use: funnel' }
}
break
case 'metadata':
switch (sub) {
case 'list': {
const kind = args.kind
if (!kind) { result = { error: '--kind <visitor|account|parentAccount> required' }; break }
result = await api('GET', `/metadata/schema/${kind}`)
break
}
default:
result = { error: 'Unknown metadata subcommand. Use: list' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
features: {
list: 'features list',
get: 'features get --id <id>',
},
pages: {
list: 'pages list',
get: 'pages get --id <id>',
},
guides: {
list: 'guides list [--state <state>]',
get: 'guides get --id <id>',
},
visitors: {
get: 'visitors get --id <id>',
search: 'visitors search --query <json>',
},
accounts: {
get: 'accounts get --id <id>',
search: 'accounts search --query <json>',
},
reports: {
funnel: 'reports funnel --pipeline <json>',
},
metadata: {
list: 'metadata list --kind <visitor|account|parentAccount>',
},
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

214
tools/clis/similarweb.js Executable file
View file

@ -0,0 +1,214 @@
#!/usr/bin/env node
const API_KEY = process.env.SIMILARWEB_API_KEY
const BASE_URL = 'https://api.similarweb.com/v1'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'SIMILARWEB_API_KEY environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
const separator = path.includes('?') ? '&' : '?'
const url = `${BASE_URL}${path}${separator}api_key=${API_KEY}`
if (args['dry-run']) {
return { _dry_run: true, method, url: url.replace(API_KEY, '***'), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: body || undefined }
}
const res = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'traffic':
switch (sub) {
case 'visits': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
if (args.granularity) params.set('granularity', args.granularity)
result = await api('GET', `/website/${encodeURIComponent(domain)}/total-traffic-and-engagement/visits?${params.toString()}`)
break
}
case 'pages-per-visit': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
if (args.granularity) params.set('granularity', args.granularity)
result = await api('GET', `/website/${encodeURIComponent(domain)}/total-traffic-and-engagement/pages-per-visit?${params.toString()}`)
break
}
case 'avg-duration': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
if (args.granularity) params.set('granularity', args.granularity)
result = await api('GET', `/website/${encodeURIComponent(domain)}/total-traffic-and-engagement/average-visit-duration?${params.toString()}`)
break
}
case 'bounce-rate': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
if (args.granularity) params.set('granularity', args.granularity)
result = await api('GET', `/website/${encodeURIComponent(domain)}/total-traffic-and-engagement/bounce-rate?${params.toString()}`)
break
}
case 'sources': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
result = await api('GET', `/website/${encodeURIComponent(domain)}/traffic-sources/overview?${params.toString()}`)
break
}
default:
result = { error: 'Unknown traffic subcommand. Use: visits, pages-per-visit, avg-duration, bounce-rate, sources' }
}
break
case 'referrals': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
result = await api('GET', `/website/${encodeURIComponent(domain)}/traffic-sources/referrals?${params.toString()}`)
break
}
case 'search':
switch (sub) {
case 'keywords-organic': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
if (args.limit) params.set('limit', args.limit)
result = await api('GET', `/website/${encodeURIComponent(domain)}/search/organic-search-keywords?${params.toString()}`)
break
}
case 'keywords-paid': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
if (args.country) params.set('country', args.country)
if (args.limit) params.set('limit', args.limit)
result = await api('GET', `/website/${encodeURIComponent(domain)}/search/paid-search-keywords?${params.toString()}`)
break
}
default:
result = { error: 'Unknown search subcommand. Use: keywords-organic, keywords-paid' }
}
break
case 'competitors': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
result = await api('GET', `/website/${encodeURIComponent(domain)}/similar-sites/similarsites`)
break
}
case 'category-rank': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
result = await api('GET', `/website/${encodeURIComponent(domain)}/category-rank/category-rank`)
break
}
case 'geography': {
const domain = args.domain
if (!domain) { result = { error: '--domain required' }; break }
if (!args.start) { result = { error: '--start required (YYYY-MM)' }; break }
if (!args.end) { result = { error: '--end required (YYYY-MM)' }; break }
const params = new URLSearchParams({ start_date: args.start, end_date: args.end })
result = await api('GET', `/website/${encodeURIComponent(domain)}/geo/traffic-by-country?${params.toString()}`)
break
}
default:
result = {
error: 'Unknown command',
usage: {
traffic: {
visits: 'traffic visits --domain <domain> --start <YYYY-MM> --end <YYYY-MM> [--country <cc>] [--granularity <monthly|weekly|daily>]',
'pages-per-visit': 'traffic pages-per-visit --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
'avg-duration': 'traffic avg-duration --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
'bounce-rate': 'traffic bounce-rate --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
sources: 'traffic sources --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
},
referrals: 'referrals --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
search: {
'keywords-organic': 'search keywords-organic --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
'keywords-paid': 'search keywords-paid --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
},
competitors: 'competitors --domain <domain>',
'category-rank': 'category-rank --domain <domain>',
geography: 'geography --domain <domain> --start <YYYY-MM> --end <YYYY-MM>',
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

168
tools/clis/supermetrics.js Executable file
View file

@ -0,0 +1,168 @@
#!/usr/bin/env node
const API_KEY = process.env.SUPERMETRICS_API_KEY
const BASE_URL = 'https://api.supermetrics.com/enterprise/v2'
if (!API_KEY) {
console.error(JSON.stringify({ error: 'SUPERMETRICS_API_KEY environment variable required' }))
process.exit(1)
}
async function api(method, path, body) {
const separator = path.includes('?') ? '&' : '?'
const url = `${BASE_URL}${path}${separator}api_key=${API_KEY}`
if (args['dry-run']) {
return { _dry_run: true, method, url: url.replace(API_KEY, '***'), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: body || undefined }
}
const res = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
switch (cmd) {
case 'query': {
const dsId = args['ds-id']
const dsAccounts = args['ds-accounts']
const dateRange = args['date-range']
const fields = args.fields
if (!dsId) { result = { error: '--ds-id required (e.g., GA4, AW, FB, LI, GSC)' }; break }
if (!dsAccounts) { result = { error: '--ds-accounts required' }; break }
if (!dateRange) { result = { error: '--date-range required (e.g., last_28_days, last_month, this_month, custom)' }; break }
if (!fields) { result = { error: '--fields required (comma-separated field names)' }; break }
const body = {
ds_id: dsId,
ds_accounts: dsAccounts,
date_range_type: dateRange,
fields: fields.split(',').map(f => ({ name: f.trim() })),
}
if (args.filter) body.filter = args.filter
if (args['max-rows']) body.max_rows = parseInt(args['max-rows'], 10)
if (args['start-date']) body.start_date = args['start-date']
if (args['end-date']) body.end_date = args['end-date']
result = await api('POST', '/query/data/json', body)
break
}
case 'sources':
switch (sub) {
case 'list':
result = await api('GET', '/datasources')
break
default:
result = { error: 'Unknown sources subcommand. Use: list' }
}
break
case 'accounts':
switch (sub) {
case 'list': {
const dsId = args['ds-id']
if (!dsId) { result = { error: '--ds-id required (e.g., GA4, AW, FB)' }; break }
const params = new URLSearchParams({ ds_id: dsId })
result = await api('GET', `/datasources/accounts?${params.toString()}`)
break
}
default:
result = { error: 'Unknown accounts subcommand. Use: list' }
}
break
case 'teams':
switch (sub) {
case 'list':
result = await api('GET', '/teams')
break
default:
result = { error: 'Unknown teams subcommand. Use: list' }
}
break
case 'users':
switch (sub) {
case 'list':
result = await api('GET', '/users')
break
default:
result = { error: 'Unknown users subcommand. Use: list' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
query: 'query --ds-id <data-source> --ds-accounts <account-id> --date-range <range> --fields <f1,f2> [--filter <filter>] [--max-rows <n>] [--start-date <date>] [--end-date <date>]',
sources: {
list: 'sources list',
},
accounts: {
list: 'accounts list --ds-id <data-source>',
},
teams: {
list: 'teams list',
},
users: {
list: 'users list',
},
'data-source-ids': {
'GA4': 'Google Analytics 4',
'GA4_PAID': 'Google Analytics (paid)',
'AW': 'Google Ads',
'FB': 'Facebook Ads',
'LI': 'LinkedIn Ads',
'TW_ADS': 'Twitter Ads',
'IG_IA': 'Instagram',
'FB_IA': 'Facebook Pages',
'GSC': 'Google Search Console',
'SE': 'Semrush',
'MC': 'Mailchimp',
},
'date-ranges': ['last_28_days', 'last_month', 'this_month', 'custom'],
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

216
tools/clis/zoominfo.js Executable file
View file

@ -0,0 +1,216 @@
#!/usr/bin/env node
const BASE_URL = 'https://api.zoominfo.com'
let ACCESS_TOKEN = process.env.ZOOMINFO_ACCESS_TOKEN
if (!ACCESS_TOKEN && !process.env.ZOOMINFO_USERNAME) {
console.error(JSON.stringify({ error: 'ZOOMINFO_ACCESS_TOKEN or ZOOMINFO_USERNAME + ZOOMINFO_PRIVATE_KEY environment variables required' }))
process.exit(1)
}
async function authenticate() {
if (ACCESS_TOKEN) return ACCESS_TOKEN
const username = process.env.ZOOMINFO_USERNAME
const password = process.env.ZOOMINFO_PRIVATE_KEY
if (!username || !password) {
throw new Error('ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY required for authentication')
}
const res = await fetch(`${BASE_URL}/authenticate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
})
const text = await res.text()
if (!res.ok) {
throw new Error(`Authentication failed (${res.status}): ${text}`)
}
try {
const data = JSON.parse(text)
if (!data.jwt) throw new Error('No JWT in response')
ACCESS_TOKEN = data.jwt
return ACCESS_TOKEN
} catch (e) {
if (e.message === 'No JWT in response') throw e
throw new Error(`Authentication failed: ${text}`)
}
}
async function api(method, path, body) {
if (args['dry-run']) {
return { _dry_run: true, method, url: `${BASE_URL}${path}`, headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ***' }, body }
}
const token = await authenticate()
const res = await fetch(`${BASE_URL}${path}`, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
try {
return JSON.parse(text)
} catch {
return { status: res.status, body: text }
}
}
function parseArgs(args) {
const result = { _: [] }
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (arg.startsWith('--')) {
const key = arg.slice(2)
const next = args[i + 1]
if (next && !next.startsWith('--')) {
result[key] = next
i++
} else {
result[key] = true
}
} else {
result._.push(arg)
}
}
return result
}
const args = parseArgs(process.argv.slice(2))
const [cmd, sub, ...rest] = args._
async function main() {
let result
const page = args.page ? Number(args.page) : 1
const perPage = args['per-page'] ? Number(args['per-page']) : 25
switch (cmd) {
case 'auth': {
if (args['dry-run']) {
result = { _dry_run: true, action: 'authenticate', url: `${BASE_URL}/authenticate`, jwt: '***' }
break
}
const token = await authenticate()
result = { jwt: token }
break
}
case 'contacts':
switch (sub) {
case 'search': {
const body = { rpp: perPage, page }
if (args['job-title']) body.jobTitle = [args['job-title']]
if (args.company) body.companyName = [args.company]
if (args.location) body.locationSearchType = 'PersonLocation', body.personLocationCity = [args.location]
if (args.seniority) body.managementLevel = [args.seniority]
if (args.department) body.department = [args.department]
result = await api('POST', '/search/contact', body)
break
}
case 'enrich': {
const body = {}
if (args.email) body.matchEmail = [args.email]
if (args['person-id']) body.personId = [args['person-id']]
if (!args.email && !args['person-id']) {
result = { error: '--email or --person-id required' }
break
}
result = await api('POST', '/enrich/contact', body)
break
}
default:
result = { error: 'Unknown contacts subcommand. Use: search, enrich' }
}
break
case 'companies':
switch (sub) {
case 'search': {
const body = { rpp: perPage, page }
if (args.name) body.companyName = [args.name]
if (args.industry) body.industry = [args.industry]
if (args['revenue-min']) body.revenueMin = Number(args['revenue-min'])
if (args['revenue-max']) body.revenueMax = Number(args['revenue-max'])
if (args['employees-min']) body.employeeCountMin = Number(args['employees-min'])
if (args['employees-max']) body.employeeCountMax = Number(args['employees-max'])
result = await api('POST', '/search/company', body)
break
}
case 'enrich': {
const body = {}
if (args.domain) body.matchCompanyWebsite = [args.domain]
if (args['company-id']) body.companyId = [args['company-id']]
if (!args.domain && !args['company-id']) {
result = { error: '--domain or --company-id required' }
break
}
result = await api('POST', '/enrich/company', body)
break
}
default:
result = { error: 'Unknown companies subcommand. Use: search, enrich' }
}
break
case 'intent':
switch (sub) {
case 'search': {
if (!args.topic) {
result = { error: '--topic required' }
break
}
const body = { topicId: [args.topic] }
if (args['company-id']) body.companyId = [args['company-id']]
result = await api('POST', '/lookup/intent', body)
break
}
default:
result = { error: 'Unknown intent subcommand. Use: search' }
}
break
case 'scoops':
switch (sub) {
case 'search': {
const body = { rpp: perPage, page }
if (args['company-id']) body.companyId = [args['company-id']]
if (args.topic) body.topicId = [args.topic]
result = await api('POST', '/lookup/scoops', body)
break
}
default:
result = { error: 'Unknown scoops subcommand. Use: search' }
}
break
default:
result = {
error: 'Unknown command',
usage: {
auth: 'auth — authenticate and get JWT token',
contacts: {
search: 'contacts search [--job-title <t>] [--company <c>] [--location <l>] [--seniority <s>] [--department <d>] [--page <n>]',
enrich: 'contacts enrich --email <email> | --person-id <id>',
},
companies: {
search: 'companies search [--name <n>] [--industry <i>] [--revenue-min <n>] [--employees-min <n>] [--page <n>]',
enrich: 'companies enrich --domain <domain> | --company-id <id>',
},
intent: {
search: 'intent search --topic <topic> [--company-id <id>]',
},
scoops: {
search: 'scoops search [--company-id <id>] [--topic <topic>] [--page <n>]',
},
}
}
}
console.log(JSON.stringify(result, null, 2))
}
main().catch(err => {
console.error(JSON.stringify({ error: err.message }))
process.exit(1)
})

View file

@ -0,0 +1,128 @@
# AirOps
AI content platform for crafting content that wins AI search. Build and execute AI workflows (flows) for SEO content generation, data enrichment, and automation.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Flows, Workflows, Runs |
| MCP | - | Not available |
| CLI | ✓ | [airops.js](../clis/airops.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: API Key + Workspace ID
- **Header**: `Authorization: Bearer {api_key}`
- **Env vars**: `AIROPS_API_KEY`, `AIROPS_WORKSPACE_ID`
- **Get key**: Settings > API Keys at https://app.airops.com
## Common Agent Operations
### List Flows
```bash
GET https://api.airops.com/public_api/v1/workspaces/{workspace_id}/flows
```
### Get Flow Details
```bash
GET https://api.airops.com/public_api/v1/workspaces/{workspace_id}/flows/{flow_id}
```
### Execute a Flow
```bash
POST https://api.airops.com/public_api/v1/workspaces/{workspace_id}/flows/{flow_id}/execute
{
"inputs": {
"keyword": "best project management tools",
"target_audience": "startup founders"
}
}
```
### List Runs for a Flow
```bash
GET https://api.airops.com/public_api/v1/workspaces/{workspace_id}/flows/{flow_id}/runs
```
### Get Run Status
```bash
GET https://api.airops.com/public_api/v1/workspaces/{workspace_id}/runs/{run_id}
```
### List Workflows
```bash
GET https://api.airops.com/public_api/v1/workspaces/{workspace_id}/workflows
```
### Execute a Workflow
```bash
POST https://api.airops.com/public_api/v1/workspaces/{workspace_id}/workflows/{workflow_id}/execute
{
"inputs": {
"topic": "email marketing best practices",
"content_type": "blog_post"
}
}
```
## Key Metrics
### Flow Data
- `id` - Flow identifier
- `name` - Flow name
- `description` - Flow description
- `status` - Active/inactive status
- `created_at` - Creation timestamp
- `updated_at` - Last modified timestamp
### Run Data
- `id` - Run identifier
- `flow_id` - Parent flow ID
- `status` - pending, running, completed, failed
- `inputs` - Input parameters used
- `outputs` - Generated results
- `started_at` - Run start time
- `completed_at` - Run completion time
## Parameters
### Flow Execution
- `inputs` - JSON object of key-value pairs matching the flow's expected inputs
- Input keys vary per flow (e.g., `keyword`, `topic`, `url`, `target_audience`)
### Workflow Execution
- `inputs` - JSON object of key-value pairs matching the workflow's expected inputs
## When to Use
- Bulk content generation for SEO at scale
- SEO-optimized article creation with AI workflows
- Data enrichment pipelines for marketing lists
- Keyword research automation
- Content optimization and rewriting
- Programmatic SEO page generation
- AI-powered content briefs and outlines
## Rate Limits
- Rate limits vary by plan
- Concurrent execution limits depend on workspace tier
- Check AirOps dashboard for current usage and limits
## Relevant Skills
- ai-seo
- content-strategy
- programmatic-seo
- copywriting

149
tools/integrations/clay.md Normal file
View file

@ -0,0 +1,149 @@
# Clay
Data enrichment and outbound automation platform for building lead lists with waterfall enrichment across 75+ data providers.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Tables, People Enrichment, Company Enrichment |
| MCP | ✓ | [Claude connector](https://claude.com/connectors/clay) |
| CLI | ✓ | [clay.js](../clis/clay.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: API Key (Bearer token)
- **Header**: `Authorization: Bearer {api_key}`
- **Get key**: Settings > API at https://app.clay.com
## Common Agent Operations
### List Tables
```bash
GET https://api.clay.com/v3/tables
Authorization: Bearer {api_key}
```
### Get Table Details
```bash
GET https://api.clay.com/v3/tables/{table_id}
Authorization: Bearer {api_key}
```
### Get Table Rows
```bash
GET https://api.clay.com/v3/tables/{table_id}/rows?page=1&per_page=25
Authorization: Bearer {api_key}
```
### Add Row to Table
```bash
POST https://api.clay.com/v3/tables/{table_id}/rows
{
"first_name": "Jane",
"last_name": "Doe",
"company": "Acme Inc",
"email": "jane@acme.com"
}
```
### People Enrichment
```bash
POST https://api.clay.com/v3/people/enrich
{
"email": "jane@acme.com"
}
```
### Company Enrichment
```bash
POST https://api.clay.com/v3/companies/enrich
{
"domain": "acme.com"
}
```
## Key Metrics
### Person Data
- `first_name`, `last_name` - Name
- `email` - Email address
- `title` - Job title
- `linkedin_url` - LinkedIn profile
- `company` - Company name
- `location` - Location
- `seniority` - Seniority level
### Company Data
- `name` - Company name
- `domain` - Website domain
- `industry` - Industry
- `employee_count` - Number of employees
- `revenue` - Estimated revenue
- `location` - Headquarters location
- `technologies` - Tech stack
- `description` - Company description
### Table Data
- `id` - Table ID
- `name` - Table name
- `row_count` - Number of rows
- `columns` - Column definitions
- `created_at` - Creation timestamp
- `updated_at` - Last update timestamp
## Parameters
### Tables
- `page` - Page number (default: 1)
- `per_page` - Results per page (default: 25)
### People Enrichment
- `email` - Email address
- `linkedin_url` - LinkedIn profile URL
- `first_name` + `last_name` - Name-based lookup
### Company Enrichment
- `domain` - Company domain (e.g., "acme.com")
### Add Row
- Fields are dynamic and match the table's column definitions
- Pass data as key-value pairs matching column names
## When to Use
- Building enriched prospect lists with waterfall enrichment across multiple providers
- Enriching leads with person and company data from 75+ sources
- Automating outbound workflows with enriched data
- Finding verified contact info (emails, phone numbers, social profiles)
- Company research and firmographic analysis
- Triggering enrichment workflows via webhooks
- Syncing enriched data back to CRM or outbound tools
## Rate Limits
- Rate limits vary by plan
- Standard: 100 requests/minute
- Enterprise plans have higher limits
- Enrichment credits consumed per lookup vary by data provider
- Webhook endpoints accept data continuously
## Relevant Skills
- cold-email
- revops
- sales-enablement
- competitor-alternatives

191
tools/integrations/close.md Normal file
View file

@ -0,0 +1,191 @@
# Close
Sales CRM for SMBs with built-in calling, email, and pipeline management designed for high-velocity sales teams.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Leads, Contacts, Opportunities, Activities, Tasks |
| MCP | - | Not available |
| CLI | ✓ | [close.js](../clis/close.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: Basic Auth
- **Header**: `Authorization: Basic {base64(api_key + ':')}`
- **Get key**: Settings > API Keys at https://app.close.com
## Common Agent Operations
### List Leads
```bash
GET https://api.close.com/api/v1/lead/
Authorization: Basic {base64(api_key + ':')}
```
### Search Leads
```bash
GET https://api.close.com/api/v1/lead/?query=company_name
Authorization: Basic {base64(api_key + ':')}
```
### Create Lead
```bash
POST https://api.close.com/api/v1/lead/
{
"name": "Acme Corp",
"url": "https://acme.com",
"description": "Enterprise prospect"
}
```
### Get Contact
```bash
GET https://api.close.com/api/v1/contact/{contact_id}/
Authorization: Basic {base64(api_key + ':')}
```
### Create Contact
```bash
POST https://api.close.com/api/v1/contact/
{
"lead_id": "lead_xxx",
"name": "Jane Smith",
"emails": [{ "email": "jane@acme.com", "type": "office" }],
"phones": [{ "phone": "+15551234567", "type": "office" }]
}
```
### Create Opportunity
```bash
POST https://api.close.com/api/v1/opportunity/
{
"lead_id": "lead_xxx",
"value": 50000,
"status_type": "active"
}
```
### List Activities
```bash
GET https://api.close.com/api/v1/activity/?lead_id=lead_xxx
Authorization: Basic {base64(api_key + ':')}
```
### Create Task
```bash
POST https://api.close.com/api/v1/task/
{
"lead_id": "lead_xxx",
"text": "Follow up on demo request",
"_type": "lead",
"date": "2026-03-10"
}
```
## Key Metrics
### Lead Data
- `id` - Lead ID
- `display_name` - Lead name
- `url` - Website URL
- `description` - Lead description
- `status_id` - Pipeline status
- `contacts` - Associated contacts
- `opportunities` - Associated opportunities
- `tasks` - Associated tasks
### Contact Data
- `id` - Contact ID
- `lead_id` - Parent lead
- `name` - Full name
- `title` - Job title
- `emails` - Email addresses array
- `phones` - Phone numbers array
### Opportunity Data
- `id` - Opportunity ID
- `lead_id` - Parent lead
- `value` - Value in cents
- `status_type` - active, won, or lost
- `confidence` - Win probability (0-100)
- `date_won` - Close date
### Task Data
- `id` - Task ID
- `lead_id` - Parent lead
- `text` - Task description
- `assigned_to` - Assigned user ID
- `date` - Due date
- `is_complete` - Completion status
## Parameters
### Leads
- `query` - Search query string
- `_skip` - Number of results to skip (pagination)
- `_limit` - Max results to return (default: 100)
- `_fields` - Comma-separated fields to return
### Contacts
- `lead_id` - Filter by parent lead
- `_skip` - Pagination offset
- `_limit` - Max results
### Opportunities
- `lead_id` - Filter by parent lead
- `status` - Filter by status type (active, won, lost)
- `_skip` - Pagination offset
- `_limit` - Max results
### Activities
- `lead_id` - Filter by lead
- `_type__type` - Filter by type (Email, Call, Note, SMS, Meeting)
- `date_created__gt` - After date
- `date_created__lt` - Before date
### Tasks
- `assigned_to` - Filter by user ID
- `is_complete` - Filter by completion (true/false)
- `lead_id` - Filter by lead
- `_type` - Task type (lead)
## When to Use
- Managing SMB sales pipelines with high-touch outreach
- Tracking sales activities (calls, emails, meetings) per lead
- Creating and managing tasks for sales follow-ups
- Opportunity tracking and revenue forecasting
- Building automated outreach workflows
- Sales team performance reporting
## Rate Limits
- Rate limits based on organization plan
- Standard: ~100 requests/minute
- Responses include `ratelimit-limit` and `ratelimit-remaining` headers
- 429 responses include `Retry-After` header
## Relevant Skills
- revops
- sales-enablement
- cold-email

View file

@ -0,0 +1,142 @@
# Coupler.io
Data integration platform that connects marketing, sales, analytics, and e-commerce data sources to destinations like spreadsheets, BI tools, and data warehouses with automated scheduling.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Importers, Runs, Sources, Destinations |
| MCP | ✓ | [Claude connector](https://claude.com/connectors/coupler-io) |
| CLI | ✓ | [coupler.js](../clis/coupler.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: API Key
- **Header**: `Authorization: Bearer {api_key}`
- **Get key**: Settings > API at https://app.coupler.io
## Common Agent Operations
### List Importers
```bash
GET https://api.coupler.io/v1/importers
```
### Get Importer Details
```bash
GET https://api.coupler.io/v1/importers/{id}
```
### Trigger an Importer Run
```bash
POST https://api.coupler.io/v1/importers/{id}/run
```
### Create an Importer
```bash
POST https://api.coupler.io/v1/importers
{
"source_type": "google_analytics",
"destination_type": "google_sheets",
"name": "GA4 to Sheets Daily"
}
```
### Delete an Importer
```bash
DELETE https://api.coupler.io/v1/importers/{id}
```
### List Runs for an Importer
```bash
GET https://api.coupler.io/v1/importers/{id}/runs
```
### Get Run Details
```bash
GET https://api.coupler.io/v1/runs/{id}
```
### List Available Sources
```bash
GET https://api.coupler.io/v1/sources
```
### List Available Destinations
```bash
GET https://api.coupler.io/v1/destinations
```
## Key Metrics
### Importer Data
- `id` - Importer ID
- `name` - Importer name
- `source_type` - Source connector type
- `destination_type` - Destination connector type
- `schedule` - Automation schedule
- `status` - Current status
- `last_run_at` - Last run timestamp
### Run Data
- `id` - Run ID
- `importer_id` - Parent importer
- `status` - Run status (pending, running, completed, failed)
- `started_at` - Start timestamp
- `finished_at` - Finish timestamp
- `rows_imported` - Number of rows processed
- `error` - Error message if failed
## Parameters
### Importer Creation
- `source_type` - Source connector (e.g., google_analytics, google_ads, facebook_ads, hubspot, shopify, stripe, airtable)
- `destination_type` - Destination connector (e.g., google_sheets, bigquery, snowflake, postgresql)
- `name` - Importer name
- `schedule` - Automation schedule (e.g., hourly, daily, weekly)
### Supported Sources
- **Analytics**: Google Analytics, Adobe Analytics
- **Ads**: Google Ads, Facebook Ads, LinkedIn Ads, TikTok Ads
- **CRM**: HubSpot, Salesforce, Pipedrive
- **E-commerce**: Shopify, Stripe, WooCommerce
- **Other**: Airtable, Google Sheets, BigQuery, MySQL, PostgreSQL
### Supported Destinations
- **Spreadsheets**: Google Sheets, Excel Online
- **BI Tools**: Looker Studio, Power BI, Tableau
- **Data Warehouses**: BigQuery, Snowflake, Redshift
- **Databases**: PostgreSQL, MySQL
## When to Use
- Automating marketing data pipelines from ads and analytics platforms
- Consolidating multi-channel campaign data into a single destination
- Scheduling recurring data syncs from CRM to spreadsheets or BI tools
- Building marketing dashboards with fresh data from multiple sources
- Exporting e-commerce data for reporting and analysis
- Connecting data sources without writing custom ETL code
## Rate Limits
- Rate limits vary by plan
- Standard: API access available on Professional and higher plans
- Importer run frequency depends on plan tier
## Relevant Skills
- analytics-tracking
- paid-ads
- revops

View file

@ -0,0 +1,137 @@
# Crossbeam
Partner ecosystem platform (now part of Reveal) for sharing account data with partners to identify co-sell opportunities, overlapping customers, and partner-sourced pipeline.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Partners, Populations, Overlaps, Reports, Threads |
| MCP | ✓ | [Claude connector](https://claude.com/connectors/crossbeam) |
| CLI | ✓ | [crossbeam.js](../clis/crossbeam.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: API Key
- **Header**: `Authorization: Bearer {api_key}`
- **Get key**: Settings > API at https://app.crossbeam.com
## Common Agent Operations
### List Partners
```bash
GET https://api.crossbeam.com/v1/partners
Authorization: Bearer {api_key}
```
### Get Partner Details
```bash
GET https://api.crossbeam.com/v1/partners/{id}
Authorization: Bearer {api_key}
```
### List Populations
```bash
GET https://api.crossbeam.com/v1/populations
Authorization: Bearer {api_key}
```
### List Overlaps
```bash
GET https://api.crossbeam.com/v1/overlaps?partner_id={partner_id}&population_id={population_id}
Authorization: Bearer {api_key}
```
### Get Overlap Details
```bash
GET https://api.crossbeam.com/v1/overlaps/{id}
Authorization: Bearer {api_key}
```
### Search Accounts
```bash
GET https://api.crossbeam.com/v1/accounts/search?domain={domain}
Authorization: Bearer {api_key}
```
### List Reports
```bash
GET https://api.crossbeam.com/v1/reports
Authorization: Bearer {api_key}
```
### List Collaboration Threads
```bash
GET https://api.crossbeam.com/v1/threads
Authorization: Bearer {api_key}
```
## Key Metrics
### Partner Data
- `id` - Partner ID
- `name` - Partner company name
- `status` - Partnership status (active, pending, etc.)
- `created_at` - When the partnership was established
- `populations_shared` - Number of shared populations
### Population Data
- `id` - Population ID
- `name` - Population name (e.g., "Customers", "Open Opportunities")
- `record_count` - Number of records in population
- `partner_visibility` - What partners can see
### Overlap Data
- `id` - Overlap ID
- `partner_id` - Partner involved
- `population_id` - Population matched
- `account_name` - Overlapping account name
- `overlap_type` - Type of overlap (customer, prospect, etc.)
- `match_confidence` - Match confidence score
### Report Data
- `id` - Report ID
- `name` - Report name
- `type` - Report type
- `created_at` - Creation date
- `results` - Report results data
## Parameters
### Overlaps List
- `partner_id` - Filter by specific partner
- `population_id` - Filter by specific population
### Accounts Search
- `domain` - Company domain to search for
## When to Use
- Identifying co-sell opportunities with channel partners
- Finding overlapping customers and prospects across partner ecosystems
- Building partner-sourced pipeline by matching accounts
- Tracking partner influence on deals
- Creating account mapping reports for partner meetings
- Prioritizing which partners to engage based on overlap data
## Rate Limits
- Rate limits vary by plan
- Standard: 100 requests/minute
- Pagination supported on list endpoints
## Relevant Skills
- revops
- sales-enablement
- referral-program
- competitor-alternatives

View file

@ -0,0 +1,172 @@
# Outreach
Sales engagement platform for managing prospects, sequences, and outbound campaigns at scale.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Prospects, Sequences, Mailings, Accounts, Tasks |
| MCP | ✓ | [Claude connector](https://claude.com/connectors/outreach) |
| CLI | ✓ | [outreach.js](../clis/outreach.js) |
| SDK | - | REST API only (JSON:API format) |
## Authentication
- **Type**: OAuth2 Bearer Token
- **Header**: `Authorization: Bearer {access_token}`
- **Content-Type**: `application/vnd.api+json`
- **Get token**: Settings > API at https://app.outreach.io or via OAuth2 flow
## Common Agent Operations
### List Prospects
```bash
curl -s https://api.outreach.io/api/v2/prospects \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json"
```
### Get a Prospect
```bash
curl -s https://api.outreach.io/api/v2/prospects/42 \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json"
```
### Create a Prospect
```bash
curl -s -X POST https://api.outreach.io/api/v2/prospects \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "prospect",
"attributes": {
"emails": ["jane@example.com"],
"firstName": "Jane",
"lastName": "Doe"
}
}
}'
```
### List Sequences
```bash
curl -s https://api.outreach.io/api/v2/sequences \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json"
```
### Add Prospect to Sequence
```bash
curl -s -X POST https://api.outreach.io/api/v2/sequenceStates \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "sequenceState",
"relationships": {
"prospect": { "data": { "type": "prospect", "id": 42 } },
"sequence": { "data": { "type": "sequence", "id": 7 } }
}
}
}'
```
### List Mailings for a Sequence
```bash
curl -s "https://api.outreach.io/api/v2/mailings?filter[sequence][id]=7" \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json"
```
### List Accounts
```bash
curl -s https://api.outreach.io/api/v2/accounts \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json"
```
### List Tasks
```bash
curl -s "https://api.outreach.io/api/v2/tasks?filter[status]=incomplete" \
-H "Authorization: Bearer $OUTREACH_ACCESS_TOKEN" \
-H "Content-Type: application/vnd.api+json"
```
## Key Metrics
### Prospect Data
- `firstName`, `lastName` - Name
- `emails` - Email addresses
- `title` - Job title
- `company` - Company name
- `tags` - Prospect tags
- `engagedAt` - Last engagement timestamp
### Sequence Data
- `name` - Sequence name
- `enabled` - Whether sequence is active
- `sequenceType` - Type (e.g., interval, date-based)
- `stepCount` - Number of steps
- `openCount`, `clickCount`, `replyCount` - Engagement metrics
### Mailing Data
- `mailingType` - Type of mailing
- `state` - Delivery state
- `openCount`, `clickCount` - Engagement
- `deliveredAt`, `openedAt`, `clickedAt` - Timestamps
## Parameters
### Prospects
- `page[number]` - Page number (default: 1)
- `page[size]` - Results per page (default: 25, max: 1000)
- `filter[emails]` - Filter by email
- `filter[firstName]` - Filter by first name
- `filter[lastName]` - Filter by last name
- `sort` - Sort field (e.g., `createdAt`, `-updatedAt`)
### Sequences
- `filter[name]` - Filter by sequence name
- `filter[enabled]` - Filter by active status
### Mailings
- `filter[sequence][id]` - Filter by sequence ID
- `filter[prospect][id]` - Filter by prospect ID
### Tasks
- `filter[status]` - Filter by status (e.g., `incomplete`, `complete`)
- `filter[taskType]` - Filter by type (e.g., `call`, `email`, `action_item`)
## When to Use
- Managing outbound sales sequences and cadences
- Adding prospects to automated email sequences
- Tracking prospect engagement across touchpoints
- Managing sales tasks and follow-ups
- Coordinating multi-channel outreach campaigns
- Monitoring sequence performance and reply rates
## Rate Limits
- 10,000 requests per hour per user
- Burst limit: 100 requests per 10 seconds
- Rate limit headers returned: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
- 429 responses when limits exceeded
## Relevant Skills
- cold-email
- revops
- sales-enablement
- email-sequence

208
tools/integrations/pendo.md Normal file
View file

@ -0,0 +1,208 @@
# Pendo
Product analytics and in-app guidance platform for tracking user behavior, measuring feature adoption, and delivering targeted in-app messages.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Features, Pages, Guides, Visitors, Accounts, Reports, Metadata |
| MCP | - | Not available |
| CLI | ✓ | [pendo.js](../clis/pendo.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: Integration Key
- **Header**: `x-pendo-integration-key: {key}`
- **Get key**: Settings > Integrations at https://app.pendo.io
## Common Agent Operations
### List Features
```bash
GET https://app.pendo.io/api/v1/feature
```
### Get Feature Details
```bash
GET https://app.pendo.io/api/v1/feature/{featureId}
```
### List Pages
```bash
GET https://app.pendo.io/api/v1/page
```
### Get Page Details
```bash
GET https://app.pendo.io/api/v1/page/{pageId}
```
### List Guides
```bash
GET https://app.pendo.io/api/v1/guide?state=public
```
### Get Guide Details
```bash
GET https://app.pendo.io/api/v1/guide/{guideId}
```
### Get Visitor Data
```bash
GET https://app.pendo.io/api/v1/visitor/{visitorId}
```
### Search Visitors
```bash
POST https://app.pendo.io/api/v1/aggregation
{
"response": { "mimeType": "application/json" },
"request": {
"pipeline": [
{ "source": { "visitors": null } },
{ "filter": "lastVisitedAt > 1700000000000" }
]
}
}
```
### Get Account Data
```bash
GET https://app.pendo.io/api/v1/account/{accountId}
```
### Search Accounts
```bash
POST https://app.pendo.io/api/v1/aggregation
{
"response": { "mimeType": "application/json" },
"request": {
"pipeline": [
{ "source": { "accounts": null } },
{ "filter": "metadata.auto.lastupdated > 1700000000000" }
]
}
}
```
### Run Funnel Report
```bash
POST https://app.pendo.io/api/v1/aggregation
{
"response": { "mimeType": "application/json" },
"request": {
"pipeline": [
{ "source": { "visitors": null, "timeSeries": { "period": "dayRange", "first": 1700000000000, "last": 1700600000000 } } },
{ "identified": "visitorId" },
{ "filter": "pageId == \"page-id-1\"" },
{ "filter": "pageId == \"page-id-2\"" }
]
}
}
```
### List Metadata Fields
```bash
GET https://app.pendo.io/api/v1/metadata/schema/visitor
GET https://app.pendo.io/api/v1/metadata/schema/account
GET https://app.pendo.io/api/v1/metadata/schema/parentAccount
```
## Key Metrics
### Feature Data
- `id` - Feature ID
- `name` - Feature name
- `kind` - Feature type
- `elementPath` - CSS selector for the tracked element
- `pageId` - Associated page ID
- `numEvents` - Event count
- `numVisitors` - Unique visitor count
### Page Data
- `id` - Page ID
- `name` - Page name
- `rules` - URL matching rules
- `numEvents` - Pageview count
- `numVisitors` - Unique visitor count
### Guide Data
- `id` - Guide ID
- `name` - Guide name
- `state` - Guide state (draft, staged, public, disabled)
- `launchMethod` - How the guide is triggered
- `steps` - Guide step definitions
- `numSteps` - Number of steps
- `numViews` - Total views
- `numVisitors` - Unique visitors who saw the guide
### Visitor Data
- `visitorId` - Unique visitor identifier
- `lastVisitedAt` - Last visit timestamp
- `firstVisit` - First visit timestamp
- `numEvents` - Total event count
- `metadata` - Custom visitor metadata
### Account Data
- `accountId` - Unique account identifier
- `lastVisitedAt` - Last visit from any account member
- `numVisitors` - Number of visitors in the account
- `metadata` - Custom account metadata
## Parameters
### Guide Filtering
- `state` - Filter by state: draft, staged, public, disabled
### Aggregation Queries
- `source` - Data source: visitors, accounts, features, pages, guides
- `filter` - Expression-based filtering
- `sort` - Sort results
- `limit` - Max results to return
- `timeSeries` - Time range with period, first, last
### Metadata Kinds
- `visitor` - Visitor metadata schema
- `account` - Account metadata schema
- `parentAccount` - Parent account metadata schema
## When to Use
- Tracking feature adoption and usage patterns
- Building and managing in-app onboarding guides
- Analyzing user behavior across pages and features
- Segmenting users by engagement level
- Running funnel analysis on user journeys
- Identifying at-risk accounts based on usage decline
- A/B testing in-app messages and tooltips
## Rate Limits
- Rate limits vary by plan
- Standard: 500 requests per minute
- Aggregation queries: may take longer for large datasets
- Use pagination for large result sets
## Relevant Skills
- analytics-tracking
- onboarding-cro
- churn-prevention
- ab-test-setup

View file

@ -0,0 +1,150 @@
# Similarweb
Competitive traffic intelligence platform providing website analytics, traffic sources, keyword data, and competitor insights.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Traffic, Search, Referrals, Competitors, Geography |
| MCP | - | Not available |
| CLI | ✓ | [similarweb.js](../clis/similarweb.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: API Key
- **Query param**: `?api_key={key}`
- **Get key**: Account Settings > API at https://account.similarweb.com
## Common Agent Operations
### Total Visits
```bash
GET https://api.similarweb.com/v1/website/example.com/total-traffic-and-engagement/visits?api_key={key}&start_date=2024-01&end_date=2024-03&country=us&granularity=monthly
```
### Pages Per Visit
```bash
GET https://api.similarweb.com/v1/website/example.com/total-traffic-and-engagement/pages-per-visit?api_key={key}&start_date=2024-01&end_date=2024-03
```
### Average Visit Duration
```bash
GET https://api.similarweb.com/v1/website/example.com/total-traffic-and-engagement/average-visit-duration?api_key={key}&start_date=2024-01&end_date=2024-03
```
### Bounce Rate
```bash
GET https://api.similarweb.com/v1/website/example.com/total-traffic-and-engagement/bounce-rate?api_key={key}&start_date=2024-01&end_date=2024-03
```
### Traffic Sources Breakdown
```bash
GET https://api.similarweb.com/v1/website/example.com/traffic-sources/overview?api_key={key}&start_date=2024-01&end_date=2024-03
```
### Top Referral Sites
```bash
GET https://api.similarweb.com/v1/website/example.com/traffic-sources/referrals?api_key={key}&start_date=2024-01&end_date=2024-03
```
### Organic Keywords
```bash
GET https://api.similarweb.com/v1/website/example.com/search/organic-search-keywords?api_key={key}&start_date=2024-01&end_date=2024-03
```
### Paid Keywords
```bash
GET https://api.similarweb.com/v1/website/example.com/search/paid-search-keywords?api_key={key}&start_date=2024-01&end_date=2024-03
```
### Similar Sites / Competitors
```bash
GET https://api.similarweb.com/v1/website/example.com/similar-sites/similarsites?api_key={key}
```
### Category Ranking
```bash
GET https://api.similarweb.com/v1/website/example.com/category-rank/category-rank?api_key={key}
```
### Traffic by Country
```bash
GET https://api.similarweb.com/v1/website/example.com/geo/traffic-by-country?api_key={key}&start_date=2024-01&end_date=2024-03
```
## Key Metrics
### Traffic & Engagement
- `visits` - Total visits for the period
- `pages_per_visit` - Average pages viewed per visit
- `average_visit_duration` - Average session duration in seconds
- `bounce_rate` - Percentage of single-page visits
### Traffic Sources
- `search` - Organic + paid search percentage
- `social` - Social media traffic percentage
- `direct` - Direct traffic percentage
- `referrals` - Referral traffic percentage
- `mail` - Email traffic percentage
- `display_ads` - Display advertising percentage
### Search Keywords
- `search_term` - Keyword text
- `share` - Traffic share percentage
- `volume` - Search volume
- `cpc` - Cost per click
- `position` - Average ranking position
### Geography
- `country` - Country code
- `share` - Traffic share from that country
## Parameters
### Common Parameters
- `start_date` - Start month (YYYY-MM format)
- `end_date` - End month (YYYY-MM format)
- `country` - Two-letter country code (e.g., us, gb, de)
- `granularity` - Data granularity: monthly, weekly, daily
### Search Parameters
- `limit` - Number of keywords to return
- `country` - Filter by country
## When to Use
- Analyzing competitor website traffic and engagement metrics
- Benchmarking your site against competitors
- Identifying top traffic sources for any website
- Discovering competitor organic and paid keywords
- Finding similar sites and competitive landscape
- Understanding geographic traffic distribution
- Auditing SEO performance relative to competitors
- Researching market share by traffic volume
## Rate Limits
- Rate limits vary by plan tier
- Standard: 10 requests/second
- Data availability depends on plan (3 months to 36 months historical)
- Some endpoints require Premium or Enterprise plans
## Relevant Skills
- seo-audit
- competitor-alternatives
- paid-ads
- content-strategy

View file

@ -0,0 +1,145 @@
# Supermetrics
Marketing data pipeline that connects 200+ marketing platforms. Pulls data from ad platforms, analytics, social, SEO, email, and more into a single query interface.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Query any connected data source, manage accounts |
| MCP | ✓ | [Claude connector](https://claude.com/connectors/supermetrics) |
| CLI | ✓ | [supermetrics.js](../clis/supermetrics.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: API Key
- **Query param**: `api_key={api_key}` or **Header**: `x-api-key: {api_key}`
- **Get key**: Supermetrics Hub > API settings at https://hub.supermetrics.com
## Common Agent Operations
### Query a Data Source
```bash
POST https://api.supermetrics.com/enterprise/v2/query/data/json
{
"ds_id": "GA4",
"ds_accounts": "123456789",
"date_range_type": "last_28_days",
"fields": [
{ "name": "sessions" },
{ "name": "pageviews" },
{ "name": "date" }
]
}
```
### Query with Filters
```bash
POST https://api.supermetrics.com/enterprise/v2/query/data/json
{
"ds_id": "AW",
"ds_accounts": "123-456-7890",
"date_range_type": "last_month",
"fields": [
{ "name": "campaign_name" },
{ "name": "clicks" },
{ "name": "impressions" },
{ "name": "cost" }
],
"max_rows": 100
}
```
### List Available Data Sources
```bash
GET https://api.supermetrics.com/enterprise/v2/datasources
```
### List Connected Accounts
```bash
GET https://api.supermetrics.com/enterprise/v2/datasources/accounts?ds_id=GA4
```
### List Teams
```bash
GET https://api.supermetrics.com/enterprise/v2/teams
```
### List Users
```bash
GET https://api.supermetrics.com/enterprise/v2/users
```
## Key Metrics
### Data Source IDs
- `GA4` - Google Analytics 4
- `GA4_PAID` - Google Analytics (paid)
- `AW` - Google Ads
- `FB` - Facebook Ads
- `LI` - LinkedIn Ads
- `TW_ADS` - Twitter Ads
- `IG_IA` - Instagram
- `FB_IA` - Facebook Pages
- `GSC` - Google Search Console
- `SE` - Semrush
- `MC` - Mailchimp
- `HubSpot` - HubSpot
### Date Range Values
- `last_28_days` - Last 28 days
- `last_month` - Previous calendar month
- `this_month` - Current month to date
- `custom` - Custom range (requires `start_date` and `end_date`)
## Parameters
### Query
- `ds_id` - Data source identifier (required)
- `ds_accounts` - Account ID for the data source (required)
- `date_range_type` - Date range preset or "custom" (required)
- `fields` - Array of field objects with `name` property (required)
- `filter` - Filter expression for narrowing results
- `max_rows` - Maximum number of rows to return
- `start_date` - Start date for custom range (YYYY-MM-DD)
- `end_date` - End date for custom range (YYYY-MM-DD)
### Common Fields by Source
- **GA4**: `sessions`, `pageviews`, `users`, `bounce_rate`, `date`, `source`, `medium`, `page_path`
- **Google Ads**: `campaign_name`, `clicks`, `impressions`, `cost`, `conversions`, `ctr`, `cpc`
- **Facebook Ads**: `campaign_name`, `impressions`, `clicks`, `spend`, `reach`, `cpm`, `cpc`
- **LinkedIn Ads**: `campaign_name`, `impressions`, `clicks`, `cost`, `conversions`
- **GSC**: `query`, `clicks`, `impressions`, `ctr`, `position`, `page`
## When to Use
- Pulling cross-platform marketing data into a single report
- Comparing performance across ad platforms (Google, Meta, LinkedIn, TikTok)
- Aggregating analytics data from multiple sources
- Automating marketing reporting workflows
- Building unified dashboards across marketing channels
- Extracting SEO data alongside paid media metrics
## Rate Limits
- Rate limits vary by plan
- Enterprise API: typically 100 requests/minute
- Query results may be paginated for large datasets
- Recommended: use `max_rows` to control response size
## Relevant Skills
- analytics-tracking
- paid-ads
- seo-audit
- content-strategy
- social-content

View file

@ -0,0 +1,191 @@
# ZoomInfo
B2B contact database and intent data platform with 100M+ business contacts and company intelligence for sales and marketing teams.
## Capabilities
| Integration | Available | Notes |
|-------------|-----------|-------|
| API | ✓ | Contact Search, Company Search, Enrichment, Intent Data, Scoops |
| MCP | ✓ | [Claude connector](https://claude.com/connectors/zoominfo) |
| CLI | ✓ | [zoominfo.js](../clis/zoominfo.js) |
| SDK | - | REST API only |
## Authentication
- **Type**: JWT Token (Bearer)
- **Flow**: POST `/authenticate` with username + password, receive JWT token
- **Header**: `Authorization: Bearer {jwt_token}`
- **Env vars**: `ZOOMINFO_USERNAME` + `ZOOMINFO_PRIVATE_KEY` or `ZOOMINFO_ACCESS_TOKEN`
- **Get credentials**: Contact ZoomInfo sales or admin portal at https://app.zoominfo.com
## Common Agent Operations
### Authenticate
```bash
POST https://api.zoominfo.com/authenticate
{
"username": "user@company.com",
"password": "private-key-here"
}
```
### Contact Search
```bash
POST https://api.zoominfo.com/search/contact
{
"jobTitle": ["VP Marketing"],
"companyName": ["Acme Corp"],
"managementLevel": ["VP"],
"rpp": 25,
"page": 1
}
```
### Contact Enrichment
```bash
POST https://api.zoominfo.com/enrich/contact
{
"matchEmail": ["jane@acme.com"]
}
```
### Company Search
```bash
POST https://api.zoominfo.com/search/company
{
"companyName": ["Acme"],
"industry": ["Software"],
"employeeCountMin": 50,
"revenueMin": 10000000,
"rpp": 25,
"page": 1
}
```
### Company Enrichment
```bash
POST https://api.zoominfo.com/enrich/company
{
"matchCompanyWebsite": ["acme.com"]
}
```
### Intent Data Lookup
```bash
POST https://api.zoominfo.com/lookup/intent
{
"topicId": ["marketing-automation"],
"companyId": ["123456"]
}
```
### Scoops Lookup
```bash
POST https://api.zoominfo.com/lookup/scoops
{
"companyId": ["123456"],
"rpp": 25,
"page": 1
}
```
## Key Metrics
### Contact Data
- `firstName`, `lastName` - Name
- `jobTitle` - Job title
- `email` - Verified email
- `phone` - Direct phone
- `linkedinUrl` - LinkedIn profile
- `companyName` - Company name
- `managementLevel` - Seniority level
- `department` - Department
### Company Data
- `companyName` - Company name
- `website` - Website URL
- `employeeCount` - Employee count
- `industry` - Industry
- `revenue` - Annual revenue
- `techStack` - Technologies used
- `fundingAmount` - Total funding
- `companyCity`, `companyState`, `companyCountry` - Location
### Intent Data
- `topicName` - Intent topic
- `signalScore` - Signal strength
- `audienceStrength` - Audience engagement level
- `firstSeenDate`, `lastSeenDate` - Signal timeframe
## Parameters
### Contact Search
- `jobTitle` - Array of job titles
- `companyName` - Array of company names
- `managementLevel` - Array: C-Level, VP, Director, Manager, Staff
- `department` - Array: Marketing, Sales, Engineering, Finance, etc.
- `personLocationCity` - Array of cities
- `personLocationState` - Array of states
- `personLocationCountry` - Array of countries
- `rpp` - Results per page (default: 25, max: 100)
- `page` - Page number (default: 1)
### Contact Enrichment
- `matchEmail` - Array of email addresses
- `personId` - Array of ZoomInfo person IDs
- `matchFirstName` + `matchLastName` + `matchCompanyName` - Alternative lookup
### Company Search
- `companyName` - Array of company names
- `industry` - Array of industries
- `employeeCountMin` / `employeeCountMax` - Employee count range
- `revenueMin` / `revenueMax` - Revenue range
- `companyLocationCity` - Array of cities
- `rpp` - Results per page
- `page` - Page number
### Company Enrichment
- `matchCompanyWebsite` - Array of domains
- `companyId` - Array of ZoomInfo company IDs
### Intent Data
- `topicId` - Array of intent topic IDs
- `companyId` - Array of company IDs
## When to Use
- Identifying in-market accounts with intent signals
- Building targeted contact lists by role, seniority, and company
- Enriching leads with verified contact data and firmographics
- Finding decision-makers at target accounts for ABM
- Tracking company news and leadership changes via scoops
- Prioritizing outreach based on buyer intent signals
## Rate Limits
- Rate limits vary by plan and endpoint
- Standard: ~200 requests/minute
- Bulk endpoints: batched requests recommended
- Authentication tokens expire after ~12 hours
## Relevant Skills
- cold-email
- revops
- sales-enablement
- competitor-alternatives