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
- **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

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) |
| clearbit | Data Enrichment | ✓ | - | [](clis/clearbit.js) | ✓ | [clearbit.md](integrations/clearbit.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) |
| salesforce | CRM | ✓ | - | ✓ | ✓ | [salesforce.md](integrations/salesforce.md) |
| close | CRM | ✓ | - | [](clis/close.js) | - | [close.md](integrations/close.md) |
| stripe | Payments | ✓ | ✓ | ✓ | ✓ | [stripe.md](integrations/stripe.md) |
| paddle | Payments | ✓ | - | [](clis/paddle.js) | ✓ | [paddle.md](integrations/paddle.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) |
| typeform | Forms | ✓ | - | [](clis/typeform.js) | ✓ | [typeform.md](integrations/typeform.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) |
| wistia | Video | ✓ | - | [](clis/wistia.js) | - | [wistia.md](integrations/wistia.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 | ✓ |
| **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
@ -125,7 +136,6 @@ Payment processing and subscription management.
| Tool | Best For | MCP Available |
|------|----------|:-------------:|
| **stripe** | SaaS subscriptions, developer-friendly | ✓ |
| **paddle** | SaaS billing with tax handling | - |
**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 |
| **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
@ -291,6 +303,56 @@ Webinar and virtual event platforms.
**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
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.
### 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
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
- **resend** - Transactional email sending
- **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.

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