Fireflies.ai Reference Architecture
Overview
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.
Prerequisites
- Fireflies.ai API key with GraphQL access
- GraphQL client (
graphql-requestorurql) - CRM system for action item sync (optional)
- Database for transcript storage
Architecture Diagram
┌──────────────────────────────────────────────────────┐
│ 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) │
└──────────────┘ └────────┘ └──────────────┘Instructions
Step 1: GraphQL Client Setup
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;
}Step 2: Transcript Processing Pipeline
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,
};
}Step 3: Webhook Integration
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 });
});Step 4: Meeting Analytics Aggregation
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),
};
}Error Handling
| 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 |
Examples
Quick Meeting Search
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))
);
}Resources
Output
- Configuration files or code changes applied to the project
- Validation report confirming correct implementation
- Summary of changes made and their rationale