Back to Twenty

Fireflies for Twenty

packages/twenty-apps/internal/twenty-fireflies/README.md

2.7.214.0 KB
Original Source

Fireflies for Twenty

Sync Fireflies call transcripts and AI summaries onto the matching CalendarEvent in your Twenty CRM — searchable, in context, and ready for AI agents and workflows to act on. Plus on-demand workflow tools to sync, list, and search Fireflies calls from the AI chat or workflow builder.

What this app does

  1. Fireflies records and transcribes your Zoom / Meet / Teams / phone call.
  2. When the transcript is ready, Fireflies fires a meeting.transcribed webhook; once Fireflies finishes its AI summary, it fires a separate meeting.summarized webhook.
  3. For each event, this app fetches the relevant data via the Fireflies GraphQL API.
  4. It finds the matching CalendarEvent in Twenty and writes the content into either the Transcript or Summary field on that event.

Alongside the webhook, three Workflow tools let you trigger Fireflies actions from the AI chat or as steps inside a workflow, without waiting for Fireflies to push.

How a transcript is matched to a CalendarEvent

The matcher tries two provider-ID strategies in priority order and stops at the first hit:

  1. Provider-native event ID — Fireflies' calendar_id / cal_id is matched against CalendarChannelEventAssociation.eventExternalId. Covers events synced into Twenty from Google Calendar (including individual instances of recurring events, where Fireflies returns the per-instance id with timestamp on cal_id).
  2. iCalUID — Fireflies' calendar_id is matched against CalendarEvent.iCalUid. Covers events synced from Outlook / CalDAV, where Fireflies returns the RFC 5545 iCalUID directly.

Both identifiers are populated by Twenty's calendar drivers on every synced CalendarEvent, so any meeting that's been pulled in via Google / Outlook / CalDAV calendar sync will match exactly. The matcher does not fall back to fuzzy URL matching — if the transcript can't be tied to a synced calendar event, the call is treated as an orphan and skipped (see Limitations below). This avoids silently writing transcripts to the wrong event.

What gets added to your Twenty workspace

Two new fields on the standard CalendarEvent object:

  • Transcript — rich-text field, speaker-attributed (e.g. "Sarah: Hi there", then "John: Doing well, thanks.").
  • Summary — rich-text field with the Fireflies AI summary: a bullet-list overview, action items grouped by speaker, topics discussed, and keywords.

Plus three workflow tools — see Workflow tools below.

Workflow tools

Once the API key is configured, three tools become available in the workflow builder and the AI chat — covering the cases the webhook can't:

  • Sync Fireflies Call"sync the Fireflies call 01HXYZ... onto its CalendarEvent now". As a workflow step: provide transcriptId. Runs the same pipeline as the webhook (fetch transcript + AI summary, find matching CalendarEvent, write Transcript + Summary fields) on demand. Use cases: backfilling historical calls that happened before the app was installed; recovering from a missed webhook (e.g. the calendar event hadn't synced yet when Fireflies pushed); or triggering a sync from a workflow instead of waiting for Fireflies. Output includes calendarEventId, updatedFields, and a per-field outcome breakdown so partial successes are visible.
  • List Fireflies Calls By Participant"show me my last 5 calls with [email protected]". As a workflow step: provide participantEmail (and optional limit, max 50). Returns recent Fireflies calls — newest first — where that email was an attendee, with title, date, duration, host, and transcript URL. The natural first step in workflows triggered on Person.created"find what we've talked about with this contact".
  • Search Fireflies Calls"find any call where we discussed pricing". As a workflow step: provide keyword (and optional limit, max 50). Matches the keyword against both meeting titles and the words actually spoken in meetings. Returns the same call-summary shape as the participant tool. Best for AI-chat-driven research.

The list-by-participant and search tools return the same compact call shape: id, title, date, durationMinutes, participants, hostEmail, transcriptUrl, meetingLink. To then sync any of those calls onto its CalendarEvent, pass the id from a list result into Sync Fireflies Call.

Installing

  1. Open Settings → Applications in your Twenty workspace.
  2. Find Fireflies in the available apps and click Install.
  3. Follow Self-hosting setup below to wire up the API key and webhook (admin-only, one-time).

Heads up: if you see "Fireflies is not configured" on the first webhook, your Twenty admin needs to follow the Self-hosting setup section.

Limitations

What this connector intentionally does not support in v1:

  • Calls without a matching CalendarEvent (orphan calls). Ad-hoc calls that were never on anyone's synced calendar are skipped. The webhook logs the skip reason; the transcript still lives in Fireflies. Synthetic event creation for orphans is planned for v2.
  • Fireflies sentiment, speaker analytics, transcript chapters. Only the raw transcript and the AI summary (overview, action items, topics, keywords) are synced today.
  • Per-user Fireflies accounts. All transcripts come through one workspace-shared API key (set by the admin). Per-user OAuth-style connections require extending Twenty's connection provider system and are planned once we have evidence that workspace-shared is too coarse.
  • Editing transcripts or summaries in Twenty. The fields are writable but the next Fireflies sync overwrites any manual edits — treat them as read-only.

Troubleshooting

SymptomLikely causeFix
Webhook returns Fireflies is not configuredFIREFLIES_API_KEY not setAdmin: paste the API key in Settings → Applications → Fireflies → Settings
Webhook returns Invalid webhook signatureFIREFLIES_WEBHOOK_SECRET mismatch between Fireflies and TwentyRe-copy the signing secret from the Fireflies webhook configuration and paste it into the Twenty app settings
Webhook returns skipped: No CalendarEvent matched the transcript by external ID or iCalUidThe meeting was never on a synced calendar in Twenty, or the workspace has no Google/Outlook/CalDAV calendar connection set upConnect the relevant calendar provider in Settings → Accounts so the calendar event lands in Twenty with eventExternalId and iCalUid populated. Manually-created CalendarEvents are intentionally not matched in v1
Transcript appears emptyFireflies returned no sentences (call too short, audio failed)Check the call in the Fireflies dashboard; nothing this app can do
Summary appears emptyFireflies hasn't summarized the call yet, or the call was too short to summarizeFireflies sends meeting.summarized separately from meeting.transcribed (typically a minute or two later); ensure that event is subscribed to in your Webhooks V2 config
Summary is populated but Transcript isn't (or vice versa)Only one of the two Fireflies events is subscribed toSubscribe to both meeting.transcribed and meeting.summarized in your Fireflies Webhooks V2 configuration
Fireflies API errors with 401API key wrong, rotated, or revokedGenerate a new key in Fireflies → Integrations → Fireflies API → Regenerate, then update FIREFLIES_API_KEY
Sync Fireflies Call reports No fields were updatedThe Fireflies call's calendar_id / cal_id doesn't match any CalendarEvent's iCalUid or eventExternalId (orphan call), or the per-field outcomes show transient Fireflies API failuresCheck the fieldOutcomes array in the result — skipped means orphan call (same limitation as the webhook); error means Fireflies-side failure (retry, or inspect the error message)
List / Search tools return count: 0 for a contact you've definitely talked toEmail mismatch — Fireflies stores the address as the participant joined the meeting with, which may differ from the contact's primary address in Twenty (aliases, plus-addressing, work vs. personal)Try the contact's other known email addresses; cross-check the participants list on a known matching call

Self-hosting setup (admin-only)

This section is for Twenty server admins. If you're on Twenty Cloud, skip this — the credentials may already be configured.

1. Generate a Fireflies API key

  1. Visit https://app.fireflies.ai and sign in.
  2. Go to Integrations → Fireflies API.
  3. Click Generate API key and copy the value (it's only shown once).

2. Configure a Webhooks V2 endpoint in Fireflies

This integration targets Fireflies Webhooks V2 (snake_case payload, granular event subscriptions). The legacy V1 webhook format (meetingId / eventType: "Transcription completed") is not supported.

  1. Open the Webhooks V2 page: https://app.fireflies.ai/integrations/api/webhook
  2. Set the Webhook URL to your Twenty deployment's webhook endpoint: https://<your-twenty-domain>/webhook/fireflies. Twenty resolves the target workspace from the request's Host header, so the URL must match the workspace's public domain — localhost is not valid in the Fireflies UI. For local development, expose your dev server with a tunnel like ngrok http 3000 and paste the HTTPS forwarding URL here, or skip the Fireflies UI entirely and POST a signed payload directly to your local endpoint (see Local webhook testing in the developer section below).
  3. Set a Signing Secret (a long random string — generate one with openssl rand -hex 32). Save it; you'll paste it into Twenty next.
  4. Under Events, subscribe to both:
    • meeting.transcribed — fires when the transcript is ready and writes it to the Transcript field.
    • meeting.summarized — fires once Fireflies finishes its AI summary and writes it to the Summary field. Subscribing to only one is fine if you don't want the other field populated; the app dispatches per event.
  5. Save the configuration.

3. Wire the credentials into Twenty

  1. In Twenty: Settings → Applications → Fireflies → Settings tab.
  2. Paste the Fireflies API key into the FIREFLIES_API_KEY row.
  3. Paste the signing secret into the FIREFLIES_WEBHOOK_SECRET row.

After saving, the next time Fireflies finishes processing a recording, the transcript will land on the matching CalendarEvent within a few seconds; the summary follows once Fireflies finishes the AI summarization step (typically a minute or two later — Fireflies sends two separate webhooks).


Why transcript / summary fields on CalendarEvent instead of a new object?

Storing the transcript and AI summary as rich-text fields directly on the existing CalendarEvent:

  • Keeps everything about a meeting in one place (no joins)
  • Avoids inventing a new object that other call-recording apps would each need to coordinate on
  • Works today without lookup fields

If later integrations (Gong, Otter, Zoom AI, etc.) make one pair of fields too restrictive — for example, needing to distinguish which tool produced the transcript — we'll promote the fields to a platform-level concept rather than keep extending this app.


Developers only

If you're working on this app rather than installing the published version:

bash
cd packages/twenty-apps/internal/twenty-fireflies

# Day-to-day development (publish + install + watch in one):
yarn twenty dev

# Run unit tests:
yarn test

# Lint:
yarn lint

twenty dev is recommended for iteration — it publishes to your local Twenty server, installs the app, and watches for changes in one command.

The Fireflies GraphQL API is called directly via fetch — no fireflies SDK dependency. See src/logic-functions/utils/fireflies-api-request.ts for the auth + error-handling wrapper that all queries go through.

Local webhook testing

Fireflies' Webhooks V2 UI only accepts a publicly reachable HTTPS URL, so pointing it at http://localhost:* directly is not possible. Two paths:

End-to-end via tunnel. Run a tunnel that fronts your local server with a public HTTPS URL (ngrok http 3000, cloudflared tunnel, etc.), paste the HTTPS forwarding URL into the Fireflies webhook UI as the Webhook URL, and exercise the integration by ending a real Fireflies meeting.

Backend-only via signed curl. Skip the Fireflies UI entirely and POST a signed payload straight to the local endpoint. The signature must be HMAC-SHA256 over the raw request body, keyed by your FIREFLIES_WEBHOOK_SECRET, prefixed with sha256=:

bash
export FIREFLIES_WEBHOOK_SECRET='<the secret you set in Twenty app settings>'

BODY='{"event":"meeting.transcribed","meeting_id":"<a-real-fireflies-transcript-id>"}'
SIG=$(printf '%s' "$BODY" \
  | openssl dgst -sha256 -hmac "$FIREFLIES_WEBHOOK_SECRET" \
  | awk '{print $NF}')

curl -X POST http://localhost:3000/webhook/fireflies \
  -H 'Content-Type: application/json' \
  -H "x-hub-signature: sha256=$SIG" \
  --data-binary "$BODY"

--data-binary (not --data) is important: it preserves the bytes verbatim so the HMAC the server computes matches the one openssl computed above. Twenty resolves the workspace from the Host header, so the default dev workspace (mapped to localhost:3000 in a standard yarn start setup) receives the request.

To match a real CalendarEvent, the transcript ID you pass must belong to a Fireflies call whose calendar_id / cal_id matches an existing CalendarEvent.iCalUid or CalendarChannelEventAssociation.eventExternalId in your local Twenty workspace. The easiest local seed is to manually insert a row with one of those identifiers and use a Fireflies transcript whose calendar fields point at it.