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.
Authenticate with environment variables
Section titled “Authenticate with environment variables”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:
export PDCLI_COMPANY_DOMAIN=acmeexport PDCLI_API_TOKEN=<personal-api-token>pdcli deal list --status openEnv 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.
Output is JSON when piped
Section titled “Output is JSON when piped”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:
pdcli deal get 42 --output jsonRefine without leaving the CLI using --jq (native jq), --fields, or
--output yaml|csv. See Output & filtering.
Branch on exit codes
Section titled “Branch on exit codes”pdcli uses sysexits codes so a script can react to the failure class without parsing text:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic error |
| 64 | Usage / bad flags or arguments (unknown flag, missing arg, invalid value, missing CSV column) |
| 65 | Bad input data (API 400 / 422) |
| 69 | Service unavailable (API 5xx, or unreachable) |
| 70 | Internal software error (unexpected — a genuine bug, not a usage mistake) |
| 75 | Rate limited (API 429) — retry later |
| 77 | Not authenticated / forbidden (API 401 / 403) |
| 78 | Configuration 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"}Self-description
Section titled “Self-description”Every command and flag is discoverable:
pdcli --helppdcli deal --helppdcli deal create --helpThe 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.
The host-locked api escape hatch
Section titled “The host-locked api escape hatch”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.
pdcli api GET /api/v2/pipelinespdcli api POST /api/v2/deals --body '{"title":"Raw deal"}'api always prints raw JSON. Both v1 and v2 paths work.
Machine-readable docs
Section titled “Machine-readable docs”The whole documentation site is available as plain text for ingestion:
- /pdcli/llms.txt — index of pages.
- /pdcli/llms-full.txt — the full docs in one file.
- /pdcli/llms-small.txt — a compact variant.
End-to-end example
Section titled “End-to-end example”Find open deals owned by user 42 that are missing a value, and report how many:
export PDCLI_COMPANY_DOMAIN=acmeexport 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"