Skip to content
Agentmart
Submit a tool

reassign-deactivated-owners

Reassign contacts and companies from deactivated team members to active owners.

Skill by tomgranot

IndexedMIT★ 77
Write a review

Chalk Labs

Need help deploying reassign-deactivated-owners?

Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.

Book a call

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/tomgranot-reassign-deactivated-owners

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Reassign contacts and companies from deactivated team members to active owners. Fully automated via the HubSpot Owners API and Batch Update API. Includes territory analysis for informed reassignment decisions.

Use cases: backend, sales

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Python package

Network hosts it talks to 2

  • api.hubapi.com · found in code
  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/reassign-deactivated-owners

What's inside

Skill name
reassign-deactivated-owners
Description
Reassign contacts and companies from deactivated team members to active owners. Fully automated via the HubSpot Owners API and Batch Update API. Includes territory analysis for informed reassignment decisions.
License
MIT
Metadata
author: tomgranot · version: 1.1 · category: database-hygiene
Instructions
510 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts
  • Instructions call the network

Agent Skills spec issues

  • body: 510 lines (spec recommends under 500)

Files (4)

  • SKILL.md
  • after.py · scripts/
  • before.py · scripts/
  • execute.py · scripts/

SKILL.md (first lines)

# Reassign Contacts From Deactivated Owners

## Purpose

When team members leave an organization, their HubSpot-owned contacts and companies become orphaned. Leads are not being worked, accounts are not being managed, and any automation that relies on Contact owner or Company owner routes to dead ends. This skill identifies deactivated owners, analyzes their contact portfolios, and batch-reassigns ownership via the API.

## Prerequisites

- A HubSpot service key (`HUBSPOT_ACCESS_TOKEN` in `.env`; an existing legacy private app token also works) with `crm.objects.contacts.read`, `crm.objects.contacts.write`, `crm.objects.companies.read`, `crm.objects.companies.write`, and `crm.objects.owners.read` scopes
- Python 3.10+ with `uv` for package management
- A `.env` file containing `HUBSPOT_ACCESS_TOKEN`
- A decision from team leadership on the reassignment strategy (see Plan stage)

## Scripts

Script paths are relative to this skill's directory. Run them from your project directory so `.env`, `data/`, and `reports/` resolve there: prefix each path with wherever this skill is installed (`skills/reassign-deactivated-owners/` in a clone of the repo).

| Stage | Script | Run with |
|-------|--------|----------|
| Before | [`scripts/before.py`](./scripts/before.py) | `uv run scripts/before.py` |
| Execute | [`scripts/execute.py`](./scripts/execute.py) | `uv run scripts/execute.py` |
| After | [`scripts/after.py`](./scripts/after.py) | `uv run scripts/after.py` |

`before.py` lists deactivated owners and counts their records, exporting the CSV baseline. `execute.py` batch-reassigns to the configured target owner. `after.py` verifies zero records remain with deactivated owners.

## CRITICAL: Retrieving Deactivated Owners

The Owners API requires the `archived=true` parameter to retrieve deactivated users. The default endpoint only returns active owners. This is the most commonly missed detail.

```python
# CORRECT - returns deactivated/archived owners
requests.get(f"{BASE}/crm/v3/owners", headers=headers,
             params={"limit": 500, "archived": "true"})

# DEFAULT - returns only active owners
requests.get(f"{BASE}/crm/v3/owners", headers=headers,
             params={"limit": 500})
```

Deactivated owners have `"archived": true` in their response object.

## Interview: Gather Requirements

Before executing, collect the following information from the user:

**Q1: Who should contacts from [deactivated user X] be reassigned to?**
- Run the Before State script first to identify deactivated users and their contact counts, then ask this question for each deactivated user found
- Examples: "Assign Jane Doe's 500 contacts to John Smith", "Distribute evenly across the sales team", "Assign all to the Marketing Team user"
- Default: No default -- this requires an explicit business decision for each deactivated user

**Q2: Should unowned contacts be assigned to a default user? If so, who?**
- Examples: "Yes, assign to our Integration User", "Yes, round-robin across the sales team", "No, leave them unowned for now"
- Default: No -- unowned contacts are a separate strategic decision from deactivated-owner contacts

## Execution Pattern

This skill follows a 4-stage execution pattern: **Plan -> Before -> Execute -> After**.

### Stage 1: Plan

Before writing any code, confirm with the user:

1. **Reassignment strategy** -- one of three options:
   - **Option A (Unassign)**: Set owner to blank. Best if no one is actively working these contacts and the team wants to start fresh with new assignment rules.
   - **Option B (Distribute to active team members)**: Round-robin across current reps. Best if there are active reps who should work these leads.
   - **Option C (Assign to shared owner)**: Assign to a single placeholder user (e.g., "Integration User" or "Marketing Team"). Good middle ground.

2. **Territory analysis**: Before reassigning, the user may want to analyze the portfolio of each deactivated owner (customer %, lifecycle distribution, industry breakdown) to make informed decisions about who gets which contacts.

3. **Unassigned contacts**: Ask whether the user also wants to address contacts that have no owner at all. This is a separate strategic decision.

4. **Preserve historical ownership**: Ask whether to create a "Previous Owner" custom property to retain a record of the original assignment.

### Stage 2: Before

Discover all deactivated owners and analyze their contact/company portfolios.

```python
"""
Before State: Identify deactivated owners and their contact/company counts.
"""
import os
import csv
import time
import requests
from dotenv import load_dotenv

load_dotenv()

TOKEN = os.environ["HUBSPOT_ACCESS_TOKEN"]
BASE = "https://api.hubapi.com"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json",
}

# --- Step 1: Get all owners (active + deactivated) ---
print("Fetching owners...")

# Active owners
resp_active = requests.get(
    f"{BASE}/crm/v3/owners", headers=headers, params={"limit": 500},
)
resp_active.raise_for_status()
active_owners = resp_active.json().get("results", [])

# CRITICAL: Use archived=true for deactivated owners
resp_archived = requests.get(
    f"{BASE}/crm/v3/owners", headers=headers,
    params={"limit": 500, "archived": "true"},
)
resp_archived.raise_for_status()
deactivated_owners = resp_archived.json().get("results", [])

print(f"Active owners: {len(active_owners)}")
print(f"Deactivated owners: {len(deactivated_owners)}")

print("\nActive owners:")
for o in active_owners:

From TomGranot/hubspot-admin-skills, MIT.

More from TomGranot/hubspot-admin-skills

Signals

Stars
77
Forks
18
Open issues
0
Last commit (this folder)
Oct 12, 2026 (21 days ago)

From GitHub (fetched Oct 24, 2026).

Details

Version
1.1
License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from tomgranot

Done for you

Rather not set it up yourself?

Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.

See how we build AI agents →Read the case studies

Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.

launches shipped
50+
launches shipped
media placements
500+
media placements
founders on every account
2
founders on every account

Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block

Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.

Get reassign-deactivated-owners running in your business

Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying reassign-deactivated-owners?

CallGet help
Cite this page

reassign-deactivated-owners skill. Agent Mart. https://agentmart.dev/skills/tomgranot-reassign-deactivated-owners (last updated 2026-10-24)