feat: add 10 new CLI tools and integration guides from Claude connectors
New tools: Clay, Outreach, ZoomInfo, Close, Pendo, Similarweb, Supermetrics, AirOps, Crossbeam, and Coupler.io. Each includes a zero-dependency Node.js CLI and an integration guide. Updates REGISTRY.md with new tool index entries, categories, and MCP-enabled tools list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
edd613d33f
commit
4ff486a702
21 changed files with 3637 additions and 2 deletions
|
|
@ -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 Intel | ✓ | - | [✓](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
|
||||
|
||||
|
|
@ -256,8 +267,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 +304,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 +367,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 +414,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
163
tools/clis/airops.js
Executable 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/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)
|
||||
})
|
||||
158
tools/clis/clay.js
Executable file
158
tools/clis/clay.js
Executable file
|
|
@ -0,0 +1,158 @@
|
|||
#!/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.email && !args.linkedin) {
|
||||
result = { error: '--email or --linkedin required' }
|
||||
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>',
|
||||
},
|
||||
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
232
tools/clis/close.js
Executable 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
173
tools/clis/coupler.js
Executable 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': '***', '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
193
tools/clis/crossbeam.js
Executable 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
213
tools/clis/outreach.js
Executable 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: parseInt(prospectId) } },
|
||||
sequence: { data: { type: 'sequence', id: parseInt(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
221
tools/clis/pendo.js
Executable 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
214
tools/clis/similarweb.js
Executable 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/${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/${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/${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/${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/${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/${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/${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/${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/${domain}/similar-sites/similarsites`)
|
||||
break
|
||||
}
|
||||
|
||||
case 'category-rank': {
|
||||
const domain = args.domain
|
||||
if (!domain) { result = { error: '--domain required' }; break }
|
||||
result = await api('GET', `/website/${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/${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
168
tools/clis/supermetrics.js
Executable 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', 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)
|
||||
})
|
||||
207
tools/clis/zoominfo.js
Executable file
207
tools/clis/zoominfo.js
Executable file
|
|
@ -0,0 +1,207 @@
|
|||
#!/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()
|
||||
try {
|
||||
const data = JSON.parse(text)
|
||||
ACCESS_TOKEN = data.jwt
|
||||
return ACCESS_TOKEN
|
||||
} catch {
|
||||
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': {
|
||||
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)
|
||||
})
|
||||
128
tools/integrations/airops.md
Normal file
128
tools/integrations/airops.md
Normal 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/v1/workspaces/{workspace_id}/flows
|
||||
```
|
||||
|
||||
### Get Flow Details
|
||||
|
||||
```bash
|
||||
GET https://api.airops.com/v1/workspaces/{workspace_id}/flows/{flow_id}
|
||||
```
|
||||
|
||||
### Execute a Flow
|
||||
|
||||
```bash
|
||||
POST https://api.airops.com/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/v1/workspaces/{workspace_id}/flows/{flow_id}/runs
|
||||
```
|
||||
|
||||
### Get Run Status
|
||||
|
||||
```bash
|
||||
GET https://api.airops.com/v1/workspaces/{workspace_id}/runs/{run_id}
|
||||
```
|
||||
|
||||
### List Workflows
|
||||
|
||||
```bash
|
||||
GET https://api.airops.com/v1/workspaces/{workspace_id}/workflows
|
||||
```
|
||||
|
||||
### Execute a Workflow
|
||||
|
||||
```bash
|
||||
POST https://api.airops.com/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
149
tools/integrations/clay.md
Normal 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
191
tools/integrations/close.md
Normal 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 `X-Rate-Limit-Limit` and `X-Rate-Limit-Remaining` headers
|
||||
- 429 responses include `Retry-After` header
|
||||
|
||||
## Relevant Skills
|
||||
|
||||
- revops
|
||||
- sales-enablement
|
||||
- cold-email
|
||||
142
tools/integrations/coupler.md
Normal file
142
tools/integrations/coupler.md
Normal 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
|
||||
137
tools/integrations/crossbeam.md
Normal file
137
tools/integrations/crossbeam.md
Normal 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
|
||||
172
tools/integrations/outreach.md
Normal file
172
tools/integrations/outreach.md
Normal 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
208
tools/integrations/pendo.md
Normal 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=published
|
||||
```
|
||||
|
||||
### 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
|
||||
150
tools/integrations/similarweb.md
Normal file
150
tools/integrations/similarweb.md
Normal 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
|
||||
145
tools/integrations/supermetrics.md
Normal file
145
tools/integrations/supermetrics.md
Normal 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
|
||||
|
||||
{
|
||||
"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
|
||||
|
||||
{
|
||||
"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
|
||||
191
tools/integrations/zoominfo.md
Normal file
191
tools/integrations/zoominfo.md
Normal 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
|
||||
Loading…
Reference in a new issue