Fireflies.ai Core Workflow A -- Transcript Retrieval & Processing
Overview
Primary workflow for Fireflies.ai: fetch meeting transcripts via GraphQL, process speaker-diarized sentences, extract action items and summaries, and route meeting intelligence downstream.
Prerequisites
- Completed
fireflies-install-authsetup FIREFLIES_API_KEYset with Business+ plan for full access- At least one completed meeting in Fireflies
Instructions
Step 1: Build the GraphQL Client
// lib/fireflies.ts
const FIREFLIES_API = "https://api.fireflies.ai/graphql";
export async function firefliesQuery<T = any>(
query: string,
variables?: Record<string, any>
): Promise<T> {
const res = await fetch(FIREFLIES_API, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.FIREFLIES_API_KEY}`,
},
body: JSON.stringify({ query, variables }),
});
const json = await res.json();
if (json.errors) {
const err = json.errors[0];
throw new Error(`Fireflies API error: ${err.message} (${err.code || "unknown"})`);
}
return json.data;
}
Step 2: List Transcripts with Filters
const LIST_TRANSCRIPTS = `
query ListTranscripts(
$limit: Int,
$mine: Boolean,
$fromDate: DateTime,
$toDate: DateTime,
$organizers: [String],
$participants: [String]
) {
transcripts(
limit: $limit
mine: $mine
fromDate: $fromDate
toDate: $toDate
organizers: $organizers
participants: $participants
) {
id title date duration
organizer_email participants
summary { overview action_items keywords }
}
}
`;
// Fetch this week's meetings for a specific organizer
const data = await firefliesQuery(LIST_TRANSCRIPTS, {
limit: 20,
fromDate: "2026-03-15T00:00:00Z",
organizers: ["alice@company.com"],
});
Step 3: Fetch Full Transcript with Sentences
const GET_TRANSCRIPT = `
query GetTranscript($id: String!) {
transcript(id: $id) {
id title date duration
organizer_email
speakers { id name }
sentences {
index
speaker_name
speaker_id
text
raw_text
start_time
end_time
ai_filters {
task
question
sentiment
pricing
metric
date_and_time
}
}
summary {
overview
short_summary
bullet_gist
action_items
keywords
outline
topics_discussed
}
meeting_attendees { displayName email }
meeting_attendance { name join_time leave_time }
analytics {
sentiments { positive_pct negative_pct neutral_pct }
speakers {
name duration word_count
words_per_minute questions
longest_monologue filler_words
}
}
}
}
`;
const { transcript } = await firefliesQuery(GET_TRANSCRIPT, { id: "abc123" });
Step 4: Process Meeting Intelligence
interface MeetingIntelligence {
id: string;
title: string;
attendees: string[];
actionItems: string[];
keyTopics: string[];
speakerBreakdown: { name: string; minutes: number; wordCount: number }[];
sentiment: { positive: number; negative: number; neutral: number };
questions: string[];
}
function processMeeting(transcript: any): MeetingIntelligence {
// Extract questions from AI filters
const questions = transcript.sentences
.filter((s: any) => s.ai_filters?.question)
.map((s: any) => `${s.speaker_name}: ${s.text}`);
return {
id: transcript.id,
title: transcript.title,
attendees: transcript.meeting_attendees?.map((a: any) => a.email) || [],
actionItems: transcript.summary?.action_items || [],
keyTopics: transcript.summary?.keywords || [],
speakerBreakdown: (transcript.analytics?.speakers || []).map((s: any) => ({
name: s.name,
minutes: Math.round(s.duration / 60),
wordCount: s.word_count,
})),
sentiment: {
positive: transcript.analytics?.sentiments?.positive_pct || 0,
negative: transcript.analytics?.sentiments?.negative_pct || 0,
neutral: transcript.analytics?.sentiments?.neutral_pct || 0,
},
questions,
};
}
Step 5: Export Transcript as Text
function transcriptToText(transcript: any): string {
const lines: string[] = [
`# ${transcript.title}`,
`Date: ${transcript.date} | Duration: ${transcript.duration}min`,
`Speakers: ${transcript.speakers.map((s: any) => s.name).join(", ")}`,
"",
"## Summary",
transcript.summary?.overview || "(no summary)",
"",
"## Action Items",
...(transcript.summary?.action_items || []).map((a: string) => `- ${a}`),
"",
"## Transcript",
];
for (const s of transcript.sentences) {
const timestamp = formatTimestamp(s.start_time);
lines.push(`[${timestamp}] ${s.speaker_name}: ${s.text}`);
}
return lines.join("\n");
}
function formatTimestamp(seconds: number): string {
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
}
Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| auth_failed | Invalid API key | Regenerate in Fireflies dashboard |
| Empty sentences array | Transcript still processing | Check meeting_info.summary_status |
| null summary | Short meeting (<1 min) | Summary requires minimum content |
| Rate limit 429 | Over 60 req/min (Business) | Implement backoff per fireflies-rate-limits |
| Missing analytics | Free/Pro plan | Analytics requires Business+ plan |
Output
- Full transcript with speaker-diarized sentences and timestamps
- AI-generated summary, action items, and keywords
- Speaker analytics with talk time, word count, and sentiment
- Meeting intelligence object ready for downstream processing
Resources
Next Steps
For search, analytics, and AskFred, see fireflies-core-workflow-b.