skills/find-leads/SKILL.md
Find new B2B leads in Germany for InboxMate outreach. Validates each lead against legal requirements (UWG), checks email is publicly visible, documents justification, and adds to CRM. Germany only — Austrian law (TKG) is stricter. Parameters: number of leads, optionally a track ('inbox' for email-automation leads only, 'chatbot' for chatbot leads only; default mixed).
npx skillsauth add psquared-development/psquared-skills find-leadsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
/find-leads [N] [track] — track is optional:
inbox — EMAIL-ONLY mode: search email-ops-heavy verticals (see "Email-volume signals" below — rotate 2-3 verticals per wave, never fixate on one industry). Every qualified lead gets Track: INBOX in its note. A chatbot on their site is a positive signal here, never a skip. Demos for these run via /inboxmate-inbox-demo.chatbot — chatbot-only mode: classic behavior, skip companies that already have a chatbot, every lead gets Track: CHATBOT.Announce:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Lead Finder started. Target: [N] new leads · Track: [INBOX / CHATBOT / mixed] Checking environment... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
This skill should run as autonomously as possible. The user may not be available to answer questions. Do NOT ask for confirmation between leads — just process them one by one:
B2B cold email in Germany and Austria is a legal grey area. The strict reading of UWG §7 (Germany) and TKG §174 (Austria) says prior consent is required. However, the prevailing practical interpretation (used by thousands of companies daily) allows individual B2B outreach IF certain conditions are met.
Our position: We send individually crafted, genuinely valuable emails (with a working demo built for their company). This is not spam. But we MUST document why each lead qualifies for outreach.
Every lead MUST pass ALL 8 of these checks:
| # | Criterion | What to check | Why it matters | |---|-----------|---------------|----------------| | 1 | German company | Domain is .de, Impressum shows German address (DE), company is registered in Germany | Austrian law (TKG §174) is stricter — no B2B cold email exception. Germany (UWG) has more practical tolerance for relevant individual B2B outreach. | | 2 | B2B only | Company, not a private person | Consumer protection is stricter | | 3 | Objectively relevant | Company would genuinely benefit from a chatbot OR from email automation (customer-facing business with visible inquiry/booking volume) | UWG requires the offer to be relevant to the recipient's business | | 4 | Publicly available email | Email found on their public website (contact page, imprint, team page) — NOT scraped from LinkedIn, purchased lists, or leaked databases | Data source must be GDPR-compliant (publicly made available by the company itself) | | 5 | Specific contact person | Email goes to a named person with decision authority (founder, CEO, marketing lead, head of sales) — NOT info@, office@, or generic addresses | Shows individual outreach, not bulk | | 6 | Genuine value offer | We built something specifically for them (a demo) — not a generic pitch | Differentiates from spam | | 7 | Not already in CRM | Company doesn't already exist in our CRM | No duplicate outreach | | 8 | Website is active | Website loads, has real content, is maintained (copyright not 2+ years old) | No dead businesses |
If ANY of the 8 criteria fails → SKIP the lead. Add to skip-list.json. Do not add to CRM.
The file skip-list.json in the current working directory tracks companies that were already checked and rejected. Before researching any company, check this list first. If the domain is in the list, skip immediately — don't waste time fetching the website again.
On first run: If skip-list.json doesn't exist, create it with [].
Format:
[
{ "domain": "example.de", "reason": "has chatbot", "date": "2026-03-19" },
{ "domain": "other.at", "reason": "no personal email", "date": "2026-03-19" }
]
When a company fails any check: Append to skip-list.json with the domain and a short reason (max 5 words).
Before checking any website: Read skip-list.json, check if domain is already there. If yes, skip without fetching.
Short reason examples:
"has chatbot, no email volume" — chat widget present AND no email-automation angle (a chatbot alone no longer disqualifies — it routes to the INBOX track)"no personal email" — only info@/office@ found"dead website" — site down or parked"outdated site" — copyright 2+ years old"too small" — sole proprietor, no real web presence"not B2B-facing" — wholesale/internal only"already in CRM" — duplicate"no email on site" — no contact info found"generic only" — only kontakt@/info@ availableRead .env in the current working directory using the Read tool. Extract these tokens:
| Variable | Purpose | Used for |
|----------|---------|----------|
| PSQUARED_CRM_TOKEN | Twenty CRM GraphQL API | Checking duplicates, creating companies/persons/notes |
The .env file is at the root of the working directory (claude-overlord-folder). If it doesn't exist, stop and tell the user.
Read skip-list.json (or create it as [] if it doesn't exist).
Once verified:
Environment OK. Skip list: [N] entries. Starting lead search...
Use WebSearch to find German and Austrian businesses that would benefit from an AI chatbot on their website.
Search strategies (rotate between these — Germany only, .de domains):
"[industry] Unternehmen" site:.de"[industry] GmbH" Kontakt Email site:.de"Geschäftsführer" "[industry]" site:.de"Immobilienmakler" OR "Versicherungsmakler" OR "Steuerberater" OR "Rechtsanwalt" site:.deGood target industries for InboxMate:
Email-volume signals (qualify for the INBOX track — the paid email product): InboxMate's email product is NOT just answering inquiries — it sorts, routes, answers and triggers actions on operational mail. Target ANY German B2B whose operations run through the inbox. Pick 2-3 verticals per wave and ROTATE — do not fixate on one industry (e.g. not only Thermen):
/inboxmate-inbox-demo) proves the fix — seeded threads should mirror THEIR ops mail, incl. a Rechnung or Bewerbung being auto-routed where plausibleBad targets (skip):
For each potential lead found:
Extract the domain from the company URL. Check if it's in skip-list.json. If found, skip immediately and move to the next lead — no need to fetch the website.
Visit their website. Verify:
/inboxmate-demo)/inboxmate-inbox-demo) — an existing chatbot even confirms they invest in automation"has chatbot, no email volume"Skip if: website is down, parked, outdated, or NOT a German company. Add to skip-list.json with reason.
Look for contact information on:
/kontakt or /contact page/impressum or /imprint page/team or /ueber-uns pageWhat we need:
Email rules:
Write a 1-2 sentence justification for why InboxMate would be valuable for this specific company. Reference something concrete:
Check by domain first (most reliable), then fall back to company name:
Step 1 — Search by domain:
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d "{\"query\":\"{ companies(filter: { domainName: { primaryLinkUrl: { like: \\\"%[domain]%\\\" } } }, first: 1) { totalCount } }\"}"
Step 2 — If not found by domain, also search by name (catches companies added without a domain):
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d "{\"query\":\"{ companies(filter: { name: { like: \\\"%[Company Name]%\\\" } }, first: 1) { totalCount } }\"}"
If either query returns totalCount > 0 → SKIP. Add to skip-list.json with reason "already in CRM".
For each lead that passes ALL 7 criteria:
Always set outreachFor: [INBOX_MATE] so downstream InboxMate skills pick this company up (demos, campaigns, email drafts filter on this field). Never set PERSONAL_ONLY or INBOX_MATE_MUSEUM here — PERSONAL_ONLY is reserved for companies Martin adds manually and must never be touched by automation; INBOX_MATE_MUSEUM is reserved for the curated museum list (imported 2026-05-08) and is handled outside this skill.
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d "{\"query\":\"mutation { createCompany(data: { name: \\\"[Company Name]\\\", domainName: { primaryLinkUrl: \\\"https://[domain]\\\" }, outreachFor: [INBOX_MATE] }) { id name outreachFor } }\"}"
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d "{\"query\":\"mutation { createPerson(data: { name: { firstName: \\\"[First]\\\", lastName: \\\"[Last]\\\" }, emails: { primaryEmail: \\\"[email]\\\" }, companyId: \\\"[companyId]\\\", jobTitle: \\\"[Role/Title]\\\" }) { id } }\"}"
Two-step process: Create the note first (with just a title), then update it with the full body. This avoids issues with long content in a single mutation.
Step 1 — Create note:
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d "{\"query\":\"mutation { createNote(data: { title: \\\"Lead Qualification: [Company Name] — [date]\\\" }) { id } }\"}"
Step 2 — Update note with full content:
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d "{\"query\":\"mutation { updateNote(id: \\\"[noteId]\\\", data: { body: \\\"Email source: [URL where email was found]\\\\nContact: [Name], [Role]\\\\nRelevance: [1-2 sentence justification]\\\\n\\\\nTrack: [CHATBOT | INBOX]\\\\nDemo approach: [suggest best angle — CHATBOT e.g. 'FAQ automation for property listings'; INBOX e.g. 'Demo-Postfach: Gutschein-Anfragen, Gruppenbuchungen, Storno-Mails vorsortiert + beantwortet']\\\\n\\\\nChecks: German company ✓, B2B ✓, Relevant ✓, Public email ✓, Named contact ✓, Value offer ✓, Not in CRM ✓, Active website ✓\\\" }) { id } }\"}"
Step 3 — Link note to company:
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d "{\"query\":\"mutation { createNoteTarget(data: { noteId: \\\"[noteId]\\\", companyId: \\\"[companyId]\\\" }) { id } }\"}"
The "Demo approach" field should suggest the best angle for the InboxMate demo based on what you found on their website. Examples:
Announce after each:
Added: [Company Name] — [contact email] — [1-line reason]
Announce:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Lead Finder complete. Added: [N] - [Company A] — [email] — [reason] - [Company B] — [email] — [reason] Skipped: [M] - [Company C] — already in CRM - [Company D] — no personal email found - [Company E] — already has chatbot Next step: Run /create-demo-for-crm-lead to build demos ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
tools
Set up a personalized InboxMate INBOX demo (Demo-Postfach) for a sales prospect: a public, read-only seeded inbox showing 5-7 pre-triaged emails in their industry's language, with categories, routing and ready AI drafts. Use for email-automation outreach (the €49-349 product), NOT for chatbot outreach. No agent is created.
development
Build InboxMate demos AND write personalised outreach drafts in a single pass per company — eliminating the double-research that happens when /inboxmate-batch-demo and /setup-email-drafts run separately. Use when kicking off a new campaign where the campaign already exists (plan via /plan-campaign first). For each target company, dispatches ONE subagent that researches the site, builds the demo, creates the CRM opportunity, and drafts the outreach email — reusing the same research across all three. After all subagents return, runs a single batch call to auto-generate follow-ups.
testing
Autonomous pilot for the InboxMate EMAIL outreach (Demo-Postfach/INBOX track). Assesses where the inbox pipeline stands (leads → demos → review → campaign → drafts) and executes the next sensible step end-to-end, always finishing with the inbox sanity check and a summary of what the user should do next (ideally: just schedule the mails). Runs in save mode by default: orchestration + all quality gates on the top model, data collection on haiku subagents, content generation on sonnet subagents (pass 'full' to disable). Use when asked to 'advance the email outreach', 'run the inbox pipeline', or 'what's next for the Demo-Postfach motion'.
tools
Generate a polished psquared client offer as a multi-page PDF (title, project description, screenshots, Angebot/pricing, AGB). Walks the user through gathering inputs (or accepts a JSON config), renders branded HTML templates with Playwright in two passes (title page edge-to-edge + body pages with margins and pagination), then merges with pdf-lib.