Audit and rebalance Pipedrive deal assignments using a Claude Code skill

low complexityCost: Usage-based
Compatible agents

This skill works with any agent that supports the Claude Code skills standard, including Claude Code, Claude Cowork, OpenAI Codex, and Google Antigravity.

Prerequisites

Prerequisites
  • One of the agents listed above
  • Pipedrive account with API access (Growth plan or higher)
  • Slack bot with chat:write permission, added to the target channel
Environment Variables
# Pipedrive API token (Settings > Personal > Tools and Integrations > API)
PIPEDRIVE_API_TOKEN=your_value_here
# Your Pipedrive subdomain (e.g. 'yourcompany' from yourcompany.pipedrive.com)
PIPEDRIVE_COMPANY_DOMAIN=your_value_here
# Slack bot token starting with xoxb- (chat:write scope required)
SLACK_BOT_TOKEN=your_value_here
# Slack channel ID starting with C (right-click channel > View channel details)
SLACK_CHANNEL_ID=your_value_here

Why a Claude Code skill?

The other approaches in this guide handle always-on routing — every new deal gets assigned the moment it arrives. This skill handles the other half: auditing and rebalancing the deals that already exist.

That means you can say:

  • "Show me deal distribution across the team this week"
  • "Rebalance unassigned deals using round-robin"
  • "Redistribute Sarah's deals — she's on leave this month"
  • "Which reps have more than 30% above average deal count?"

The skill contains workflow guidelines, API reference materials, and a Slack message template that the agent reads on demand. When you invoke the skill, Claude reads these files, writes a script on the fly, runs it, and reports results. Unlike the n8n or Pipedrive Workflow Automation approaches, this handles bulk reassignment of existing records naturally — you describe what you want, and the agent figures out the right API calls.

How it works

The skill has three parts:

  1. SKILL.md — instructions telling the agent the audit and rebalance workflow
  2. references/ — Pipedrive API documentation for querying deals, fetching users, and updating deal ownership
  3. templates/ — a Slack Block Kit template for assignment notifications

When you invoke the skill, the agent reads these files, writes a script that follows the patterns, executes it, and reports the results.

What is a Claude Code skill?

A Claude Code skill is a reusable command you add to your project that Claude Code can run on demand. Skills live in a .claude/skills/ directory and are defined by a SKILL.md file that tells the agent what the skill does, when to run it, and what tools it's allowed to use.

In this skill, the agent doesn't run a pre-written script. Instead, SKILL.md provides workflow guidelines and points to reference files — API documentation, message templates — that the agent reads to generate and execute code itself. This is the key difference from a traditional script: the agent can adapt its approach based on what you ask for while still using the right APIs and message formats.

Once installed, you can invoke a skill as a slash command (e.g., /pd-deal-routing), or the agent will use it automatically when you give it a task where the skill is relevant. Skills are portable — anyone who clones your repo gets the same commands.

Step 1: Create the skill directory

mkdir -p .claude/skills/pd-deal-routing/{templates,references}

This creates the layout:

.claude/skills/pd-deal-routing/
├── SKILL.md
├── references/
│   └── pipedrive-deals-api.md
└── templates/
    └── slack-assignment.md

Step 2: Write the SKILL.md

Create .claude/skills/pd-deal-routing/SKILL.md:

---
name: pd-deal-routing
description: Audit and rebalance Pipedrive deal assignments across the sales team using round-robin logic with Slack notifications.
disable-model-invocation: true
allowed-tools: Bash, Read
---
 
## Goal
 
Audit deal distribution across the sales team in Pipedrive and optionally rebalance assignments using round-robin. For each reassigned deal, update the owner in Pipedrive and notify the rep via Slack.
 
## Configuration
 
Read these environment variables:
 
- `PIPEDRIVE_API_TOKEN` — Pipedrive API token from Settings > Personal > Tools and Integrations > API (required)
- `PIPEDRIVE_COMPANY_DOMAIN` — your Pipedrive subdomain, e.g. "yourcompany" from yourcompany.pipedrive.com (required)
- `SLACK_BOT_TOKEN` — Slack bot token starting with xoxb- (required for notifications, skip if not set)
- `SLACK_CHANNEL_ID` — Slack channel ID starting with C (required for channel summary)
 
Default scope: all open deals. The user may request a different filter (e.g., deals created this week, deals in a specific pipeline, unassigned deals only).
 
## Workflow
 
1. Validate that all required env vars are set. If any are missing, print which ones and exit.
2. Fetch active team members from Pipedrive (GET /v1/users, filter by active_flag=1). See `references/pipedrive-deals-api.md`.
3. Query deals to audit or reassign based on the user's request. See `references/pipedrive-deals-api.md`.
4. Apply round-robin assignment or show distribution analysis. Default to audit-only — show counts per rep, ideal distribution, and max imbalance.
5. For each reassigned deal, update owner via PUT /v1/deals/{id} with the new owner_id, then notify the rep via Slack using the format in `templates/slack-assignment.md`.
6. Print a summary of assignments made (or distribution audit if no changes requested).
 
## Important notes
 
- Default to audit-only mode — show the distribution without making changes. Only rebalance when the user explicitly asks.
- Deal owner is updated via `PUT /v1/deals/{id}` with the `owner_id` field in the request body.
- GET /v1/users returns all Pipedrive users. Filter by `active_flag=1` to exclude deactivated accounts.
- The API token does not expire (unlike OAuth tokens). It remains valid until regenerated in Pipedrive settings.
- `SLACK_CHANNEL_ID` must be the channel ID (starts with `C`), not the channel name. The bot must be invited to the channel.
- If `SLACK_BOT_TOKEN` is not set, skip Slack notifications and only print results to the terminal.
- Use `urllib.request` for HTTP calls (no external dependencies required).
- Rate limit: add 200ms delay between Pipedrive API calls to stay within burst limits (2-second rolling window).
- Stage IDs are numeric. Resolve them via /v1/stages if you need human-readable stage names in output.

Step 3: Add reference and template files

references/pipedrive-deals-api.md

Create .claude/skills/pd-deal-routing/references/pipedrive-deals-api.md:

# Pipedrive Deals & Users API Reference
 
## List active users
 
Fetch all users in the Pipedrive account. Filter by `active_flag` to get only active team members.
 
**Request:**
 
```
GET https://api.pipedrive.com/v1/users?api_token=<PIPEDRIVE_API_TOKEN>
```
 
**Response shape:**
 
```json
{
  "success": true,
  "data": [
    {
      "id": 12345,
      "name": "Marcus Johnson",
      "email": "marcus@example.com",
      "active_flag": true
    },
    {
      "id": 23456,
      "name": "Sarah Chen",
      "email": "sarah@example.com",
      "active_flag": true
    }
  ]
}
```
 
Filter to `active_flag: true` only. The `id` field is used as `owner_id` when assigning deals.
 
## List deals
 
Fetch deals with optional filters for status, pipeline, user, and stage.
 
**Request:**
 
```
GET https://api.pipedrive.com/v1/deals?api_token=<PIPEDRIVE_API_TOKEN>&status=open&start=0&limit=500
```
 
**Optional query parameters:**
 
- `status` — filter by deal status: `open`, `won`, `lost`, `deleted` (default: all)
- `user_id` — filter by deal owner
- `stage_id` — filter by pipeline stage
- `start` — pagination offset (default: 0)
- `limit` — page size (max 500)
 
**Response shape:**
 
```json
{
  "success": true,
  "data": [
    {
      "id": 98765,
      "title": "Northwind Traders — Enterprise Platform",
      "value": 42000,
      "currency": "USD",
      "status": "open",
      "stage_id": 3,
      "pipeline_id": 1,
      "user_id": {
        "id": 12345,
        "name": "Marcus Johnson",
        "email": "marcus@example.com"
      },
      "org_id": {
        "name": "Northwind Traders",
        "value": 11111
      },
      "person_id": {
        "name": "Elena Ruiz",
        "value": 54321
      },
      "add_time": "2026-03-01 10:30:00",
      "update_time": "2026-03-05 14:15:00",
      "expected_close_date": "2026-04-15"
    }
  ],
  "additional_data": {
    "pagination": {
      "start": 0,
      "limit": 500,
      "more_items_in_collection": false,
      "next_start": 500
    }
  }
}
```
 
Notes:
- `user_id` is an object containing the owner's `id`, `name`, and `email`. Use `user_id.id` for the numeric owner ID.
- If `more_items_in_collection` is `true`, fetch the next page with `start=<next_start>`.
- To find unassigned deals, look for deals where `user_id` is null or matches a queue/default user.
- `value` is a number. It may be null if no deal value is set.
 
## Update a deal (change owner)
 
**Request:**
 
```
PUT https://api.pipedrive.com/v1/deals/{deal_id}?api_token=<PIPEDRIVE_API_TOKEN>
Content-Type: application/json
```
 
**Body:**
 
```json
{
  "owner_id": 23456
}
```
 
**Response shape:**
 
```json
{
  "success": true,
  "data": {
    "id": 98765,
    "title": "Northwind Traders — Enterprise Platform",
    "user_id": {
      "id": 23456,
      "name": "Sarah Chen"
    }
  }
}
```
 
Notes:
- `owner_id` accepts a Pipedrive user ID (numeric, from the /v1/users endpoint).
- A successful update returns `"success": true` with the updated deal object.
- Rate limit: token-based daily budget (30,000 base tokens x plan multiplier x seats). A single update costs 1 token. Add 200ms between calls to avoid burst limits.
 
## Fetch pipeline stages
 
Resolve numeric stage IDs to human-readable names.
 
**Request:**
 
```
GET https://api.pipedrive.com/v1/stages?api_token=<PIPEDRIVE_API_TOKEN>
```
 
**Response shape:**
 
```json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "Qualified Lead",
      "pipeline_id": 1,
      "order_nr": 1
    },
    {
      "id": 2,
      "name": "Discovery",
      "pipeline_id": 1,
      "order_nr": 2
    }
  ]
}
```
 
Build a dictionary mapping `id` to `name` for display purposes.

templates/slack-assignment.md

Create .claude/skills/pd-deal-routing/templates/slack-assignment.md:

# Slack Assignment Notification Template
 
Use this Block Kit structure when notifying a rep about a newly assigned deal.
 
## Block Kit JSON (per-deal notification)
 
```json
{
  "channel": "<SLACK_CHANNEL_ID>",
  "text": "New deal assigned: <deal_title>",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": ":round_pushpin: *Deal Assigned to You*\n*<deal_title>*\nValue: $<deal_value>\nStage: <stage_name>"
      }
    },
    {
      "type": "context",
      "elements": [
        {
          "type": "mrkdwn",
          "text": "<<pipedrive_link>|View in Pipedrive>"
        }
      ]
    }
  ]
}
```
 
## Block Kit JSON (channel summary after rebalance)
 
```json
{
  "channel": "<SLACK_CHANNEL_ID>",
  "text": "Deal rebalance complete",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": ":arrows_counterclockwise: *Deal Rebalance Complete*\n<count> deals redistributed across <rep_count> reps:\n\n<per_rep_summary>"
      }
    }
  ]
}
```
 
## Notes
 
- The top-level `text` field is required by the Slack API as a fallback for notifications and accessibility.
- The Pipedrive deal link format: `https://<PIPEDRIVE_COMPANY_DOMAIN>.pipedrive.com/deal/<DEAL_ID>`.
- Format the value with comma separators (e.g., $42,000 not $42000). Display "N/A" if value is null.
- To post to a channel, use `SLACK_CHANNEL_ID`. To DM a user, set `channel` to their Slack User ID (starts with U).
- The bot must have `chat:write` scope and be invited to the target channel.
- Post the per-deal notification to the channel. If you have Slack User IDs for reps, DM each rep their assigned deals.

Step 4: Test the skill

Invoke the skill conversationally:

/pd-deal-routing

Start with an audit:

"Show me deal distribution across the team this week"

The agent will query Pipedrive, count deals per rep, and display the current distribution without making any changes. A typical audit looks like:

Checking deal distribution for open deals...
  Fetched 5 active users
  Found 47 open deals
 
  Distribution:
    Marcus Johnson:  14 deals (30%)
    Sarah Chen:       4 deals  (9%)
    Alex Rivera:     12 deals (26%)
    Jordan Lee:      11 deals (23%)
    Priya Patel:      6 deals (13%)
 
  Ideal per rep: 9.4
  Max imbalance: Marcus Johnson (+4.6 above average)
 
  No changes made. Ask me to rebalance if you'd like to redistribute.

Then if the distribution looks off:

"Rebalance deals evenly across the team and notify everyone in Slack"

What the Slack notification looks like

What you'll get
#sales-inbound
Deal Routerapp9:41 AM

Deal Assigned to You

Northwind Traders — Enterprise Platform

Value: $42,000

Stage: Discovery

View in Pipedrive

Because the agent generates code on the fly, you can also make ad hoc requests:

  • "Redistribute Sarah's deals — she's on leave this month" — the agent reassigns only Sarah's deals across the remaining reps
  • "Rebalance unassigned deals only" — the agent filters to deals with no owner and distributes them
  • "Show me deal distribution by pipeline" — the agent groups by pipeline before counting
Test with a few deals first

Before rebalancing your full pipeline, test with a small scope: "Rebalance deals in the Discovery stage only." This lets you verify the round-robin logic and Slack notifications work correctly before applying to all deals.

Step 5: Schedule it (optional)

Option A: Cron + Claude CLI

# Audit every Monday morning (report only, no rebalance)
0 9 * * 1 cd /path/to/your/project && claude -p "Run /pd-deal-routing — show deal distribution for the last 7 days." --allowedTools 'Bash(*)' 'Read(*)'

Option B: GitHub Actions + Claude

name: Pipedrive Deal Distribution Audit
on:
  schedule:
    - cron: '0 14 * * 1'  # 9 AM ET Monday = 2 PM UTC
  workflow_dispatch: {}
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: anthropics/claude-code-action@v1
        with:
          prompt: "Run /pd-deal-routing — show deal distribution for the last 7 days."
          allowed_tools: "Bash(*),Read(*)"
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          PIPEDRIVE_API_TOKEN: ${{ secrets.PIPEDRIVE_API_TOKEN }}
          PIPEDRIVE_COMPANY_DOMAIN: ${{ secrets.PIPEDRIVE_COMPANY_DOMAIN }}
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
          SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }}

Option C: Cowork Scheduled Tasks

Claude Desktop's Cowork supports built-in scheduled tasks. Open a Cowork session, type /schedule, and configure the cadence — hourly, daily, weekly, or weekdays only. Each scheduled run has full access to your connected tools, plugins, and MCP servers.

Scheduled tasks only run while your computer is awake and Claude Desktop is open. If a run is missed, Cowork executes it automatically when the app reopens. For always-on scheduling, use GitHub Actions (Option B) instead. Available on all paid plans (Pro, Max, Team, Enterprise).

Troubleshooting

When to use this approach

  • A rep leaves or joins the team and you need to redistribute their deals
  • Deal assignments have drifted out of balance over the quarter
  • You want a weekly audit of deal distribution without building dashboards
  • You need a one-time bulk reassignment before turning on always-on routing via n8n

When to switch approaches

  • You need real-time assignment on deal creation (use n8n or Pipedrive Workflow Automation)
  • You want always-on round-robin without human involvement (use n8n)
  • Multiple team members need to manage the rep roster through a visual interface (use n8n)

Common questions

Why not use this for always-on routing?

This skill runs on demand or on a schedule — it queries deals that already exist and redistributes them. For real-time routing where each new deal is assigned the instant it's created, you need a webhook-based approach like n8n that triggers on deal creation. The two approaches complement each other: n8n handles incoming deals, this skill handles periodic rebalancing.

How many Pipedrive API tokens does a rebalance use?

1 call to fetch users + 1 call per page of deals (up to 500 per page) + 1 PUT call per reassigned deal. For 50 open deals where 30 need reassignment, that's roughly 32 API tokens. Pipedrive allocates 30,000 base tokens per day (multiplied by plan tier and seat count), so even large rebalances are well within limits.

What rebalancing strategy does the agent use?

The agent uses round-robin by default — it cycles through the active rep list and assigns one deal at a time until all deals are distributed. If you want a different strategy, just ask: "Distribute proportionally based on quota" or "Give the new hire fewer deals." The agent adapts because it generates code from the workflow guidelines, not from a fixed script.

Does the agent rebalance automatically or ask first?

By default, the agent only audits — it shows the current distribution and reports imbalances. You must explicitly ask it to rebalance (e.g., "go ahead and redistribute evenly"). This is controlled by the "audit-only by default" rule in SKILL.md.

Cost

  • Claude API — $0.01-0.05 per invocation (the agent reads files and generates code)
  • Pipedrive API — included in Growth plan and above, token-based daily budget (30,000+ tokens/day)
  • Slack API — included in all plans, no per-call cost
  • GitHub Actions (if scheduled) — free tier includes 2,000 minutes/month

Looking to scale your AI operations?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.