contactctl Try ContactCTL

If it can run a command, it’s integrated

ContactCTL is a CLI, so there is no integration directory to wait on and no connector to break. Agents, schedulers, workflow tools, and CRMs all integrate the same honest way: by executing ctc. Below are the setups we see most.

Claude Code, Codex,
Cursor, Gemini CLI.

Each of these agents already executes shell commands. The setup is one context-file section; the agent learns the rest from ctc instructions.

Claude Code

Claude runs ctc through its Bash tool. A short CLAUDE.md section makes the tool part of every session — no MCP server, no schema overhead.

CLAUDE.md
## Contact data
Use the contactctl CLI (alias `ctc`) for B2B contact
work. Run `ctc instructions` once per session.
Always pass --json; preview spend with --estimate.

Codex CLI

Codex executes shell commands; ctc never prompts and exits with meaningful codes, so it behaves in approval and full-auto modes alike.

AGENTS.md
## Contact data (contactctl)
`ctc` finds/verifies B2B contacts. Non-interactive.
Read `ctc instructions` before first use.
Exit codes: 0 found · 2 not found · 4 budget cap.

Cursor

Cursor’s agent drives the integrated terminal. One project rule teaches it when to reach for ctc and how to treat spend.

.cursor/rules/contactctl.mdc
---
description: B2B contact data via contactctl
alwaysApply: true
---
Use `ctc` in the terminal for contact tasks.
Run `ctc instructions` first; cap batches
with --max-cost.

Gemini CLI

Gemini CLI shells out through its run-command tooling. A GEMINI.md section is the entire setup.

GEMINI.md
## Contact data (contactctl)
Run `ctc` via the shell tool for find / verify /
whois / search / lookalike. All support --json.
Preview any spend with --estimate.

Safe on a timer.
Safe in a pipeline.

No prompts, deterministic exit codes, and automatic rate-limit backoff make ctc safe to run unattended.

A nightly hygiene job

Authenticate via the CONTACTCTL_API_KEY environment variable — no config file needed on ephemeral machines. Cap every unattended run.

crontab
# verify the active list every night at 02:00
0 2 * * * CONTACTCTL_API_KEY=ctc_live_... \
  ctc verify /data/active.csv /data/active.csv \
  --max-cost 25 >> /var/log/hygiene.log 2>&1

A CI gate

Fail the pipeline when a seed list contains undeliverable addresses — exit codes do the talking.

.github/workflows/lists.yml
- name: Verify seed list
  env:
    CONTACTCTL_API_KEY: ${{ secrets.CONTACTCTL_API_KEY }}
  run: |
    npm install -g contactctl
    ctc verify seeds.csv checked.csv
    ! grep -q undeliverable checked.csv

n8n, Zapier,
anything with a shell step.

Workflow platforms integrate through their command/SSH nodes — ctc becomes a step like any other.

In n8n, use the Execute Command node (self-hosted) or an SSH node pointed at a machine with ctc installed. In Zapier, use a Code/webhook step that calls a small shell endpoint you control. Either way the pattern is the same: pass values in, parse --json out.

Because output shapes are stable and exit codes deterministic, the downstream nodes never need defensive parsing — branch on the exit code, read the JSON.

n8n — Execute Command node
# command
ctc whois {{ $json.email }} --json

# downstream: JSON parse node reads
# .profile.full_name, .company.name, .cost.actual_credits

Any CRM,
in the format they all speak.

HubSpot, Salesforce, Pipedrive, Attio — every CRM exports and imports CSV. That round-trip is the integration.

Export the segment, run the batch verbs, import the same file back. Row order is preserved and your columns survive untouched, so the field mapping you set up once keeps working. In-place overwrites are atomic — a crash cannot corrupt the export.

A full 150-row enrich-and-verify pass like the one shown costs the found emails plus 3 credits in verification. The worked example lives on the CRM enrichment page.

agent — ~/crm
# 1. export contacts.csv from the CRM
$ ctc find contacts.csv contacts.csv --max-cost 150
done: 132 found · 18 not found
# charged: 132 credits · row order preserved
 
$ ctc verify contacts.csv contacts.csv
done: 150 rows verified
# 3. import contacts.csv back — columns intact

No connectors.
No backlog. Just commands.

Wondering about MCP? We chose a native CLI deliberately — here is the comparison.