Files
twenty/packages/twenty-apps/public/twenty-fireflies/src/logic-functions/utils/fetch-fireflies-transcript.ts
T
nitin 525ed74b2f Fireflies: port app to the CallRecording standard object (#22642)
## Context

The Fireflies app predates the core `CallRecording` standard object: it
wrote transcripts and summaries as markdown into two rich-text field
extensions on `CalendarEvent`. That model has no per-call record,
silently drops orphan calls, and diverges from how the Call Recorder app
stores recordings. This is the first step of moving Fireflies onto the
CallRecording architecture; media ingestion, retry markers, and a
dedicated tab/front component iterate on top of this.

## What this PR does

Replaces the CalendarEvent field extensions with upserts into the core
`CallRecording` object:

- **One CallRecording per Fireflies call** via a deterministic UUID
derived from the Fireflies meeting id — the `meeting.transcribed`
webhook, the `meeting.summarized` webhook, and manual **Sync Fireflies
Call** runs all converge on the same row regardless of order, with a
create-race fallback to update
- **Transcript as diarized JSON** (participant + sentence-level relative
timestamps) instead of markdown, matching the entry shape the
CallRecording `transcript` field holds for other recording apps;
fetchers now request `date` and sentence `end_time`
- **Summary stays rich text** in `CallRecording.summary`, composed from
the Fireflies overview / action items / topics / keywords
- **Call metadata filled** — title, `startedAt`/`endedAt` (from `date` +
`duration`), `externalRecordingId`; transcript sync marks the row
`COMPLETED`, a summary-first sync creates it as `PROCESSING`
- **Orphan calls are kept**: when no CalendarEvent matches by
`eventExternalId` / `iCalUid`, the CallRecording is created without a
calendar event link instead of being dropped
- **Role**: now reads/writes `callRecording`; `calendarEvent` drops to
read-only; the two schema field extensions and the markdown transcript
formatter are removed

### Housekeeping

- App version bumped to **0.2.0** so installed workspaces pick up the
upgrade
- `twenty-sdk` / `twenty-client-sdk` aligned on `^2.18.0` (matches
call-recorder)
- Gallery screenshots depicting the removed CalendarEvent fields dropped
from the marketplace config
- README: "Upgrading from 0.1.x" section documenting the field removal
and the backfill path
- Integration test guarding the mirrored status constants against the
server's CallRecording select options

## Upgrade note

Upgrading removes the two app-owned CalendarEvent fields and their
stored content (app upgrades infer deletions from the manifest diff).
That data is a cache of Fireflies content and is re-derivable: any call
still in Fireflies can be re-ingested as a CallRecording via **Sync
Fireflies Call**.

A follow-up PR adds a post-install/upgrade sweep (same pattern as the
Call Recorder sweep in #22552) that pages through Fireflies history and
replays each call through the same sync flow, so history backfills
automatically on install and upgrade — the deterministic ids make
re-sweeping idempotent.



<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22642?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-08 14:11:49 +05:30

61 lines
1.3 KiB
TypeScript

import { type FirefliesTranscript } from 'src/logic-functions/types/fireflies-transcript.type';
import {
firefliesApiRequest,
type FirefliesApiResult,
} from 'src/logic-functions/utils/fireflies-api-request';
const TRANSCRIPT_QUERY = `
query Transcript($transcriptId: String!) {
transcript(id: $transcriptId) {
id
title
date
duration
meeting_link
participants
organizer_email
calendar_id
cal_id
calendar_type
sentences {
speaker_name
text
start_time
end_time
}
}
}
`;
type FirefliesTranscriptResponse = {
transcript: FirefliesTranscript | null;
};
export const fetchFirefliesTranscript = async ({
apiKey,
transcriptId,
}: {
apiKey: string;
transcriptId: string;
}): Promise<FirefliesApiResult<FirefliesTranscript>> => {
const result = await firefliesApiRequest<FirefliesTranscriptResponse>({
apiKey,
query: TRANSCRIPT_QUERY,
variables: { transcriptId },
});
if (!result.ok) {
return result;
}
if (result.data.transcript === null) {
return {
ok: false,
status: 404,
errorMessage: `Fireflies transcript ${transcriptId} not found (may have been deleted or access was revoked)`,
};
}
return { ok: true, status: result.status, data: result.data.transcript };
};