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/binon macOS/Linux and%LOCALAPPDATA%\Programs\PrintingPress\binon Windows:npx -y @mvanhorn/printing-press-library install copper --cli-only - Verify:
copper-pp-cli --version - Ensure the reported install directory is on
$PATHfor 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.6 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 activitycopper-pp-cli activities delete— Delete an activitycopper-pp-cli activities get— Fetch an activity by idcopper-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 activitiescopper-pp-cli companies create— Create a new companycopper-pp-cli companies delete— Delete a companycopper-pp-cli companies get— Fetch a company by idcopper-pp-cli companies search— List/search companiescopper-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 typecopper-pp-cli custom-activity-types get— Fetch a custom activity type by idcopper-pp-cli custom-activity-types list— List all custom activity typescopper-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 definitioncopper-pp-cli custom-field-definitions delete— Delete a custom field definitioncopper-pp-cli custom-field-definitions get— Fetch a custom field definition by idcopper-pp-cli custom-field-definitions list— List all custom field definitionscopper-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 activitiescopper-pp-cli leads convert— Convert a lead into a person/company/opportunitycopper-pp-cli leads create— Create a new leadcopper-pp-cli leads delete— Delete a leadcopper-pp-cli leads get— Fetch a lead by idcopper-pp-cli leads search— List/search leadscopper-pp-cli leads update— Update a leadcopper-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 opportunitycopper-pp-cli opportunities delete— Delete an opportunitycopper-pp-cli opportunities get— Fetch an opportunity by idcopper-pp-cli opportunities search— List/search opportunitiescopper-pp-cli opportunities update— Update an opportunity
people — Manage people (contacts)
copper-pp-cli people activities— List a person's activitiescopper-pp-cli people create— Create a new personcopper-pp-cli people delete— Delete a personcopper-pp-cli people fetch-by-email— Fetch a person by email addresscopper-pp-cli people get— Fetch a person by idcopper-pp-cli people search— List/search peoplecopper-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 pipelinescopper-pp-cli pipelines stages— List stages within a specific pipeline
projects — Manage projects
copper-pp-cli projects create— Create a new projectcopper-pp-cli projects delete— Delete a projectcopper-pp-cli projects get— Fetch a project by idcopper-pp-cli projects search— List/search projectscopper-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 taskcopper-pp-cli tasks delete— Delete a taskcopper-pp-cli tasks get— Fetch a task by idcopper-pp-cli tasks search— List/search taskscopper-pp-cli tasks update— Update a task
users — Users in the account
copper-pp-cli users get— Fetch a user by idcopper-pp-cli users search— List/search users
webhooks — Webhook subscriptions
copper-pp-cli webhooks create— Create a new webhook subscriptioncopper-pp-cli webhooks delete— Delete (unsubscribe) a webhook subscriptioncopper-pp-cli webhooks get— Fetch a webhook subscription by idcopper-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 —
--selectkeeps 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-runshows 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
--idempotentonly when an already-existing create should count as success, and--ignore-missingonly 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 setCOPPER_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. -
configcontains settings likeconfig.tomland profiles.datacontainscredentials.toml,data.db, cookies, and auth sidecars.statecontains persisted queries, jobs, andteach.log.cachecontains regenerable HTTP/cache files. -
Stored secrets live in
credentials.tomlunder the data dir. Existing legacyconfig.tomlsecrets are read for compatibility and leaveconfig.tomlon the first auth write. -
Run
copper-pp-cli doctor --fail-on warnto surface path and credential-location warnings.agent-contextexposes a schema v4pathsblock 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→ showcopper-pp-cli --helpoutput - Starts with
install→ ends withmcp→ 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-cliIf 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
--agentflag:copper-pp-cli <command> [subcommand] [args] --agent - If ambiguous, drill into subcommand help:
copper-pp-cli <command> --help.