Skip to content
pdcli
Get started

Quickstart for AI agents

pdcli is built to be driven by agents (Claude Code, Codex, CI bots). It is self-describing, emits machine-readable JSON, and uses deterministic exit codes. This page covers the conventions an agent needs.

Avoid the interactive auth login flow. Set credentials in the environment so no prompt blocks the run and no token lands in command history or a stored profile:

Terminal window
export PDCLI_COMPANY_DOMAIN=acme
export PDCLI_API_TOKEN=<personal-api-token>
pdcli deal list --status open

Env vars take precedence over a stored profile and require no keychain, which makes them the right choice for ephemeral or headless environments. The token must stay in the environment, never on a flag like --api-token where it would be visible in the process list.

In a TTY the default output is a table; when stdout is not a TTY (piped or captured), pdcli defaults to JSON. Be explicit to remove all doubt:

Terminal window
pdcli deal get 42 --output json

Refine without leaving the CLI using --jq (native jq), --fields, or --output yaml|csv. See Output & filtering.

pdcli uses sysexits codes so a script can react to the failure class without parsing text:

CodeMeaning
0Success
1Generic error
64Usage / bad flags or arguments (unknown flag, missing arg, invalid value, missing CSV column)
65Bad input data (API 400 / 422)
69Service unavailable (API 5xx, or unreachable)
70Internal software error (unexpected — a genuine bug, not a usage mistake)
75Rate limited (API 429) — retry later
77Not authenticated / forbidden (API 401 / 403)
78Configuration error (e.g. API 402, missing domain)

Errors print a JSON object on stderr whenever output is JSON — with --output json, a json profile default, or when stdout is piped (the same TTY→JSON rule as success output), so a non-interactive consumer always gets a parseable failure:

{
"error": "ApiError",
"message": "Pipedrive API 401: invalid token",
"exitCode": 77,
"statusCode": 401,
"path": "/api/v2/deals"
}

Every command and flag is discoverable:

Terminal window
pdcli --help
pdcli deal --help
pdcli deal create --help

The full reference is at /pdcli/reference/commands/. Beyond CRUD, agents have deal history <id> for a deal's field-change audit trail, deal product for line items, field create/field update/field option to manage the custom-field schema, deal participant and deal/person/org follower for the people around a record, task for project action items, lead convert/deal convert (with --wait) to change a record's type, deal summary for a server-side per-currency value rollup, time-intelligence metrics (metrics aging/slippage/conversion-matrix) plus audit stage-skips for stage-skip compliance — all mined from per-deal changelogs — metrics forecast for a per-currency commit/best-case/weighted close-month forecast, rep scorecard for per-owner performance, digest for the whole Monday packet in one fetch (--format md|html --out for a cron → Slack/email artifact), changes for an incremental cross-entity change feed with a self-advancing watermark (no receiver to host, unlike webhooks), deal context <id> for a one-call denormalized, prompt-ready bundle (deal + person + org + activities + notes + products + flags), backup diff for a zero-API field-level diff of two snapshots, watch for an exit-code-gated anomaly poller that fires only on findings new since the last run (pdcli watch || notify), sync warehouse for an incremental NDJSON export with per-entity high-water marks, and --updated-since on the list commands for incremental polling.

For writes that must be safe to retry, person/org/deal upsert match a record by --by (a built-in key or a searchable custom field) and then create or PATCH only what changed — and refuse with exit 65 when more than one record matches, so an agent never silently writes the wrong one. person import/org import --upsert --match-on <field> apply the same match-or-create per CSV row, reporting created/updated/unchanged counts. Pair upsert with an external key (a custom field carrying your system's ID) for clean, repeatable sync.

When no dedicated command exists, call any endpoint directly. The request is host-locked to your authenticated company domain (or the OAuth api_domain), so a hallucinated host cannot leak the token. There is no generic data host.

Terminal window
pdcli api GET /api/v2/pipelines
pdcli api POST /api/v2/deals --body '{"title":"Raw deal"}'

api always prints raw JSON. Both v1 and v2 paths work.

The whole documentation site is available as plain text for ingestion:

Find open deals owned by user 42 that are missing a value, and report how many:

Terminal window
export PDCLI_COMPANY_DOMAIN=acme
export PDCLI_API_TOKEN=$PIPEDRIVE_TOKEN
deals=$(pdcli deal list --status open --owner 42 --output json)
status=$?
if [ "$status" -ne 0 ]; then
echo "pdcli failed (exit $status)" >&2 # branch on the sysexits code
exit "$status"
fi
count=$(echo "$deals" | jq '[.[] | select(.value == null)] | length')
echo "Open deals missing a value: $count"
pdcli v0.18.0 · MIT · not affiliated with Pipedrive