Implement Fireflies.ai reference architecture with best-practice project layout. Use when designing new Fireflies.ai integrations, reviewing project structure, or establishing architecture standards for Fireflies.ai applications. Trigger with phrases like "fireflies architecture", "fireflies best practices", "fireflies project structure", "how to organize fireflies", "fireflies layout".
From fireflies-packnpx claudepluginhub nickloveinvesting/nick-love-plugins --plugin fireflies-packThis skill is limited to using the following tools:
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Details PluginEval's skill quality evaluation: 3 layers (static, LLM judge), 10 dimensions, rubrics, formulas, anti-patterns, badges. Use to interpret scores, improve triggering, calibrate thresholds.
Production architecture for meeting intelligence with Fireflies.ai. Covers transcript processing pipelines, CRM integration, action item extraction, and meeting analytics dashboards using the GraphQL API.
graphql-request or urql)āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Meeting Sources ā
ā Zoom ā Google Meet ā Teams ā Dialpad ā Phone ā
āāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāā
ā ā
ā¼ ā¼
āāāāāāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Fireflies.ai Bot ā ā Upload API ā
ā (auto-join meetings)ā ā (manual recordings) ā
āāāāāāāāāāāā¬āāāāāāāāāāāā āāāāāāāāāāāā¬āāāāāāāāāāāāāāā
ā ā
ā¼ ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Fireflies Processing ā
ā Transcription ā Speaker ID ā AI Summary ā Actions ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
āāāāāāāāāāāāāā¼āāāāāāāāāāāāā
ā¼ ā¼ ā¼
āāāāāāāāāāāāāāāā āāāāāāāāāā āāāāāāāāāāāāāāāā
ā Transcript ā ā Action ā ā Webhook ā
ā Storage ā ā Items ā ā Events ā
ā (DB/Search) ā ā (CRM) ā ā (Real-time) ā
āāāāāāāāāāāāāāāā āāāāāāāāāā āāāāāāāāāāāāāāāā
import { GraphQLClient } from 'graphql-request';
const fireflies = new GraphQLClient('https://api.fireflies.ai/graphql', {
headers: { Authorization: `Bearer ${process.env.FIREFLIES_API_KEY}` },
});
// Fetch recent transcripts
const GET_TRANSCRIPTS = `
query GetTranscripts($limit: Int) {
transcripts(limit: $limit) {
id title date duration
organizer_email participants
summary { overview action_items keywords }
}
}
`;
async function getRecentMeetings(limit = 20) {
const data = await fireflies.request(GET_TRANSCRIPTS, { limit });
return data.transcripts;
}
const GET_FULL_TRANSCRIPT = `
query GetTranscript($id: String!) {
transcript(id: $id) {
id title
sentences {
speaker_name text
start_time end_time
}
summary { overview action_items keywords }
meeting_attendees { displayName email }
}
}
`;
interface ProcessedMeeting {
id: string;
title: string;
attendees: string[];
actionItems: string[];
keyTopics: string[];
speakerBreakdown: Record<string, number>;
}
async function processMeeting(id: string): Promise<ProcessedMeeting> {
const { transcript } = await fireflies.request(GET_FULL_TRANSCRIPT, { id });
const speakerTime: Record<string, number> = {};
for (const s of transcript.sentences) {
const duration = s.end_time - s.start_time;
speakerTime[s.speaker_name] = (speakerTime[s.speaker_name] || 0) + duration;
}
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: speakerTime,
};
}
import express from 'express';
const app = express();
app.post('/webhooks/fireflies', express.json(), async (req, res) => {
const { event_type, meeting_id, transcript_id } = req.body;
if (event_type === 'Transcription completed') {
const meeting = await processMeeting(transcript_id);
// Sync action items to CRM
for (const item of meeting.actionItems) {
await createCRMTask(item, meeting.attendees);
}
}
res.json({ received: true });
});
async function weeklyMeetingReport() {
const meetings = await getRecentMeetings(50);
const oneWeekAgo = Date.now() - 7 * 86400000; # 86400000 = configured value
const recent = meetings.filter(
(m: any) => new Date(m.date).getTime() > oneWeekAgo
);
return {
totalMeetings: recent.length,
totalHours: recent.reduce((s: number, m: any) => s + m.duration / 3600, 0).toFixed(1), # 3600: timeout: 1 hour
topAttendees: countAttendees(recent).slice(0, 5),
actionItemCount: recent.reduce((s: number, m: any) =>
s + (m.summary?.action_items?.length || 0), 0),
};
}
| Issue | Cause | Solution |
|---|---|---|
| Empty transcript | Meeting too short | Check minimum duration before processing |
| Missing speakers | Speaker diarization failed | Fall back to participant list |
| GraphQL rate limit | Too many queries | Batch requests, cache transcripts |
| Webhook not firing | URL not configured | Verify in Fireflies dashboard settings |
async function searchMeetings(keyword: string) {
const meetings = await getRecentMeetings(100);
return meetings.filter((m: any) =>
m.title?.toLowerCase().includes(keyword.toLowerCase()) ||
m.summary?.keywords?.some((k: string) => k.toLowerCase().includes(keyword))
);
}