twenty-partner-recap

Pull recent Fireflies partner meetings, match each to an existing Partner record by attendee email or domain, write a recap (transcript-first, Fireflies summary as fallback), and inject it as a Note on the partner's profile. Use after a batch of partner calls when you want each partner's CRM record updated with what was said. Leads and discovery calls have no Partner match and are skipped. Pass --prune to delete the Fireflies recordings once the recaps are safely in the CRM.

Install
npx skills add 'https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/internal/twenty-partners/src/skills/twenty-partner-recap'
Download bundle ↓
main · cd49113Scanned 2026-09-17

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

SKILL.md

SKILL.mdBrowse 1 file
View on GitHub

twenty-partner-recap

Network upkeep, not the lead path. After a batch of partner calls: pull the Fireflies meetings, work out which partner each one is, summarize the call, and drop that summary as a Note on the partner's profile. Runs end to end with no per-note confirmation.

The recap notes it writes are what twenty-partner-shortlist later reads to tell a real capability from a partner's own marketing copy. Roughly 5 of the 29 validated partners have one today, so every run makes the shortlist better.

Optional --prune: after the recaps are safely in the CRM, delete the corresponding Fireflies recordings to free storage. Confirmed first, see Phase 6.

Credentials, the gql() helper and every query live in ../_shared/partner-api.md. This skill needs all three keys.


Phase 1 — Pull meetings

Default scope: the last 2 days, which covers "yesterday". The user can override per run: "last week", a date, or pasted Fireflies URLs and IDs.

List recent transcripts, keep the ones inside the window (date is epoch milliseconds), then fetch each kept transcript's detail.

If two transcripts share the same partner and day (Fireflies sometimes double-records), keep the one with more sentences.


Phase 2 — Match each meeting to a Partner

Only a meeting tied to an existing Partner gets processed. A lead or discovery call has no Partner match: skip it and list it at the end.

  1. Page through all partners once, building two maps:
    • email → partner from every persons.edges.node.emails.primaryEmail
    • domain → [partners] from each company.domainName.primaryLinkUrl
  2. For each meeting, take the attendee emails and drop anything @twenty.com plus the host and organizer: that is the Twenty side. For each remaining attendee:
    • Exact email match wins. It is the strongest signal.
    • Otherwise domain match, skipping free providers (gmail.com, outlook.com, hotmail.com, yahoo.com, icloud.com, proton.me and friends). Exactly one partner on the domain means a match. Several means flag it in the report and skip, never guess.
  3. The meeting title is a secondary hint only (Partner intro between … and <name> was the historical convention). Never the primary matcher.

Phase 3 — Summarize, transcript first

Judge content quality before writing. Never write a note from nothing or from noise.

  • A transcript is usable only with real content: roughly 15 sentences or more and an average of 4 words or more per sentence. A handful of one-word lines (Platform. Opportunity. Background.) is garbled ASR, not a transcript. Treat it as unusable even though the array is not empty.
  • Usable transcript → write the recap yourself from the transcript. The default whenever one exists.
  • No usable transcript but a Fireflies summary exists → fall back to summary.overview, then richer fields, then short_summary. Note the fallback in the source line.
  • Neither → the call is still processing or was never recorded. Skip it, record it as "skipped: content not ready", move on. Today's calls often land here for a while. Never inject an empty or placeholder note.

Write the note in English, structured. No em dashes: use a colon or a comma.

**TL;DR:** one-line verdict / state of the relationship.

**Profil:** team size, location, languages, structure.
**Compétences Twenty:** deployment (cloud / self-host), data model, migrations, what they've
actually shipped.
**Contexte:** background, how they found Twenty, motivation, target clients, current
partnerships.
**Next steps:** concrete follow-ups (who owes what).
**Flags:** risks, unknowns, ASR artifacts to double-check.

Source: Fireflies <transcript-id> (call <YYYY-MM-DD>, transcript|summary).

The Source: Fireflies <transcript-id> line is load-bearing: it is the dedup key for re-runs and the prune key for Phase 6. Always include the real transcript id.

Compétences Twenty is the section that matters downstream. It is the only place a verified capability gets recorded, as opposed to what the partner writes about themselves. State what they have actually shipped, not what they say they can do.


Phase 4 — Inject the Note

For each matched meeting, check first whether it was already noted: read the partner's notes (noteTargets filtered by targetPartnerId) and look for a body containing Fireflies <transcript-id>.

  • No such notecreateNote with bodyV2.markdown, then createNoteTarget linking noteId to targetPartnerId. Title: Partner call recap: <Partner name> (<YYYY-MM-DD>).
  • Note exists → regenerate the recap, diff it against the existing body, and append only net-new information under a dated **Update <YYYY-MM-DD>:** block via updateNote. Nothing new means leaving it untouched.

No confirmation step: match, summarize, write. Then verify each write by reading the note back and confirming the partner link resolved.


Phase 5 — Report

One table:

Meeting (date · title)Attendee matchedPartnerAction

Action is one of created, updated (appended), unchanged, skipped: no partner match, skipped: ambiguous domain (N partners), skipped: content not ready. End with the counts.


Phase 6 — Prune (--prune, opt-in, deletes Fireflies recordings)

Runs only with --prune. Fireflies storage fills up, and a recording whose content is already safe in the CRM is dead weight. Deletion is irreversible and on an external service, so always confirm before deleting.

A recording is safe to prune only when its recap note was confirmed written this run (created or updated), or already exists in the CRM with this transcript's Fireflies <id> in its body. Never prune a meeting that was skipped, has no note, or whose note you could not verify: losing the recording loses the only copy.

  1. Build the prune set from this run's safe meetings. If asked to "free more", add existing recap notes whose Fireflies <id> resolves to a still-present transcript.
  2. Present the exact list (partner, transcript id, date) and get explicit confirmation. Default to keeping the most recent unless told otherwise.
  3. Delete each confirmed transcript with deleteTranscript(id:), then verify by re-listing and confirming the ids are gone. Report deleted N/M and how many remain.

Matching old notes back to transcripts: notes written before this skill embed only a date, not an id, so fall back to the meeting-title person name (Partner intro between … and <name>, … - <name> x Rashad) against the partner name in the note title. Notes written by this skill carry Fireflies <id> in the body, so the mapping is exact going forward.

Discovery context

Discovered by repository scan. No exact path reference found in the snapshot’s root CLAUDE.md.