Match a qualified lead to Twenty partners and generate all intro emails in one run. Use when a lead folder exists and you need to find the right implementation partner, either after running twenty-partner-design-doc or standalone when partner-match-criteria.md is already present. Chains back into twenty-lead-intro-call-summary and twenty-partner-design-doc if the criteria file is missing.
76
96%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Match a qualified lead to Twenty partners and generate all intro emails in one run.
Chains after twenty-lead-intro-call-summary → twenty-partner-design-doc. Can be run standalone if partner-match-criteria.md already exists in the lead folder.
Before running, ~/.twenty/credentials.env must exist with:
TWENTY_PARTNERS_API_URL=https://partners.twenty.com
TWENTY_PARTNERS_API_KEY=<your key>
# Only needed if the input is a Fireflies link/ID
FIREFLIES_API_KEY=<your key>The partners API key lives in packages/twenty-apps/internal/twenty-partners/.env.prod (gitignored) — copy it to ~/.twenty/credentials.env on first setup. The skill reads credentials.env at startup and stops cleanly if a required key is missing.
Read ~/.twenty/credentials.env. Verify TWENTY_PARTNERS_API_URL and TWENTY_PARTNERS_API_KEY are present. If not, stop and tell the user exactly which key is missing and where to add it.
Look for partner-match-criteria.md in the lead folder.
If missing: Ask the user what is available — a text transcript, a Fireflies meeting ID/URL, or an existing call summary or brief. Then chain:
FIREFLIES_API_KEY) → /twenty-lead-intro-call-summary → /twenty-partner-design-doc/twenty-lead-intro-call-summary → /twenty-partner-design-doc/twenty-partner-design-docpartner-match-criteria.md is produced by /twenty-partner-design-doc as Step 8.
If present — be critical before proceeding. Read the file and assess quality:
If the brief is thin on any of these axes, say so and ask targeted follow-up questions before querying the API. A match built on a vague brief is noise. Only proceed once the criteria are solid enough to produce a meaningful ranking.
Query the partners API:
query ListPartners($after: String) {
partners(
filter: {
validationStage: { eq: VALIDATED }
availability: { eq: AVAILABLE }
}
after: $after
) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id name slug languagesSpoken skills deploymentExpertise
partnerScope partnerTier country region city introduction
persons { edges { node { name { firstName lastName } emails { primaryEmail } } } }
company { id name }
}
}
}
}Paginate until pageInfo.hasNextPage is false, passing pageInfo.endCursor as $after each iteration. Endpoint: $TWENTY_PARTNERS_API_URL/graphql.
Evaluate every candidate against the criteria in partner-match-criteria.md. Apply hard requirements as eliminators first (a partner missing a hard requirement does not appear in results, even with a note). Then rank remaining candidates by fit across:
languagesSpokenSELF_HOST deploymentExpertise, etc.Show at minimum 2 candidates, more if others score well. For each:
## [Partner name] — [fort / moyen / faible]
**Pourquoi ça matche**
- point 1
- point 2
- point 3
**Ce qui manque ou est risqué**
- point
**Contacts** : [name, email if available]Be honest. If only one candidate is a strong match, say so and explain why the others are weaker — don't artificially inflate scores to fill a quota.
Ask the user which partners to introduce. They can pick one, several, or none (and ask to search differently). Wait for explicit confirmation before generating emails.
For each confirmed partner, generate three email types. For N confirmed partners, the total is 1 + 2N emails.
—) — use : or , insteadCheers,\nRashad\nPartnerships @twenty[Twenty]https://mail.google.com/mail/?view=cm&fs=1&to=...&su=...&body=... using open -a "Google Chrome"This is a reply to the existing client thread, not a new draft. Don't open a Gmail compose for it and don't invent a subject — output the body text only so the user pastes it as a reply in the ongoing thread. No re-introduction (it's an existing conversation).
persons → emails.primaryEmail; leave blank if none found)[Twenty] Partner opportunity: [Client], [one-line project description][Twenty] [Client] x [Partner]: CRM projectOpen the partner emails only (Email 2 and Email 3) as new Gmail drafts, sequentially with a 1.5s delay between each. Email 1 is a reply to the existing client thread — output its text for the user to paste, don't open a compose window. Use Python:
import subprocess, urllib.parse, time, sys
params = {"view": "cm", "fs": "1", "to": to, "su": subject, "body": body}
if cc: params["cc"] = cc
url = "https://mail.google.com/mail/?" + urllib.parse.urlencode(params)
if sys.platform == "darwin":
subprocess.run(["open", "-a", "Google Chrome", url])
else:
# Linux / Windows: fall back to the system default browser
import webbrowser
webbrowser.open(url)
time.sleep(1.5)Remind the user to attach YYYY-MM-DD-<lead>-partner-brief.md to all partner emails before sending.
Save email content as text files in the lead folder for reference:
email-1-client.txtemail-2-<partner-slug>-solo.txtemail-3-<partner-slug>-intro.txtUse the partner's slug field for filenames.
This skill is the third step in the Twenty partner pipeline:
/twenty-lead-intro-call-summary → /twenty-partner-design-doc → /twenty-partner-matchThe partner-match-criteria.md produced by /twenty-partner-design-doc (Step 8) is this skill's primary input. Keep the criteria file updated as you learn more about the lead — it feeds both this skill and the eventual in-product matching logic.
bb4e427
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.