| name | pp-copper |
| description | The Copper CRM command line no one else built: full CRUD plus a local database, weighted pipeline forecasting, stale-deal detection, and the bulk operations Copper's own API refuses to provide. Trigger phrases: `forecast my copper pipeline`, `find stale deals in copper`, `bulk update copper opportunities`, `log a call in copper`, `dedupe copper contacts`, `use copper`, `run copper`. |
| author | Kerry Morrison |
| license | Apache-2.0 |
| argument-hint | <command> [args] | install cli|mcp |
| allowed-tools | Read Bash |
| metadata | {"openclaw":{"requires":{"bins":["copper-pp-cli"]},"install":[{"kind":"go","bins":["copper-pp-cli"],"module":"github.com/mvanhorn/printing-press-library/library/sales-and-crm/copper/cmd/copper-pp-cli"}]}} |
Copper โ Printing Press CLI
Prerequisites: Install the CLI
This skill drives the copper-pp-cli binary. You must verify the CLI is installed before invoking any command from this skill. If it is missing, install it first:
- Install via the Printing Press installer. It defaults binaries to
$HOME/.local/bin on macOS/Linux and %LOCALAPPDATA%\Programs\PrintingPress\bin on Windows:
npx -y @mvanhorn/printing-press-library install copper --cli-only
- Verify:
copper-pp-cli --version
- Ensure the reported install directory is on
$PATH for the agent/runtime that will invoke this skill.
If the npx install fails (no Node, offline, etc.), fall back to a direct Go install (requires Go 1.26.5 or newer). This installs into $GOPATH/bin (default $HOME/go/bin), so add that directory to $PATH instead:
go install github.com/mvanhorn/printing-press-library/library/sales-and-crm/copper/cmd/copper-pp-cli@latest
If --version reports "command not found" after install, the runtime cannot see the binary directory on $PATH. Do not proceed with skill commands until verification succeeds.
Copper has no CLI, no Go client, and no agent-native tool. This turns a click-heavy web CRM into a scriptable, offline-queryable surface. It mirrors people, companies, leads, opportunities, projects, tasks, and activities into local SQLite, then adds the weighted forecast (forecast), cold-deal sweep (stale), and rate-limit-aware bulk editor (bulk) that the API and web UI leave out.
When to Use This CLI
Use this CLI for scripted or agent-driven Copper work: weighted pipeline forecasting, finding and acting on stale deals, mass field updates across many opportunities, idempotent contact sync, and offline cross-entity queries. It is the right tool when the web UI would mean repetitive clicking or when a spreadsheet export would be stale on arrival.
Anti-triggers
Do not use this CLI for:
- Configuring Copper account settings, billing, or user provisioning (use the web app)
- Editing a logged activity in place (Copper activities are immutable; use log fix to delete+recreate)
- OAuth-based multi-tenant distribution to other Copper orgs (partner-gated; this CLI uses personal API-key auth)
Unique Capabilities
These capabilities aren't available in any other tool for this API.
Pipeline intelligence
-
forecast โ Weighted expected-revenue roll-up: sums monetary_value x win_probability over open opportunities, grouped by stage, assignee, or close-month.
Reach for this instead of exporting CSV and pivoting by hand for any expected-revenue or commit/quota question.
copper-pp-cli forecast --pipeline 12345 --by stage --agent
-
stale โ Surfaces open opportunities with no interaction in N days, sorted by staleness x value, across all reps.
Use to find deals going cold before they die; pipe the output into bulk reassign.
copper-pp-cli stale --days 21 --by assignee --agent
-
who โ Joins an opportunity to its company, people, and recent activities into one related-records view.
Use for one-call deal prep before nudging or logging a touch.
copper-pp-cli who opportunity:88 --agent
Operations Copper's API refuses to provide
-
bulk โ Applies field updates (stage, owner, custom fields) across many opportunities with bounded concurrency and heuristic 429 backoff.
Use for mass updates to existing records; the safe way to avoid the rate-limit wall a naive loop hits.
copper-pp-cli bulk move --query stale.json --set pipeline_stage_id=9 --concurrency 4 --dry-run
-
upsert โ Match-then-create-or-update for people and leads; normalizes the people.emails[] vs leads.email shape difference.
Use to sync external rows without creating duplicates; not a blind create.
copper-pp-cli upsert person --match email --file contacts.json --dry-run
-
dedupe โ Local SQLite self-join surfacing people or leads that share an email, name, or company.
Run before or after a sync to catch duplicate contacts the API will not flag.
copper-pp-cli dedupe people --on email --agent
CRM hygiene
-
log โ Creates an activity with the type resolved by name (bumps interaction_count); log fix deletes and recreates to edit an immutable activity.
Use to log or correct a single touch; for the same touch across many records use bulk.
copper-pp-cli log call --on opportunity:88 --note "Left voicemail"
Command Reference
account โ Account details
copper-pp-cli account โ Fetch account details
activities โ Manage activities (notes and logged interactions)
copper-pp-cli activities create โ Create a new activity
copper-pp-cli activities delete โ Delete an activity
copper-pp-cli activities get โ Fetch an activity by id
copper-pp-cli activities search โ List/search activities
activity-types โ Activity types
copper-pp-cli activity-types โ List all activity types
companies โ Manage companies
copper-pp-cli companies activities โ List a company's activities
copper-pp-cli companies create โ Create a new company
copper-pp-cli companies delete โ Delete a company
copper-pp-cli companies get โ Fetch a company by id
copper-pp-cli companies search โ List/search companies
copper-pp-cli companies update โ Update a company
contact-types โ Contact types
copper-pp-cli contact-types โ List all contact types
custom-activity-types โ Custom activity types
copper-pp-cli custom-activity-types create โ Create a new custom activity type
copper-pp-cli custom-activity-types get โ Fetch a custom activity type by id
copper-pp-cli custom-activity-types list โ List all custom activity types
copper-pp-cli custom-activity-types update โ Update a custom activity type
custom-field-definitions โ Custom field definitions
copper-pp-cli custom-field-definitions create โ Create a new custom field definition
copper-pp-cli custom-field-definitions delete โ Delete a custom field definition
copper-pp-cli custom-field-definitions get โ Fetch a custom field definition by id
copper-pp-cli custom-field-definitions list โ List all custom field definitions
copper-pp-cli custom-field-definitions update โ Update a custom field definition
customer-sources โ Customer (lead) sources
copper-pp-cli customer-sources โ List all customer sources
lead-statuses โ Lead statuses
copper-pp-cli lead-statuses โ List all lead statuses
leads โ Manage leads
copper-pp-cli leads activities โ List a lead's activities
copper-pp-cli leads convert โ Convert a lead into a person/company/opportunity
copper-pp-cli leads create โ Create a new lead
copper-pp-cli leads delete โ Delete a lead
copper-pp-cli leads get โ Fetch a lead by id
copper-pp-cli leads search โ List/search leads
copper-pp-cli leads update โ Update a lead
copper-pp-cli leads upsert โ Create or update a lead, matched by email or a custom field
loss-reasons โ Opportunity loss reasons
copper-pp-cli loss-reasons โ List all loss reasons
opportunities โ Manage opportunities (deals)
copper-pp-cli opportunities create โ Create a new opportunity
copper-pp-cli opportunities delete โ Delete an opportunity
copper-pp-cli opportunities get โ Fetch an opportunity by id
copper-pp-cli opportunities search โ List/search opportunities
copper-pp-cli opportunities update โ Update an opportunity
people โ Manage people (contacts)
copper-pp-cli people activities โ List a person's activities
copper-pp-cli people create โ Create a new person
copper-pp-cli people delete โ Delete a person
copper-pp-cli people fetch-by-email โ Fetch a person by email address
copper-pp-cli people get โ Fetch a person by id
copper-pp-cli people search โ List/search people
copper-pp-cli people update โ Update a person
pipeline-stages โ Pipeline stages
copper-pp-cli pipeline-stages โ List all pipeline stages
pipelines โ Sales pipelines
copper-pp-cli pipelines list โ List all pipelines
copper-pp-cli pipelines stages โ List stages within a specific pipeline
projects โ Manage projects
copper-pp-cli projects create โ Create a new project
copper-pp-cli projects delete โ Delete a project
copper-pp-cli projects get โ Fetch a project by id
copper-pp-cli projects search โ List/search projects
copper-pp-cli projects update โ Update a project
tags โ Tags used across records
copper-pp-cli tags โ List all tags
tasks โ Manage tasks
copper-pp-cli tasks create โ Create a new task
copper-pp-cli tasks delete โ Delete a task
copper-pp-cli tasks get โ Fetch a task by id
copper-pp-cli tasks search โ List/search tasks
copper-pp-cli tasks update โ Update a task
users โ Users in the account
copper-pp-cli users get โ Fetch a user by id
copper-pp-cli users search โ List/search users
webhooks โ Webhook subscriptions
copper-pp-cli webhooks create โ Create a new webhook subscription
copper-pp-cli webhooks delete โ Delete (unsubscribe) a webhook subscription
copper-pp-cli webhooks get โ Fetch a webhook subscription by id
copper-pp-cli webhooks list โ List all webhook subscriptions
Finding the right command
When you know what you want to do but not which command does it, ask the CLI directly:
copper-pp-cli which "<capability in your own words>"
which resolves a natural-language capability query to the best matching command from this CLI's curated feature index. Exit code 0 means at least one match; exit code 2 means no confident match โ fall back to --help or use a narrower query.
Recipes
Monday weighted forecast by rep
copper-pp-cli forecast --by assignee --agent --select assignee_name,weighted_value,open_value
Commit view per rep without a spreadsheet pivot.
Sweep then bulk-reassign cold deals
copper-pp-cli stale --days 30 --by assignee --agent
List cold deals grouped by owner. Save the JSON, then feed the ids into the bulk reassign command (with --set assignee_id=) to mass-reassign โ preview with --dry-run before applying.
Idempotent contact import
copper-pp-cli upsert person --match email --file leads.json --dry-run
Create-or-update external rows without duplicating contacts.
One-call deal prep
copper-pp-cli who opportunity:88 --agent --select company.name,people.name,activities.details
Pull the deal's company, contacts, and recent touches in one narrowed view.
Auth Setup
Copper uses a multi-header API key. Set COPPER_API_KEY (System Settings -> API Keys -> Create a Key) and COPPER_USER_EMAIL (the email of the key owner). Every request sends X-PW-AccessToken, X-PW-UserEmail, and X-PW-Application: developer_api.
Run copper-pp-cli doctor to verify setup.
Agent Mode
Add --agent to any command. Expands to: --json --compact --no-input --no-color --yes.
-
Pipeable โ JSON on stdout, errors on stderr
-
Filterable โ --select keeps a subset of fields. Dotted paths descend into nested structures; arrays traverse element-wise. Critical for keeping context small on verbose APIs:
copper-pp-cli account --agent --select id,name,status
-
Previewable โ --dry-run shows the request without sending
-
Offline-friendly โ sync/search commands can use the local SQLite store when available
-
Non-interactive โ never prompts, every input is a flag
-
Explicit retries โ use --idempotent only when an already-existing create should count as success, and --ignore-missing only when a missing delete target should count as success
Response envelope
Commands that read from the local store or the API wrap output in a provenance envelope:
{
"meta": {"source": "live" | "local", "synced_at": "...", "reason": "..."},
"results": <data>
}
Parse .results for data and .meta.source to know whether it's live or local. A human-readable N results (live) summary is printed to stderr only when stdout is a terminal AND no machine-format flag (--json, --csv, --compact, --quiet, --plain, --select) is set โ piped/agent consumers and explicit-format runs get pure JSON on stdout.
Paths and state
Agents should treat the CLI's path resolver as part of the runtime contract:
-
Use --home <dir> for one invocation, or set COPPER_HOME=<dir> to relocate all four path kinds under one root.
-
Use per-kind env vars only when a specific kind must diverge: COPPER_CONFIG_DIR, COPPER_DATA_DIR, COPPER_STATE_DIR, COPPER_CACHE_DIR.
-
Resolution order is per-kind env var, --home, COPPER_HOME, XDG (XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME), then platform defaults.
-
config contains settings like config.toml and profiles. data contains credentials.toml, data.db, cookies, and auth sidecars. state contains persisted queries, jobs, and teach.log. cache contains regenerable HTTP/cache files.
-
Stored secrets live in credentials.toml under the data dir. Existing legacy config.toml secrets are read for compatibility and leave config.toml on the first auth write.
-
Run copper-pp-cli doctor --fail-on warn to surface path and credential-location warnings. agent-context exposes a schema v4 paths block for agents that need the resolved dirs.
-
For MCP, pass relocation through the MCP host config. The MCP binary does not inherit CLI flags:
{
"mcpServers": {
"copper": {
"command": "copper-pp-mcp",
"env": {
"COPPER_HOME": "/srv/copper"
}
}
}
}
Fleet precedence: an inherited per-kind env var overrides an explicit --home for that kind. Use COPPER_HOME or per-kind vars as durable fleet levers, and use --home only for a single invocation. Relocation is not reversible by unsetting env vars; move files manually before clearing COPPER_HOME, or doctor will not find credentials left under the former root.
Agent Feedback
When you (or the agent) notice something off about this CLI, record it:
copper-pp-cli feedback "the --since flag is inclusive but docs say exclusive"
copper-pp-cli feedback --stdin < notes.txt
copper-pp-cli feedback list --json --limit 10
Entries are stored locally as feedback.jsonl under the resolved data dir. They are never POSTed unless COPPER_FEEDBACK_ENDPOINT is set AND either --send is passed or COPPER_FEEDBACK_AUTO_SEND=true. Default behavior is local-only.
Write what surprised you, not a bug report. Short, specific, one line: that is the part that compounds.
Output Delivery
Every command accepts --deliver <sink>. The output goes to the named sink in addition to (or instead of) stdout, so agents can route command results without hand-piping. Three sinks are supported:
| Sink | Effect |
|---|
stdout | Default; write to stdout only |
file:<path> | Atomically write output to <path> (tmp + rename) |
webhook:<url> | POST the output body to the URL (application/json or application/x-ndjson when --compact) |
Unknown schemes are refused with a structured error naming the supported set. Webhook failures return non-zero and log the URL + HTTP status on stderr.
Named Profiles
A profile is a saved set of flag values, reused across invocations. Use it when a scheduled agent calls the same command every run with the same configuration - HeyGen's "Beacon" pattern.
copper-pp-cli profile save briefing --json
copper-pp-cli --profile briefing account
copper-pp-cli profile list --json
copper-pp-cli profile show briefing
copper-pp-cli profile delete briefing --yes
Explicit flags always win over profile values; profile values win over defaults. agent-context lists all available profiles under available_profiles so introspecting agents discover them at runtime.
Exit Codes
| Code | Meaning |
|---|
| 0 | Success |
| 2 | Usage error (wrong arguments) |
| 3 | Resource not found |
| 4 | Authentication required |
| 5 | API error (upstream issue) |
| 7 | Rate limited (wait and retry) |
| 10 | Config error |
Argument Parsing
Parse $ARGUMENTS:
- Empty,
help, or --help โ show copper-pp-cli --help output
- Starts with
install โ ends with mcp โ MCP installation; otherwise โ see Prerequisites above
- Anything else โ Direct Use (execute as CLI command with
--agent)
MCP Server Installation
- Install the MCP server:
go install github.com/mvanhorn/printing-press-library/library/sales-and-crm/copper/cmd/copper-pp-mcp@latest
- Register with Claude Code:
claude mcp add copper-pp-mcp -- copper-pp-mcp
- Verify:
claude mcp list
Direct Use
- Check if installed:
which copper-pp-cli
If not found, offer to install (see Prerequisites at the top of this skill).
- Match the user query to the best command from the Unique Capabilities and Command Reference above.
- Execute with the
--agent flag:
copper-pp-cli <command> [subcommand] [args] --agent
- If ambiguous, drill into subcommand help:
copper-pp-cli <command> --help.