Agent Skills: Fireflies.ai Client Patterns

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/fireflies-sdk-patterns

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/fireflies-pack/skills/fireflies-sdk-patterns

Skill Files

Browse the full folder contents for fireflies-sdk-patterns.

Download Skill

Loading file tree…

plugins/saas-packs/fireflies-pack/skills/fireflies-sdk-patterns/SKILL.md

Skill Metadata

Name
fireflies-sdk-patterns
Description
|

Fireflies.ai Client Patterns

Overview

Production-ready patterns for the Fireflies.ai GraphQL API. Fireflies has no official SDK -- all interaction is via HTTP POST to https://api.fireflies.ai/graphql. These patterns provide typed wrappers, error handling, caching, and multi-tenant support.

Prerequisites

  • FIREFLIES_API_KEY environment variable set
  • TypeScript 5+ or Python 3.10+
  • Optional: graphql-request for typed queries

Instructions

Step 1: Typed GraphQL Client (TypeScript)

// lib/fireflies-client.ts
const FIREFLIES_API = "https://api.fireflies.ai/graphql";

interface FirefliesError {
  message: string;
  code?: string;
  extensions?: { status: number; helpUrls?: string[] };
}

interface FirefliesResponse<T> {
  data?: T;
  errors?: FirefliesError[];
}

export class FirefliesClient {
  private apiKey: string;
  private baseUrl: string;

  constructor(apiKey?: string) {
    this.apiKey = apiKey || process.env.FIREFLIES_API_KEY!;
    this.baseUrl = FIREFLIES_API;
    if (!this.apiKey) throw new Error("FIREFLIES_API_KEY is required");
  }

  async query<T = any>(gql: string, variables?: Record<string, any>): Promise<T> {
    const res = await fetch(this.baseUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${this.apiKey}`,
      },
      body: JSON.stringify({ query: gql, variables }),
    });

    const json: FirefliesResponse<T> = await res.json();

    if (json.errors?.length) {
      const err = json.errors[0];
      const error = new Error(`Fireflies: ${err.message}`) as any;
      error.code = err.code;
      error.status = err.extensions?.status;
      throw error;
    }

    return json.data!;
  }

  // Convenience methods for common queries
  async getUser() {
    return this.query<{ user: any }>(`{ user { name email user_id is_admin } }`);
  }

  async getTranscripts(limit = 20) {
    return this.query<{ transcripts: any[] }>(`
      query($limit: Int) {
        transcripts(limit: $limit) {
          id title date duration organizer_email participants
          summary { overview action_items keywords }
        }
      }
    `, { limit });
  }

  async getTranscript(id: string) {
    return this.query<{ transcript: any }>(`
      query($id: String!) {
        transcript(id: $id) {
          id title date duration
          speakers { id name }
          sentences { speaker_name text start_time end_time }
          summary { overview action_items keywords short_summary }
          analytics {
            sentiments { positive_pct negative_pct neutral_pct }
            speakers { name duration word_count questions }
          }
        }
      }
    `, { id });
  }
}

Step 2: Singleton Pattern

// lib/fireflies.ts
let instance: FirefliesClient | null = null;

export function getFirefliesClient(): FirefliesClient {
  if (!instance) {
    instance = new FirefliesClient();
  }
  return instance;
}

Step 3: Multi-Tenant Factory

const tenantClients = new Map<string, FirefliesClient>();

export function getClientForTenant(tenantId: string): FirefliesClient {
  if (!tenantClients.has(tenantId)) {
    const apiKey = getTenantApiKey(tenantId); // from your secret store
    tenantClients.set(tenantId, new FirefliesClient(apiKey));
  }
  return tenantClients.get(tenantId)!;
}

Step 4: Response Validation with Zod

import { z } from "zod";

const TranscriptSchema = z.object({
  id: z.string(),
  title: z.string(),
  date: z.string(),
  duration: z.number(),
  speakers: z.array(z.object({ id: z.string(), name: z.string() })),
  summary: z.object({
    overview: z.string().nullable(),
    action_items: z.array(z.string()).nullable(),
    keywords: z.array(z.string()).nullable(),
  }).nullable(),
});

type Transcript = z.infer<typeof TranscriptSchema>;

async function getValidatedTranscript(id: string): Promise<Transcript> {
  const client = getFirefliesClient();
  const { transcript } = await client.getTranscript(id);
  return TranscriptSchema.parse(transcript);
}

Step 5: Python Client

import os
from typing import Any
import requests

class FirefliesClient:
    API_URL = "https://api.fireflies.ai/graphql"

    def __init__(self, api_key: str | None = None):
        self.api_key = api_key or os.environ["FIREFLIES_API_KEY"]

    def query(self, gql: str, variables: dict | None = None) -> dict[str, Any]:
        resp = requests.post(
            self.API_URL,
            json={"query": gql, "variables": variables},
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.api_key}",
            },
        )
        data = resp.json()
        if "errors" in data:
            raise Exception(f"Fireflies: {data['errors'][0]['message']}")
        return data["data"]

    def get_transcripts(self, limit: int = 20) -> list[dict]:
        result = self.query("""
            query($limit: Int) {
                transcripts(limit: $limit) {
                    id title date duration organizer_email
                    summary { overview action_items keywords }
                }
            }
        """, {"limit": limit})
        return result["transcripts"]

    def get_transcript(self, transcript_id: str) -> dict:
        result = self.query("""
            query($id: String!) {
                transcript(id: $id) {
                    id title date duration
                    speakers { name }
                    sentences { speaker_name text start_time end_time }
                    summary { overview action_items keywords }
                }
            }
        """, {"id": transcript_id})
        return result["transcript"]

# Usage
client = FirefliesClient()
for t in client.get_transcripts(5):
    print(f"{t['title']} - {t['duration']}min")

Error Handling

| Pattern | Use Case | Benefit | |---------|----------|---------| | Typed client class | All API calls | Centralized auth and error handling | | Singleton | Single-tenant apps | Reuse connection, consistent config | | Factory | Multi-tenant SaaS | Isolated API keys per customer | | Zod validation | API responses | Runtime type safety, catches schema drift |

Output

  • Type-safe GraphQL client with error codes
  • Singleton and factory patterns for different deployment models
  • Zod schemas for runtime response validation
  • Python client with identical API surface

Resources

Next Steps

Apply patterns in fireflies-core-workflow-a for real-world usage.