Use when running xaffinity CLI commands, or when user asks to search, find, get, export, or list people, companies, opportunities, lists, or CRM data from Affinity via command line. Also use when user mentions "xaffinity", "export to CSV", or needs help with Affinity CLI commands.
/plugin marketplace add yaniv-golan/affinity-sdk/plugin install cli@xaffinityThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Use this skill when running xaffinity commands to interact with Affinity CRM.
STOP. Before doing ANYTHING else, run this command:
xaffinity config check-key --json
This MUST be your first action when handling any Affinity request.
If "configured": true - Use the pattern field from the output for ALL subsequent commands:
"pattern": "xaffinity --dotenv --readonly <command> --json" -> use --dotenv"pattern": "xaffinity --readonly <command> --json" -> no --dotenv neededIf "configured": false - Stop and help user set up:
xaffinity config setup-key (do NOT run it for them - it's interactive)Always use --readonly unless user explicitly requests writes.
Write operations include creating, updating, or deleting:
IMPORTANT: Before executing ANY delete command, you MUST:
--yesExample flow:
User: "Delete person 123"
You: xaffinity person get 123 --readonly --json
You: "This will permanently delete John Smith (ID: 123, email: john@example.com).
Type 'yes' to confirm deletion."
[Stop here and wait for user's response]
User: "yes"
You: xaffinity person delete 123 --yes
Destructive commands: person delete, company delete, opportunity delete, note delete, reminder delete, field delete, list entry delete, interaction delete
Note: This is conversation-based confirmation - you ask, then wait for the user's next message. The --yes flag bypasses the CLI's interactive prompt, but you must get explicit user confirmation in the conversation first.
| Pattern | Purpose |
|---|---|
--readonly | Prevent accidental data modification (ALWAYS use) |
--json | Structured, parseable output (ALWAYS use) |
--all | Fetch all pages (for exports) |
--yes | Skip confirmation on delete commands (use after user confirms) |
--help | Discover command options (USE THIS, don't guess) |
# Search/Get entities
xaffinity person search "John Smith" --json
xaffinity person get 123 --json
xaffinity person get email:alice@example.com --json
xaffinity company get domain:acme.com --json
# List all
xaffinity person ls --all --json
xaffinity company ls --all --json
xaffinity list ls --json
# Export to CSV
xaffinity person ls --all --csv contacts.csv --csv-bom
xaffinity list export LIST_ID --all --csv output.csv --csv-bom
# Export with expanded associations
xaffinity list export LIST_ID --expand people --all --csv output.csv
xaffinity list export LIST_ID --expand people --expand companies --all --csv output.csv
# Filter on custom fields
xaffinity person ls --filter 'Department = "Sales"' --all --json
xaffinity list export LIST_ID --filter 'Status = "Active"' --all --json
# Filter syntax
# = exact match
# =~ contains
# =^ starts with
# =$ ends with
# != * is NULL
# & AND
# | OR
# Examples
--filter 'Status = "Active" & Region = "US"'
--filter 'Status = "New" | Status = "Pending"'
Cannot filter: name, email, domain, type - use --all and post-process with jq.
The interactions API only shows meetings with external contacts.
# Returns NOTHING for internal-only meetings:
xaffinity interaction ls --person-id 123 --type meeting ...
# Workaround - use notes:
xaffinity note ls --person-id 123 --json # Filter for isMeeting: true
# WRONG:
xaffinity interaction ls --person-id 123 --type meeting --json
# CORRECT:
xaffinity interaction ls --person-id 123 --type meeting \
--start-time 2025-01-01 --end-time 2025-12-31 --json
"Last Meeting", "Next Meeting" are UI-only. Use:
xaffinity person search "Alice" --with-interaction-dates --json
xaffinity company search "Acme" --with-interaction-dates --json
All entries fetched, then filtered locally. For efficiency:
# INEFFICIENT - 3 API calls fetching same data:
xaffinity list export 123 --filter 'Status = "New"' --all --json > new.json
xaffinity list export 123 --filter 'Status = "Active"' --all --json > active.json
# BETTER - 1 API call, post-process:
xaffinity list export 123 --all --json > all.json
jq '[.[] | select(.Status == "New")]' all.json > new.json
jq '[.[] | select(.Status == "Active")]' all.json > active.json
Cannot move/copy opportunities between lists. Created with --list-id.
Companies with global: true cannot be modified.
| Task | Command |
|---|---|
| Find person by email | person get email:user@example.com |
| Find company by domain | company get domain:acme.com |
| Export all contacts | person ls --all --csv contacts.csv --csv-bom |
| Export pipeline with people | list export LIST_ID --expand people --all --csv out.csv |
| Get command help | xaffinity <command> --help |
pip install "affinity-sdk[cli]"
xaffinity --helpUse when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.