CI recipes
pdcli is built for non-interactive use. In CI, authenticate with environment variables (no
keychain, no prompts), and use deterministic flags so a run either succeeds or fails cleanly.
Authenticate with env vars
Section titled “Authenticate with env vars”In CI, set the company domain and a personal API token as environment variables instead of
running pdcli auth login. The token never touches disk and stays out of shell history.
PDCLI_COMPANY_DOMAIN=acme PDCLI_API_TOKEN=xxxxxxxx pdcli deal listStore the token as an encrypted secret and inject it per step. In GitHub Actions:
name: pipedrive-auditon: schedule: - cron: '0 6 * * 1' # Mondays 06:00 UTC workflow_dispatch:
jobs: audit: runs-on: ubuntu-latest env: PDCLI_COMPANY_DOMAIN: acme PDCLI_API_TOKEN: ${{ secrets.PIPEDRIVE_API_TOKEN }} steps: - uses: actions/setup-node@v5 with: node-version: 20 - run: npm install -g @wavyx/pdcli - run: pdcli audit --strictEnv auth means no OS keychain is required on the runner — env vars take precedence over the keychain, so writes (login) are never attempted. See Security model.
Deterministic flags
Section titled “Deterministic flags”CI runs should be predictable. Two flags matter most:
--no-retry— disable automatic backoff on429and5xx. The command fails immediately with exit75(rate limited) or69(service unavailable) instead of sleeping through a retry window. Use it when you'd rather fail fast and let the CI scheduler retry the job.--timeout <ms>— cap each request (default 30000). A hung network won't stall the job past your budget.
pdcli deal list --no-retry --timeout 10000 --output jsonPiped output defaults to JSON, so pdcli ... | jq works without --output json. Errors go
to stderr as JSON; see Exit codes.
Audit as a data-quality gate
Section titled “Audit as a data-quality gate”pdcli audit runs 11 hygiene checks. With --strict it exits 1 when any must-severity
check has findings (stale deals, duplicate persons, missing fields, …), which fails the job.
pdcli audit --strict# narrow the gate to specific checks:pdcli audit --checks duplicate-persons,missing-fields --strictExit 1 = the gate caught something. Wire it as a required check on a schedule, or before a
deploy that depends on clean data. See Data-hygiene audit.
Nightly backup
Section titled “Nightly backup”Export the whole account to a JSON tree on a schedule. --resume skips resources already
written, so a re-run after a transient failure continues instead of restarting.
jobs: backup: runs-on: ubuntu-latest env: PDCLI_COMPANY_DOMAIN: acme PDCLI_API_TOKEN: ${{ secrets.PIPEDRIVE_API_TOKEN }} steps: - uses: actions/setup-node@v5 with: { node-version: 20 } - run: npm install -g @wavyx/pdcli - run: pdcli backup --dir ./pipedrive-backup - uses: actions/upload-artifact@v4 with: name: pipedrive-backup path: ./pipedrive-backupbackup paces itself against the token budget, so it's safe to run alongside other jobs.
Incremental pulls
Section titled “Incremental pulls”For a cheap scheduled sync, pull only what changed since the last run rather than the whole
account. The list commands take --updated-since (RFC3339, no fractional seconds), and
--limit 500 keeps each page at the maximum so you make the fewest calls:
pdcli deal list --updated-since 2026-06-01T00:00:00Z --limit 500 --output jsonPass the previous run's timestamp on each schedule tick and you sync deltas instead of the
full set. (product list supports --updated-since only — products have no
--updated-until.)
If the daily token budget runs out, pdcli fails fast: a 429 carrying
x-daily-ratelimit-token-remaining: 0 is not retried (backoff would stall until the daily reset at midnight server time),
so the command exits 75 immediately with a clear Daily API token budget exhausted
message. Add --verbose to log the remaining daily budget after each request, so a job can
warn before it hits the wall.
Bulk import in a pipeline
Section titled “Bulk import in a pipeline”Validate CSV rows first with --dry-run, then import with --yes to skip the confirmation
prompt (there's no TTY in CI):
pdcli person import people.csv --dry-run # fails (exit 65) on bad rowspdcli person import people.csv --yes # creates rowsA failed --dry-run exits non-zero, so chaining with && stops the import if validation
fails. CSV headers map to fields, custom fields by human name. See
Bulk operations & CSV import.