freshsales
Freshsales CRM integration — manage contacts, leads, deals, accounts, tasks, and sales sequences via the Freshsales API. Track deal pipelines, automate lead assignments, log activities, and generate sales reports. Built for AI agents — Python stdlib only, no dependencies. Use for sales CRM, contact management, deal tracking, pipeline reporting, and sales automation.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-freshsales
Repository
Skill path: skills/aiwithabidi/freshsales
Freshsales CRM integration — manage contacts, leads, deals, accounts, tasks, and sales sequences via the Freshsales API. Track deal pipelines, automate lead assignments, log activities, and generate sales reports. Built for AI agents — Python stdlib only, no dependencies. Use for sales CRM, contact management, deal tracking, pipeline reporting, and sales automation.
Open repositoryBest for
Primary workflow: Analyze Data & AI.
Technical facets: Full Stack, Backend, Data / AI, Integration.
Target audience: everyone.
License: MIT.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install freshsales into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding freshsales to shared team environments
- Use freshsales for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: freshsales
description: "Freshsales CRM integration — manage contacts, leads, deals, accounts, tasks, and sales sequences via the Freshsales API. Track deal pipelines, automate lead assignments, log activities, and generate sales reports. Built for AI agents — Python stdlib only, no dependencies. Use for sales CRM, contact management, deal tracking, pipeline reporting, and sales automation."
homepage: https://www.agxntsix.ai
license: MIT
compatibility: Python 3.10+ (stdlib only — no dependencies)
metadata: {"openclaw": {"emoji": "🟢", "requires": {"env": ["FRESHSALES_API_KEY", "FRESHSALES_DOMAIN"]}, "primaryEnv": "FRESHSALES_API_KEY", "homepage": "https://www.agxntsix.ai"}}
---
# 🟢 Freshsales
Freshsales CRM integration — manage contacts, leads, deals, accounts, tasks, and sales sequences via the Freshsales API.
## Features
- **Manage contacts** — create, update, search, and segment
- **Lead tracking** — capture, qualify, assign, and convert
- **Deal pipeline** — stages, values, forecasting, and won/lost
- **Account management** — company profiles and hierarchies
- **Task management** — create, assign, and track sales tasks
- **Activity logging** — calls, emails, meetings, and notes
- **Sales sequences** — view and manage outreach campaigns
- **Search** across contacts, leads, deals, and accounts
- **Reports** — pipeline value, conversion rates, activity metrics
- **Filters** — custom views with field-level filtering
## Requirements
| Variable | Required | Description |
|----------|----------|-------------|
| `FRESHSALES_API_KEY` | ✅ | API key/token for Freshsales |
| `FRESHSALES_DOMAIN` | ✅ | Your Freshsales domain (e.g. yourorg.freshsales.io) |
## Quick Start
```bash
# List contacts
python3 {baseDir}/scripts/freshsales.py contacts --limit 20
```
```bash
# Get contact details
python3 {baseDir}/scripts/freshsales.py contact-get 12345
```
```bash
# Create a contact
python3 {baseDir}/scripts/freshsales.py contact-create '{"first_name":"Jane","last_name":"Doe","email":"[email protected]"}'
```
```bash
# Update a contact
python3 {baseDir}/scripts/freshsales.py contact-update 12345 '{"lead_score":85}'
```
## Commands
### `contacts`
List contacts.
```bash
python3 {baseDir}/scripts/freshsales.py contacts --limit 20
```
### `contact-get`
Get contact details.
```bash
python3 {baseDir}/scripts/freshsales.py contact-get 12345
```
### `contact-create`
Create a contact.
```bash
python3 {baseDir}/scripts/freshsales.py contact-create '{"first_name":"Jane","last_name":"Doe","email":"[email protected]"}'
```
### `contact-update`
Update a contact.
```bash
python3 {baseDir}/scripts/freshsales.py contact-update 12345 '{"lead_score":85}'
```
### `leads`
List leads.
```bash
python3 {baseDir}/scripts/freshsales.py leads --limit 20 --sort updated_at
```
### `lead-create`
Create a lead.
```bash
python3 {baseDir}/scripts/freshsales.py lead-create '{"first_name":"John","company":"Acme"}'
```
### `deals`
List deals.
```bash
python3 {baseDir}/scripts/freshsales.py deals --limit 20
```
### `deal-create`
Create a deal.
```bash
python3 {baseDir}/scripts/freshsales.py deal-create '{"name":"Acme Upgrade","amount":50000}'
```
### `deal-update`
Update deal stage.
```bash
python3 {baseDir}/scripts/freshsales.py deal-update 789 '{"deal_stage_id":3}'
```
### `accounts`
List accounts.
```bash
python3 {baseDir}/scripts/freshsales.py accounts --limit 20
```
### `tasks`
List tasks.
```bash
python3 {baseDir}/scripts/freshsales.py tasks --limit 10 --status open
```
### `task-create`
Create a task.
```bash
python3 {baseDir}/scripts/freshsales.py task-create '{"title":"Follow up with Acme","due_date":"2026-03-01"}'
```
### `search`
Search across all entities.
```bash
python3 {baseDir}/scripts/freshsales.py search "Acme"
```
### `activities`
List recent activities.
```bash
python3 {baseDir}/scripts/freshsales.py activities --limit 20
```
### `pipeline`
Pipeline summary.
```bash
python3 {baseDir}/scripts/freshsales.py pipeline
```
## Output Format
All commands output JSON by default. Add `--human` for readable formatted output.
```bash
# JSON (default, for programmatic use)
python3 {baseDir}/scripts/freshsales.py contacts --limit 5
# Human-readable
python3 {baseDir}/scripts/freshsales.py contacts --limit 5 --human
```
## Script Reference
| Script | Description |
|--------|-------------|
| `{baseDir}/scripts/freshsales.py` | Main CLI — all Freshsales operations |
## Data Policy
This skill **never stores data locally**. All requests go directly to the Freshsales API and results are returned to stdout. Your data stays on Freshsales servers.
## Credits
---
Built by [M. Abidi](https://www.linkedin.com/in/mohammad-ali-abidi) | [agxntsix.ai](https://www.agxntsix.ai)
[YouTube](https://youtube.com/@aiwithabidi) | [GitHub](https://github.com/aiwithabidi)
Part of the **AgxntSix Skill Suite** for OpenClaw agents.
📅 **Need help setting up OpenClaw for your business?** [Book a free consultation](https://cal.com/agxntsix/abidi-openclaw)
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"owner": "aiwithabidi",
"slug": "freshsales",
"displayName": "Freshsales",
"latest": {
"version": "1.0.0",
"publishedAt": 1772592769213,
"commit": "https://github.com/openclaw/skills/commit/960cfc842fade770adf3f65705b7fe14226c1138"
},
"history": []
}
```
### scripts/freshsales.py
```python
#!/usr/bin/env python3
"""Freshsales CLI — comprehensive API integration for AI agents.
Full CRUD operations, search, reporting, and automation.
Zero dependencies beyond Python stdlib.
"""
import argparse
import json
import os
import sys
import urllib.request
import urllib.error
import urllib.parse
from datetime import datetime, timezone
API_BASE = "https://{domain}/api"
def get_token():
"""Get API token from environment."""
token = os.environ.get("FRESHSALES_API_KEY", "")
if not token:
env_path = os.path.join(
os.environ.get("WORKSPACE", os.path.expanduser("~/.openclaw/workspace")),
".env"
)
if os.path.exists(env_path):
with open(env_path) as f:
for line in f:
line = line.strip()
if line.startswith("FRESHSALES_API_KEY="):
token = line.split("=", 1)[1].strip().strip('"').strip("'")
break
if not token:
print(f"Error: FRESHSALES_API_KEY not set", file=sys.stderr)
sys.exit(1)
return token
def api(method, path, data=None, params=None):
"""Make an API request."""
token = get_token()
url = f"{API_BASE}{path}"
if params:
qs = urllib.parse.urlencode({k: v for k, v in params.items() if v is not None})
if qs:
url = f"{url}?{qs}"
body = json.dumps(data).encode() if data else None
req = urllib.request.Request(url, data=body, method=method)
req.add_header("Authorization", f"Bearer {token}")
req.add_header("Content-Type", "application/json")
req.add_header("Accept", "application/json")
try:
resp = urllib.request.urlopen(req, timeout=30)
raw = resp.read().decode()
return json.loads(raw) if raw.strip() else {"ok": True}
except urllib.error.HTTPError as e:
err_body = e.read().decode()
print(json.dumps({"error": True, "code": e.code, "message": err_body}), file=sys.stderr)
sys.exit(1)
def output(data, human=False):
"""Output data as JSON or human-readable."""
if human and isinstance(data, list):
for item in data:
if isinstance(item, dict):
for k, v in item.items():
print(f" {k}: {v}")
print()
else:
print(item)
elif human and isinstance(data, dict):
for k, v in data.items():
print(f" {k}: {v}")
else:
print(json.dumps(data, indent=2, default=str))
def cmd_contacts(args):
"""List contacts."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/contacts/{args.id}")
else:
data = api("GET", "/contacts", params=params)
output(data, getattr(args, 'human', False))
def cmd_contact_get(args):
"""Get contact details."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/contact/{args.id}")
else:
data = api("GET", "/contact/get", params=params)
output(data, getattr(args, 'human', False))
def cmd_contact_create(args):
"""Create a contact."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("POST", f"/contact/{args.id}")
else:
data = api("POST", "/contact/create", params=params)
output(data, getattr(args, 'human', False))
def cmd_contact_update(args):
"""Update a contact."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("PUT", f"/contact/{args.id}")
else:
data = api("PUT", "/contact/update", params=params)
output(data, getattr(args, 'human', False))
def cmd_leads(args):
"""List leads."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/leads/{args.id}")
else:
data = api("GET", "/leads", params=params)
output(data, getattr(args, 'human', False))
def cmd_lead_create(args):
"""Create a lead."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("POST", f"/lead/{args.id}")
else:
data = api("POST", "/lead/create", params=params)
output(data, getattr(args, 'human', False))
def cmd_deals(args):
"""List deals."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/deals/{args.id}")
else:
data = api("GET", "/deals", params=params)
output(data, getattr(args, 'human', False))
def cmd_deal_create(args):
"""Create a deal."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("POST", f"/deal/{args.id}")
else:
data = api("POST", "/deal/create", params=params)
output(data, getattr(args, 'human', False))
def cmd_deal_update(args):
"""Update deal stage."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("PUT", f"/deal/{args.id}")
else:
data = api("PUT", "/deal/update", params=params)
output(data, getattr(args, 'human', False))
def cmd_accounts(args):
"""List accounts."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/accounts/{args.id}")
else:
data = api("GET", "/accounts", params=params)
output(data, getattr(args, 'human', False))
def cmd_tasks(args):
"""List tasks."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/tasks/{args.id}")
else:
data = api("GET", "/tasks", params=params)
output(data, getattr(args, 'human', False))
def cmd_task_create(args):
"""Create a task."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("POST", f"/task/{args.id}")
else:
data = api("POST", "/task/create", params=params)
output(data, getattr(args, 'human', False))
def cmd_search(args):
"""Search across all entities."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/search/{args.id}")
else:
data = api("GET", "/search", params=params)
output(data, getattr(args, 'human', False))
def cmd_activities(args):
"""List recent activities."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/activities/{args.id}")
else:
data = api("GET", "/activities", params=params)
output(data, getattr(args, 'human', False))
def cmd_pipeline(args):
"""Pipeline summary."""
params = {}
if hasattr(args, 'limit') and args.limit:
params["limit"] = args.limit
if hasattr(args, 'id') and args.id:
data = api("GET", f"/pipeline/{args.id}")
else:
data = api("GET", "/pipeline", params=params)
output(data, getattr(args, 'human', False))
COMMANDS = {
"contacts": cmd_contacts,
"contact-get": cmd_contact_get,
"contact-create": cmd_contact_create,
"contact-update": cmd_contact_update,
"leads": cmd_leads,
"lead-create": cmd_lead_create,
"deals": cmd_deals,
"deal-create": cmd_deal_create,
"deal-update": cmd_deal_update,
"accounts": cmd_accounts,
"tasks": cmd_tasks,
"task-create": cmd_task_create,
"search": cmd_search,
"activities": cmd_activities,
"pipeline": cmd_pipeline,
}
def main():
parser = argparse.ArgumentParser(
description="Freshsales CLI — AI agent integration",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("command", choices=list(COMMANDS.keys()), help="Command to run")
parser.add_argument("args", nargs="*", help="Command arguments")
parser.add_argument("--human", action="store_true", help="Human-readable output")
parser.add_argument("--limit", type=int, help="Limit results")
parser.add_argument("--id", help="Resource ID")
parser.add_argument("--from", dest="from_date", help="Start date")
parser.add_argument("--to", dest="to_date", help="End date")
parser.add_argument("--status", help="Filter by status")
parser.add_argument("--sort", help="Sort field")
parser.add_argument("--query", help="Search query")
parsed = parser.parse_args()
cmd_func = COMMANDS[parsed.command]
cmd_func(parsed)
if __name__ == "__main__":
main()
```