by GortexAI
Franco: an outbound sales agent that runs on your Claude Code or Codex subscription
# Add to your Claude Code skills
git clone https://github.com/GortexAI/francoSee how franco compares with popular alternatives.
franco is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by GortexAI. Franco: an outbound sales agent that runs on your Claude Code or Codex subscription. It has 98 GitHub stars.
franco's catalog security scan is still queued. You can run an instant dependency and prompt-injection check now with the "Scan for vulnerabilities" button above.
Clone the repository with "git clone https://github.com/GortexAI/franco" and add it to your Claude Code skills directory (see the Installation section above).
franco is primarily written in Python. It is open-source under GortexAI on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh franco against similar tools.
No comments yet. Be the first to share your thoughts!
Available after the catalog security scan passes.
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
The deep catalog scan for this skill is still queued. Run an instant dependency check now instead.
An outbound sales agent that runs on your own machine, on the Claude Code or Codex subscription you already pay for.
Franco works through a list of accounts. For each one it finds or imports a reason to write, sends a short email sequence from your own mailbox, reads the replies, and handles them. Every fact it uses is recorded with its source. You review every email before it sends, unless you turn on autopilot.
It works in three ways (outreach.mode):
generated, the default). Franco finds businesses, reads their websites, picks the right person, and drafts a personalized sequence with the model.templates). You import accounts you researched yourself, with one sourced observation for each. Franco sends your approved sequences word for word. No model writes them, the schedule counts business days, and the compliance footer and suppression rules are enforced in code. This is the mode Gortex uses today.raad). Franco places every account in the narrowest funnel it fits, based on what it observed about the account (a marketplace that already runs AdSense is a different conversation from one with no ads). It then writes email 1 from that funnel's story, one customer benefit, the buyer's role and the account's own sourced fact, and follows up with the funnel's approved emails. More below.Bring your own lists as CSV files: researched accounts, plain company/contact lists, and do-not-contact lists. Use the terminal or the dashboard. Every new campaign starts in preview: a few accounts per owner are staged for review, and nothing sends until someone signs the campaign off.
There is no model API bill. Model calls go through claude (Claude Pro or Max) or, if you opt in, an isolated codex runtime (a ChatGPT plan). Everything that can be done in plain code is done in plain code.

Recorded from made-up demo data by scripts/record_demo.py. Re-run it after changing the dashboard.
You need:
brew install python@3.13.claude login, on a Pro or Max plan. Codex is the alternative; see below.Install from PyPI (recommended):
pipx install franco # or, in a virtualenv: pip install franco
franco init ~/franco && cd ~/franco # your home folder: config, .env, prompts, skills, data
franco install # Chromium, for LinkedIn and JavaScript-heavy sites
$EDITOR .env # mail provider + verifier keys, documented inline
franco gmail auth # one-time browser login when provider: gmail
franco gmail test
franco dashboard # http://localhost:5555
franco run # the heartbeat loop
Franco works in one home folder. It finds it in this order:
$FRANCO_HOME, when set;franco.yaml;~/.franco otherwise.franco init creates a home folder. The prompts and skills it copies there are yours to edit; delete one to go back to the version that ships with Franco.
Or run from a clone (to change Franco itself):
git clone https://github.com/GortexAI/franco.git && cd franco
python3.13 -m venv .venv && source .venv/bin/activate && pip install -e .
cp .env.example .env && $EDITOR .env
The clone is its own home folder: config, prompts/, skills/ and data/ live next to the code.
Or run the published container image (Linux, Intel or ARM). It already includes Chromium and the Claude CLI:
docker pull ghcr.io/gortexai/franco:latest # or :edge for the latest main, or a version tag
docker compose up -d # uses that image, with ./data, .env and your config mounted
Every image is built by GitHub Actions with a signed provenance attestation. You can check one with gh attestation verify oci://ghcr.io/gortexai/franco:latest --owner GortexAI.
Or open the repo in Claude Code and say "set up Franco for me". CLAUDE.md turns Claude into the setup guide: it checks what's missing and asks for one thing at a time.
Put your real settings in franco.local.yaml. It is gitignored, and when it exists it is used instead of franco.yaml. The tracked franco.yaml stays a template.
Franco runs a heartbeat. By default, every 15 minutes it wakes up, checks quiet hours and its usage budget, decides what most needs doing, does it, logs it, and goes back to sleep.
handle replies > send due email > write/stage sequences > prospect > idle (analyze)
The decision is made from counts in the database, not by asking a model. Profiling and draining the outbox happen on every cycle because they cost nothing.
| Agent | What it does |
|---|---|
| Scout | Scores and personalizes prospects. Searching, website reading and email resolution are plain Python. |
| Writer | Drafts sequences with the model (generated), renders approved copy with no model (approved copy), or places accounts in funnels and writes email 1 from the funnel's brief (raad). |
| Sender | Stages each email in the outbox with a send time, then sends due, approved items with human-like pacing, the daily cap and the pre-send gate. |
| Handler | Reads replies, removes quoted text, classifies intent, stops sequences, records opt-outs, detects bounces, and queues responses through the outbox. |
| Analyst | On idle cycles, writes pipeline stats to data/analytics.json. |
Every list Franco takes can come in as a CSV file. You don't have to use discovery or type entries one at a time. There are three kinds, each with a template:
| Kind | One row is | Required columns | Used by |
|---|---|---|---|
accounts |
a researched account: one buyer, the campaign they get, and the sourced fact behind it | company, domain, campaign, first_name, last_name, title, email, observation, source_url, source_date |
approved copy, raad |
contacts |
a company, optionally with a person at it | company, domain |
generated, raad |
suppressions |
an email address or a whole domain never to contact | value (an address or a domain) |
everything |
Optional columns:
owner, segment, hypothesis, score (0-10), email_status, linkedin_url.first_name, last_name, title, email, email_status, linkedin_url, segment, location, phone, owner, notes. In raad mode, also campaign (pin a funnel), plus observation, source_url, source_date.reason. A suppressions file with no header, one address or domain per line, also works.Common spreadsheet header names are accepted, for example website for domain, role for title and funnel for campaign.
From the terminal:
franco import --template --kind contacts > contacts.csv # start from the template
franco import contacts.csv --kind contacts --dry-run # check every row, write nothing
franco import contacts.csv --kind contacts --verify # import; verify unverified emails
franco import accounts.csv # --kind defaults to accounts in approved-copy mode
franco suppress import do_not_contact.csv # same as --kind suppressions
franco suppress export > suppressions.csv
From the dashboard: the Contacts tab has an Import CSV panel. Pick the kind, download its template, then choose a file. The file is checked straight away, and nothing is written until you press Import. The browser reads the file and sends its text only to your local dashboard.
Rules that apply to every import:
--verify spends one verification credit per address that isn't already marked verified. Only verified addresses are emailed, unless channels.email.send_to_risky is on.Use this mode when the copy has been signed off and must go out exactly as written.
In franco.local.yaml:
outreach:
mode: templates
timezone: America/Los_Angeles
send_hour: 9
observation_max_age_days: 90
campaigns:
M1:
name: Vertical marketplaces
steps:
- day: 1
subject: "{{company}} sponsored listings"
body: |
Hi {{first_name}},
I noticed {{verified_observation}}.
...