fireflies
Query Fireflies.ai meeting transcripts, meeting notes, summaries, contacts, channels, AI meeting analytics, AskFred, audio uploads, and webhook signatures through its GraphQL API. Use when a user mentions Fireflies, Fireflies.ai, meeting transcripts or notes stored in Fireflies,
Install
npx skills add https://github.com/magnus919/agent-skills/tree/main/fireflies
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install magnus919-agent-skills@llmmart
git clone https://github.com/magnus919/agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole magnus919/agent-skills collection as a plugin from our marketplace. Git is the plain clone.
README
Fireflies.ai: meeting intelligence from the terminal
Why Install This Skill
Turn Fireflies meetings into usable data without hand-copying notes. Search transcripts, inspect summaries and action items, review analytics, fetch audio and video recording links, and ask focused questions with AskFred from a single dependency-free command line tool.
It also makes sensitive operations deliberate: every mutation requires an explicit confirmation, and every mutation can be previewed locally before it is sent.
What You Get
| Path | What it provides |
|---|---|
scripts/fireflies |
Python 3.9+ GraphQL CLI with safe reads, mutations, dry-runs, and webhook verification |
references/api-reference.md |
API model, operation families, limits, and source links |
references/cli-reference.md |
Complete CLI contract and examples |
references/workflows.md |
Transcript, analytics, upload, AskFred, and GraphQL recipes |
references/webhook-security.md |
Webhooks V2 verification guidance |
references/troubleshooting.md |
Failure diagnosis and escalation boundaries |
Quick Start
export FIREFLIES_API_KEY='...'
python3 scripts/fireflies transcripts list --keyword roadmap --limit 10 --json
Output is the Fireflies GraphQL response, for example:
{"data":{"transcripts":[{"id":"...","title":"Roadmap review"}]}}
Preview a change without calling the API:
python3 scripts/fireflies meetings rename transcript-id --title "Q3 roadmap" --dry-run --json
Triggers
- Fireflies.ai transcripts, summaries, notes, contacts, channels, or analytics
- Meeting audio/video recording download links (
transcripts getreturnsvideo_url/audio_url) - AskFred questions about meeting content
- Remote audio upload to Fireflies
- Fireflies Webhooks V2 signature verification
Requirements
- Python 3.9 or newer
- A Fireflies API key in
FIREFLIES_API_KEYfor API calls - Network access to
https://api.fireflies.ai/graphql - No third-party Python packages
Skill manifest
Fireflies.ai
Use scripts/fireflies from this skill directory. It uses the Fireflies GraphQL endpoint directly
and emits the API response as JSON. Read-only discovery is safe. Before any state change, confirm
the target, scope, and rollback path; then route the change through the CLI's literal --confirm.
--dry-run previews a payload only and never authorizes a write.
First Use
- Check the command schema:
scripts/fireflies --helpand the relevant subcommand help. - Set
FIREFLIES_API_KEYonly for an actual API request. Do not expose or persist it. - Start with read-only discovery, such as
scripts/fireflies transcripts list --limit 10 --json. - Use
--dry-run --jsonbefore each mutation to inspect the exact GraphQL payload.
Command Map
| Need | Command |
|---|---|
| Find transcript metadata | scripts/fireflies transcripts list --keyword TEXT --limit 10 --json |
| Read a meeting, summary, sentences, analytics, and recording links | scripts/fireflies transcripts get ID --json |
| People, channels, groups, contacts, apps | users, channels, groups, contacts, apps |
| Meeting analytics or live meetings | analytics --start DATE --end DATE, meetings active |
| Ask a transcript question | askfred create --question TEXT --transcript-id ID --confirm |
| Read one AskFred thread | askfred get THREAD_ID |
| Assign an admin/user role | users set-role --user-id ID --role admin --confirm |
| Move meetings into a channel | meetings update-channel --channel-id ID --transcript-ids A,B,C --confirm |
| Add a live meeting by link | live add-to --meeting-link URL --confirm |
| Create a soundbite clip | live soundbite --meeting-id ID --prompt TEXT --confirm |
| Two-phase file upload | audio create-upload ... --confirm then audio confirm-upload --meeting-id ID --confirm |
| Run an exact current/future API operation | query --document GRAPHQL --variables JSON |
| Verify a delivered webhook locally | webhook verify --secret SECRET --signature sha256=... --body FILE |
Safe Mutation Workflow
- Identify the Fireflies object ID and present the intended change.
- Confirm target, scope, and rollback path with the user. Deletion may not be reversible.
- Preview the exact document using
--dry-run; this makes no HTTP request. - Run exactly the approved mutation with
--confirm. - Return the response without printing credentials.
Examples:
scripts/fireflies meetings rename transcript-id --title "Weekly product review" --dry-run --json
scripts/fireflies meetings rename transcript-id --title "Weekly product review" --confirm --json
scripts/fireflies transcripts delete transcript-id --confirm --json
Reference Routing
- Read references/cli-reference.md for syntax, output, exit codes, or generic GraphQL execution.
- Read references/api-reference.md for the API model, exact documented operation families, limits, permissions, and source links.
- Read references/workflows.md for transcript, analytics, upload, AskFred, and generic-operation recipes.
- Read references/webhook-security.md when receiving or implementing Webhooks V2.
- Read references/troubleshooting.md after an auth, GraphQL, limit, permission, pagination, or webhook failure.
- Read references/source-index.md when validating source scope or documentation freshness.
Boundaries
The CLI never starts a webhook server, stores API keys, or guesses undocumented GraphQL fields.
If an ergonomic command does not cover the needed current operation, use query or mutation
with a documented GraphQL document. The generic mutation command still requires --confirm.
Files (agent-skills)
-
evals
-
evals.json 4.9 KB
{ "schema_version": 1, "skill_name": "fireflies", "evals": [ { "id": "list-transcripts", "prompt": "I recorded a meeting about the Q3 roadmap and I need to find its transcript. Use the Fireflies skill to list the available transcripts and locate the one I am looking for. How would you do that?", "expected_output": "The agent uses the fireflies skill's transcripts list command with a keyword filter to search meeting titles, then reads the returned transcript metadata (id, title, date, organizer) to identify the Q3 roadmap meeting. It explains that the command is a read-only GraphQL query requiring FIREFLIES_API_KEY, and that the response is JSON.", "assertions": [ "Uses the fireflies transcripts list command with a keyword filter", "Notes the command is a read-only query requiring FIREFLIES_API_KEY", "Identifies the transcript by its id and title from the returned metadata", "Does not propose a mutation for a read-only lookup" ] }, { "id": "inspect-transcript", "prompt": "I have a transcript ID and want to understand what was discussed: the summary, who spoke, and what was said. What does the skill offer for inspecting a single meeting in detail?", "expected_output": "The agent points to the fireflies transcripts get command with the transcript ID, which returns the transcript with summary overview, speakers, timestamped sentences, and sentiment analytics. It explains the fields are selected conservatively and the output is JSON.", "assertions": [ "Uses the fireflies transcripts get command with a transcript ID", "Mentions the summary, speakers, and sentences in the returned detail", "Notes the output is JSON", "Keeps the answer to a single read-only command" ] }, { "id": "mutation-safety", "prompt": "I want to rename a meeting to 'Q3 planning sync'. The skill's mutations require confirmation. Show me how to preview the exact change before sending it, then how to apply it.", "expected_output": "The agent first runs the fireflies meetings rename command with --dry-run --json to inspect the exact GraphQL mutation and payload without making an HTTP request, then runs the same command with --confirm to apply it. It explains that every mutation requires the literal --confirm flag and that dry-run needs no API key.", "assertions": [ "Uses the fireflies meetings rename command", "Runs --dry-run --json first to preview the payload", "Runs --confirm to apply the approved change", "Explains dry-run makes no HTTP request and needs no API key" ] }, { "id": "webhook-verification", "prompt": "Fireflies delivered a Webhooks V2 event to my server and I want to verify the HMAC signature locally before trusting the payload. How does the skill handle this?", "expected_output": "The agent uses the fireflies webhook verify command with the shared secret, the sha256= signature, and the payload file path. It explains this is a fully local HMAC-SHA256 check that makes no network request and requires no API key, and returns a JSON result with a valid boolean and the event name.", "assertions": [ "Uses the fireflies webhook verify command", "Passes --secret, --signature, and --body", "Explains the check is local HMAC-SHA256 with no network call", "Mentions the JSON result reports whether the signature is valid" ] }, { "id": "askfred-questions", "prompt": "I want to ask a natural-language question about a meeting transcript, like 'What decisions were made?', and then ask a follow-up. What does the skill provide?", "expected_output": "The agent describes the AskFred workflow: fireflies askfred create with --question and --transcript-id to start a thread, then askfred continue with the thread ID and a new --question for the follow-up. It notes AskFred mutations require AI credits and --confirm.", "assertions": [ "Uses fireflies askfred create with --question and --transcript-id", "Uses fireflies askfred continue with the thread ID for follow-ups", "Notes AskFred mutations require AI credits and --confirm", "Does not claim AskFred works without an API key" ] }, { "id": "recording-urls", "prompt": "I have a transcript ID for a meeting I attended and I want to download the audio and video recordings of it. How does the skill give me those files?", "expected_output": "The agent uses the fireflies transcripts get command with the transcript ID; the returned JSON includes video_url and audio_url fields, which are the download links for the meeting's video and audio recordings. It explains the query is a read-only GraphQL query requiring FIREFLIES_API_KEY and returns JSON.", "assertions": [ "Uses the fireflies transcripts get command with a transcript ID", "Identifies video_url and audio_url in the returned fields as the recording download links", "Notes the query is read-only and requires FIREFLIES_API_KEY", "Does not propose an upload or mutation for a read-only lookup" ] } ] }
-
-
references
-
api-reference.md 3.9 KB
# Fireflies API Reference ## Model and Auth Fireflies exposes GraphQL at `https://api.fireflies.ai/graphql`. Send JSON with `query`, optional `variables`, and optional `operationName`; authenticate with `Authorization: Bearer <API key>`. Queries are read-only. Mutations create, update, or delete server-side state. Use `scripts/fireflies query` and `scripts/fireflies mutation` as the compatibility escape hatch for every documented current or future operation. The CLI rejects mutations on `query`, queries on `mutation`, and requires `--confirm` for a non-dry-run generic mutation. ## Documented Families | Family | Documented operation | |---|---| | Meetings | `transcripts`, `transcript`, `deleteTranscript`, `updateMeetingTitle`, `updateMeetingPrivacy`, `updateMeetingState`, `updateMeetingChannel`, `shareMeeting`, `revokeSharedMeetingAccess` | | Workspace | `user`, `users`, `contacts`, `channels`, `channel`, `user_groups`, `setUserRole` | | Content | `bites`, `bite`, `createBite`, `apps`, `analytics` | | Live | `active_meetings`, `live_action_items`, `createLiveActionItem`, `addToLiveMeeting`, `createLiveSoundbite` | | Automation | `auditEvents`, `rule_executions_by_meeting`, `uploadAudio`, `createUploadUrl`, `confirmUpload` | | AskFred | `askfred_threads`, `askfred_thread`, `createAskFredThread`, `continueAskFredThread`, `deleteAskFredThread` | The ergonomic CLI documents use conservative selections. `live add` calls `createLiveActionItem(input: CreateLiveActionItemInput!)` with `meeting_id` and `prompt` only; `live add-to` calls `addToLiveMeeting` with the documented flat arguments; `live soundbite` calls `createLiveSoundbite`. Use the generic `mutation` escape hatch for any operation not represented by an ergonomic command. Use introspection only through `schema introspect`; availability depends on the deployment. ## Live-Schema Audit (2026-08-05) CLI documents were re-validated against live GraphQL introspection on 2026-08-05. The published docs at docs.fireflies.ai drifted from the live schema in the following ways, which the CLI now follows (live schema wins): - `TranscriptsQueryScope` is documented for the `transcripts` query but absent from the live schema; `scope` is a plain `String` there. - `transcripts` organizers/participants require non-null elements (`[String!]`), and the query accepts `title`, `organizer_email`, and `participant_email` filters. - `createBite` names its argument `transcript_Id` (capital I) and `privacies` takes `[BitePrivacy!]` (enum: `public`, `team`, `participants`). - `shareMeeting` input gained `expiry_days` (7, 14, or 30). - `transcript` selection gained `video_url` and `audio_url` (meeting recording download links), verified by live introspection on 2026-08-10. - `updateMeetingChannel` (changelog 2.15.0), `addToLiveMeeting`, `createLiveSoundbite`, `createUploadUrl`/`confirmUpload`, `setUserRole`, and `askfred_thread` were added as ergonomic commands after the audit found them documented but uncovered. ## Pagination and Limits `transcripts` uses `limit` plus `skip`, with a documented maximum limit of 50. The CLI rejects a higher value. Bites also support `limit` and `skip`; their list query requires one of `mine`, `transcript_id`, or `my_team`. Respect plan limits: Free is 50 requests/day, Pro 500/day, and Business/Enterprise 60/minute. Add to Live is 3 per 20 minutes, meeting sharing is 10/hour, and `deleteTranscript` is 10/minute. ## Errors and Access GraphQL errors are returned in `errors` and can include `message`, `code`, `friendly`, and `extensions.helpUrls`. The CLI preserves that response on stdout in JSON mode and exits 5. `too_many_requests` can include `retryAfter`; delay before retrying. Audit events and rule execution logs have Enterprise/admin restrictions. Do not infer the caller's plan or authorization locally. AskFred mutations require AI credits. ## Primary Sources See [source-index.md](source-index.md) for dated primary documentation URLs and claim scope. -
cli-reference.md 2.1 KB
# CLI Reference Run `scripts/fireflies --help` for the authoritative interface. Global flags work before or after subcommands: `--json`, `--api-key`, `--endpoint`, `--timeout`, `--dry-run`, `--quiet`, and `--verbose`. `--json` writes one JSON document to stdout; diagnostics are stderr-only. ## Commands | Command | Purpose | |---|---| | `query --document DOC [--variables JSON|--variables-file PATH]` | Generic read-only GraphQL | | `mutation --document DOC ... --confirm` | Generic mutation | | `transcripts list|get|delete` | Search, inspect, or delete meetings; `get` also returns `video_url`/`audio_url` recording download links | | `users`, `contacts`, `channels`, `groups`, `bites`, `apps` | Workspace/content reads; `users set-role --user-id ID --role admin|user` assigns roles | | `analytics`, `meetings active`, `live-action-items` | Analytics and live data | | `meetings rename|privacy|state|share|revoke-share|update-channel` | Meeting mutations; `share` accepts `--expiry-days` | | `bites create`, `live add|add-to|soundbite`, `audio upload|create-upload|confirm-upload` | Creation/upload mutations; `live add` creates a live action item, `add-to` joins a live meeting by link, `soundbite` clips one; `audio create-upload`/`confirm-upload` are the two-phase file upload | | `askfred threads|get|create|continue|delete` | AskFred workflow | | `audit-events`, `rule-executions` | Enterprise/admin queries | | `webhook verify` | Local signature check, no network | | `schema introspect` | Explicit GraphQL introspection | ## Examples ```bash scripts/fireflies query --document 'query { user { name } }' --json scripts/fireflies transcripts list --from-date 2026-01-01 --to-date 2026-01-31 --limit 50 --json scripts/fireflies mutation --document 'mutation X { ... }' --confirm --dry-run --json scripts/fireflies webhook verify --secret "$WEBHOOK_SECRET" --signature sha256=... --body payload.json --json ``` Variables must be a JSON object, inline or from a file. `--dry-run` returns the exact endpoint and payload and does not need an API key. Exit codes: 2 usage/input, 3 configuration, 4 transport/HTTP, 5 GraphQL errors, 6 confirmation refused. -
source-index.md 2 KB
# Source Index Access date: 2026-07-15. Sources are primary Fireflies documentation only. CLI documents were re-validated against live schema introspection on 2026-08-05 (see api-reference.md for the documented drift between these pages and the live API). | Source | Relevant claim and scope | |---|---| | https://docs.fireflies.ai/getting-started/introduction | API introduction and GraphQL model | | https://docs.fireflies.ai/fundamentals/authorization | Bearer authorization and API endpoint | | https://docs.fireflies.ai/fundamentals/limits | Plan, sharing, and Add to Live limits | | https://docs.fireflies.ai/fundamentals/errors | GraphQL `errors` response shape | | https://docs.fireflies.ai/fundamentals/introspection | Introspection behavior | | https://docs.fireflies.ai/graphql-api/query/transcripts | Transcript filters, pagination, and fields | | https://docs.fireflies.ai/graphql-api/query/transcript | Single transcript fields | | https://docs.fireflies.ai/graphql-api/query/analytics | Analytics variables and selections | | https://docs.fireflies.ai/graphql-api/mutation/create-live-action-item | `createLiveActionItem` input and response | | https://docs.fireflies.ai/graphql-api/mutation/delete-askfred-thread | `deleteAskFredThread` response selection | | https://docs.fireflies.ai/graphql-api/query/audit-events | Conservative audit event query selection | | https://docs.fireflies.ai/graphql-api/webhooks-v2 | V2 event names, HMAC signature, response deadline | | https://docs.fireflies.ai/graphql-api/mutation/delete-transcript | `deleteTranscript` mutation and limit | | https://docs.fireflies.ai/graphql-api/mutation/update-meeting-title | `updateMeetingTitle` input | | https://docs.fireflies.ai/graphql-api/mutation/upload-audio | `uploadAudio` input | | https://docs.fireflies.ai/askfred/overview | AskFred operation family and AI-credit requirement | | https://docs.fireflies.ai/llms-full.txt | Current primary query/mutation schema documentation used for CLI documents | -
troubleshooting.md 1 KB
# Troubleshooting - **Missing key:** set `FIREFLIES_API_KEY`, pass `--api-key`, or use `--dry-run`. Help and local webhook checks need no key. - **Authentication failure:** verify `Authorization: Bearer <key>` and regenerate/check the key in Fireflies Integrations. - **GraphQL error:** inspect JSON `errors`, especially `code` and `extensions.helpUrls`; do not retry malformed documents unchanged. - **`too_many_requests`:** honor `retryAfter` when present and reduce request rate. - **Permission or plan restriction:** audit events/rule executions can require Enterprise/admin access; AskFred mutations need AI credits. Escalate access, do not infer it. - **Pagination:** use transcript `limit` (50 or less) and `skip`; use documented cursors for audit/rule queries. - **Webhook verification failure:** compare the raw body, unmodified signature header, and the correct shared secret. Do not verify re-serialized JSON. - **Safe escalation:** capture the command (without credentials), exit code, response error code, and help URL; provide these to Fireflies support. -
webhook-security.md 790 B
# Webhook Security Webhooks V2 events are `meeting.transcribed`, `meeting.summarized`, and `meeting.bot_joined`. Fireflies sends `X-Hub-Signature: sha256=<hex>`, an HMAC-SHA256 over the exact raw request body. Verify it before parsing or acting on the event. Use timing-safe comparison. ```bash scripts/fireflies webhook verify --secret "$WEBHOOK_SECRET" --signature "$X_HUB_SIGNATURE" --body request-body.json --json ``` Use `--body -` to read raw bytes from stdin. This command is a local verifier, never makes a network call, and does not print the secret. A receiving endpoint should return a 2xx response within 10 seconds. Persist or queue work after verification; handle retries idempotently and avoid assuming delivery order. Test valid and invalid signatures before deployment. -
workflows.md 2.7 KB
# Workflows ## List, Filter, and Read a Transcript Use this to locate meetings and then inspect their content. ```bash scripts/fireflies transcripts list --keyword roadmap --organizers owner@example.com --limit 10 --json scripts/fireflies transcripts get transcript-id --json ``` The detailed query includes summary, speakers, sentences, and analytics. ## Fetch Recording Download URLs Use this to grab the meeting's audio and video recordings. The response's `video_url` and `audio_url` fields are the download links for the recording. ```bash scripts/fireflies transcripts get transcript-id --json # response includes "video_url" and "audio_url" — signed download links for the recording ``` ## Extract Actions and Summary Use this for a currently live meeting or for a completed transcript. ```bash scripts/fireflies live-action-items meeting-id --json scripts/fireflies transcripts get transcript-id --json ``` ## Team Analytics Use a bounded time range for trend reporting. ```bash scripts/fireflies analytics --start 2026-01-01 --end 2026-01-31 --json ``` ## Upload Audio with Webhook Correlation Use a publicly retrievable HTTPS media URL and a webhook endpoint you control. Preview first. ```bash scripts/fireflies audio upload --url https://media.example/call.mp3 --webhook https://app.example/fireflies --client-reference-id import-42 --dry-run --json scripts/fireflies audio upload --url https://media.example/call.mp3 --webhook https://app.example/fireflies --client-reference-id import-42 --confirm --json ``` ## Upload a File (Two-Phase) Use this when the media is not on a public URL and you must push the bytes yourself. ```bash scripts/fireflies audio create-upload --content-type audio/wav --file-size 5242880 --dry-run --json scripts/fireflies audio create-upload --content-type audio/wav --file-size 5242880 --confirm --json # PUT the file to upload_url from the response, then: scripts/fireflies audio confirm-upload --meeting-id meeting-id --confirm --json ``` ## AskFred Conversation Use AskFred for natural-language analysis when the account has AI credits. ```bash scripts/fireflies askfred create --question 'What decisions were made?' --transcript-id transcript-id --confirm --json scripts/fireflies askfred continue thread-id --question 'Who owns the follow-up?' --confirm --json ``` ## Generic GraphQL Use this when a documented operation is newer than the CLI's ergonomic commands. Copy the document from Fireflies primary documentation, pass variables as JSON, and preview mutations before confirmation. ```bash scripts/fireflies query --document 'query { user { name } }' --json scripts/fireflies mutation --document 'mutation Example($input: SomeInput!) { someMutation(input: $input) { success } }' --variables '{"input":{}}' --dry-run --json ```
-
-
scripts
-
fireflies 29.8 KB · in bundle
-
-
tests
-
test_fireflies.py 9.5 KB
import hashlib import hmac import json import os import subprocess import tempfile import threading import unittest from http.server import BaseHTTPRequestHandler, HTTPServer from pathlib import Path CLI = Path(__file__).parents[1] / "scripts" / "fireflies" class Handler(BaseHTTPRequestHandler): calls = [] response = {"data": {"ok": True}} status = 200 def do_POST(self): body = self.rfile.read(int(self.headers["Content-Length"])) self.__class__.calls.append((dict(self.headers), json.loads(body))) self.send_response(self.__class__.status); self.send_header("Content-Type", "application/json"); self.end_headers() self.wfile.write(json.dumps(self.__class__.response).encode()) def log_message(self, *_): pass class FirefliesTests(unittest.TestCase): @classmethod def setUpClass(cls): cls.server = HTTPServer(("127.0.0.1", 0), Handler) cls.thread = threading.Thread(target=cls.server.serve_forever, daemon=True); cls.thread.start() cls.endpoint = "http://127.0.0.1:%s/graphql" % cls.server.server_port @classmethod def tearDownClass(cls): cls.server.shutdown() def run_cli(self, *args, key=False): env = os.environ.copy(); env.pop("FIREFLIES_API_KEY", None) if key: env["FIREFLIES_API_KEY"] = "test-key" return subprocess.run(["python3", str(CLI), *args], text=True, capture_output=True, env=env) def test_dry_run_needs_no_key_and_no_http(self): Handler.calls.clear(); result=self.run_cli("transcripts","list","--dry-run","--json") self.assertEqual(result.returncode,0); self.assertEqual(Handler.calls,[]); self.assertTrue(json.loads(result.stdout)["dry_run"]) def test_read_uses_bearer_and_payload(self): Handler.calls.clear(); Handler.response={"data":{"users":[]}} result=self.run_cli("--endpoint",self.endpoint,"users","list","--json",key=True) self.assertEqual(result.returncode,0); self.assertEqual(Handler.calls[-1][0]["Authorization"],"Bearer test-key"); self.assertIn("query Users",Handler.calls[-1][1]["query"]) def test_graphql_error_json_and_exit_five(self): Handler.response={"errors":[{"message":"denied","code":"auth_failed"}]} result=self.run_cli("--endpoint",self.endpoint,"users","list","--json",key=True) self.assertEqual(result.returncode,5); self.assertEqual(json.loads(result.stdout)["errors"][0]["message"],"denied"); self.assertIn("GraphQL error",result.stderr) Handler.response={"data":{"ok":True}} def test_http_error_with_graphql_errors_exits_five(self): Handler.status=400; Handler.response={"errors":[{"message":"denied","code":"auth_failed"}]} result=self.run_cli("--endpoint",self.endpoint,"users","list","--json",key=True) self.assertEqual(result.returncode,5); self.assertEqual(json.loads(result.stdout)["errors"][0]["message"],"denied"); self.assertIn("GraphQL error",result.stderr) Handler.status=200; Handler.response={"data":{"ok":True}} def test_generic_guards(self): self.assertEqual(self.run_cli("query","--document","mutation { x }","--json").returncode,2) self.assertEqual(self.run_cli("mutation","--document","mutation { x }","--json").returncode,6) def test_limit_json_and_help_examples(self): self.assertEqual(self.run_cli("transcripts","list","--limit","51","--json").returncode,2) result=self.run_cli("transcripts","list","--dry-run","--json"); json.loads(result.stdout); self.assertEqual(result.stderr,"") self.assertIn("Examples:",self.run_cli("transcripts","list","--help").stdout) def test_webhook_signatures(self): with tempfile.NamedTemporaryFile("wb",delete=False) as f: f.write(b'{"event":"meeting.transcribed"}'); path=f.name digest=hmac.new(b"test",Path(path).read_bytes(),hashlib.sha256).hexdigest() self.assertTrue(json.loads(self.run_cli("webhook","verify","--secret","test","--signature","sha256="+digest,"--body",path,"--json").stdout)["valid"]) self.assertFalse(json.loads(self.run_cli("webhook","verify","--secret","test","--signature","sha256=bad","--body",path,"--json").stdout)["valid"]) os.unlink(path) def test_mutation_dry_run_payload(self): result=self.run_cli("meetings","rename","abc","--title","New","--dry-run","--json") payload=json.loads(result.stdout)["payload"]; self.assertIn("updateMeetingTitle",payload["query"]); self.assertEqual(payload["variables"]["input"]["id"],"abc") def test_source_faithful_dry_run_payloads(self): live=json.loads(self.run_cli("live","add","--meeting-id","meeting-id","--action-item","Follow up","--dry-run","--json").stdout)["payload"] self.assertIn("createLiveActionItem(input: $input) { success }",live["query"]) self.assertEqual(live["variables"],{"input":{"meeting_id":"meeting-id","prompt":"Follow up"}}) deleted=json.loads(self.run_cli("askfred","delete","thread-id","--dry-run","--json").stdout)["payload"] self.assertIn("id title transcript_id user_id created_at",deleted["query"]) self.assertEqual(deleted["variables"],{"id":"thread-id"}) analytics=json.loads(self.run_cli("analytics","--start","2026-01-01","--end","2026-01-31","--dry-run","--json").stdout)["payload"] self.assertIn("$startTime",analytics["query"]); self.assertIn("$endTime",analytics["query"]) self.assertEqual(analytics["variables"],{"startTime":"2026-01-01","endTime":"2026-01-31"}) transcript=json.loads(self.run_cli("transcripts","get","transcript-id","--dry-run","--json").stdout)["payload"] self.assertIn("negative_pct neutral_pct positive_pct",transcript["query"]) self.assertIn("video_url audio_url",transcript["query"]) audit=json.loads(self.run_cli("audit-events","--filter",'{"category":"MEETING_OPERATIONS"}',"--dry-run","--json").stdout)["payload"] self.assertIn("events { id time action actor { user_id } resource { type id } }",audit["query"]) def test_transcripts_list_document_is_current(self): payload=json.loads(self.run_cli("transcripts","list","--title","X","--organizers","a@b.c","--participants","d@e.f","--dry-run","--json").stdout)["payload"] self.assertNotIn("TranscriptsQueryScope",payload["query"]) self.assertIn("$organizers: [String!]",payload["query"]); self.assertIn("$participants: [String!]",payload["query"]) self.assertIn("title: $title",payload["query"]) self.assertEqual(payload["variables"]["organizers"],["a@b.c"]); self.assertEqual(payload["variables"]["participants"],["d@e.f"]) self.assertEqual(payload["variables"]["title"],"X") def test_bite_create_uses_live_arg_names_and_enum(self): payload=json.loads(self.run_cli("bites","create","--transcript-id","tid","--start","0","--end","30","--privacy","participants","--dry-run","--json").stdout)["payload"] self.assertIn("transcript_Id",payload["query"]); self.assertIn("[BitePrivacy!]",payload["query"]) self.assertEqual(payload["variables"]["privacy"],["participants"]) def test_meetings_update_channel_payload(self): payload=json.loads(self.run_cli("meetings","update-channel","--channel-id","CH","--transcript-ids","A,B,C","--dry-run","--json").stdout)["payload"] self.assertIn("updateMeetingChannel",payload["query"]) self.assertEqual(payload["variables"]["input"],{"transcript_ids":["A","B","C"],"channel_id":"CH"}) def test_meetings_share_expiry_payload(self): payload=json.loads(self.run_cli("meetings","share","mid","--emails","a@b.c,d@e.f","--expiry-days","14","--dry-run","--json").stdout)["payload"] self.assertEqual(payload["variables"]["input"]["expiry_days"],14) self.assertEqual(payload["variables"]["input"]["emails"],["a@b.c","d@e.f"]) def test_live_add_to_payload(self): payload=json.loads(self.run_cli("live","add-to","--meeting-link","https://x","--title","T","--duration","30","--dry-run","--json").stdout)["payload"] self.assertIn("addToLiveMeeting",payload["query"]) self.assertEqual(payload["variables"]["meeting_link"],"https://x"); self.assertEqual(payload["variables"]["duration"],30) def test_live_soundbite_payload(self): payload=json.loads(self.run_cli("live","soundbite","--meeting-id","M","--prompt","P","--dry-run","--json").stdout)["payload"] self.assertIn("createLiveSoundbite",payload["query"]) self.assertEqual(payload["variables"]["input"],{"meeting_id":"M","prompt":"P"}) def test_audio_two_phase_upload_payloads(self): create=json.loads(self.run_cli("audio","create-upload","--content-type","audio/wav","--file-size","1000","--dry-run","--json").stdout)["payload"] self.assertIn("createUploadUrl",create["query"]); self.assertEqual(create["variables"]["input"],{"content_type":"audio/wav","file_size":1000}) confirm=json.loads(self.run_cli("audio","confirm-upload","--meeting-id","M","--dry-run","--json").stdout)["payload"] self.assertIn("confirmUpload",confirm["query"]); self.assertEqual(confirm["variables"]["input"],{"meeting_id":"M"}) def test_users_set_role_payload(self): payload=json.loads(self.run_cli("users","set-role","--user-id","U","--role","admin","--dry-run","--json").stdout)["payload"] self.assertIn("setUserRole",payload["query"]); self.assertEqual(payload["variables"],{"userId":"U","role":"admin"}) def test_askfred_get_payload(self): payload=json.loads(self.run_cli("askfred","get","thread-id","--dry-run","--json").stdout)["payload"] self.assertIn("askfred_thread(id: $id)",payload["query"]); self.assertEqual(payload["variables"],{"id":"thread-id"}) if __name__ == "__main__": unittest.main()
-
-
README.md 2 KB
# Fireflies.ai: meeting intelligence from the terminal ## Why Install This Skill Turn Fireflies meetings into usable data without hand-copying notes. Search transcripts, inspect summaries and action items, review analytics, fetch audio and video recording links, and ask focused questions with AskFred from a single dependency-free command line tool. It also makes sensitive operations deliberate: every mutation requires an explicit confirmation, and every mutation can be previewed locally before it is sent. ## What You Get | Path | What it provides | |---|---| | `scripts/fireflies` | Python 3.9+ GraphQL CLI with safe reads, mutations, dry-runs, and webhook verification | | `references/api-reference.md` | API model, operation families, limits, and source links | | `references/cli-reference.md` | Complete CLI contract and examples | | `references/workflows.md` | Transcript, analytics, upload, AskFred, and GraphQL recipes | | `references/webhook-security.md` | Webhooks V2 verification guidance | | `references/troubleshooting.md` | Failure diagnosis and escalation boundaries | ## Quick Start ```bash export FIREFLIES_API_KEY='...' python3 scripts/fireflies transcripts list --keyword roadmap --limit 10 --json ``` Output is the Fireflies GraphQL response, for example: ```json {"data":{"transcripts":[{"id":"...","title":"Roadmap review"}]}} ``` Preview a change without calling the API: ```bash python3 scripts/fireflies meetings rename transcript-id --title "Q3 roadmap" --dry-run --json ``` ## Triggers - Fireflies.ai transcripts, summaries, notes, contacts, channels, or analytics - Meeting audio/video recording download links (`transcripts get` returns `video_url`/`audio_url`) - AskFred questions about meeting content - Remote audio upload to Fireflies - Fireflies Webhooks V2 signature verification ## Requirements - Python 3.9 or newer - A Fireflies API key in `FIREFLIES_API_KEY` for API calls - Network access to `https://api.fireflies.ai/graphql` - No third-party Python packages -
SKILL.md 4.4 KB
--- name: fireflies description: >- Query Fireflies.ai meeting transcripts, meeting notes, summaries, contacts, channels, AI meeting analytics, AskFred, audio uploads, and webhook signatures through its GraphQL API. Use when a user mentions Fireflies, Fireflies.ai, meeting transcripts or notes stored in Fireflies, AskFred, or Fireflies webhooks. Do not use for local audio transcription, calendar management, or meetings that are not Fireflies data. license: MIT compatibility: Requires Python 3.9+, network access for API calls, and FIREFLIES_API_KEY for non-dry-run API requests. metadata: service: fireflies.ai api: graphql allowed-tools: Bash Read --- # Fireflies.ai Use `scripts/fireflies` from this skill directory. It uses the Fireflies GraphQL endpoint directly and emits the API response as JSON. Read-only discovery is safe. Before any state change, confirm the target, scope, and rollback path; then route the change through the CLI's literal `--confirm`. `--dry-run` previews a payload only and never authorizes a write. ## First Use 1. Check the command schema: `scripts/fireflies --help` and the relevant subcommand help. 2. Set `FIREFLIES_API_KEY` only for an actual API request. Do not expose or persist it. 3. Start with read-only discovery, such as `scripts/fireflies transcripts list --limit 10 --json`. 4. Use `--dry-run --json` before each mutation to inspect the exact GraphQL payload. ## Command Map | Need | Command | |---|---| | Find transcript metadata | `scripts/fireflies transcripts list --keyword TEXT --limit 10 --json` | | Read a meeting, summary, sentences, analytics, and recording links | `scripts/fireflies transcripts get ID --json` | | People, channels, groups, contacts, apps | `users`, `channels`, `groups`, `contacts`, `apps` | | Meeting analytics or live meetings | `analytics --start DATE --end DATE`, `meetings active` | | Ask a transcript question | `askfred create --question TEXT --transcript-id ID --confirm` | | Read one AskFred thread | `askfred get THREAD_ID` | | Assign an admin/user role | `users set-role --user-id ID --role admin --confirm` | | Move meetings into a channel | `meetings update-channel --channel-id ID --transcript-ids A,B,C --confirm` | | Add a live meeting by link | `live add-to --meeting-link URL --confirm` | | Create a soundbite clip | `live soundbite --meeting-id ID --prompt TEXT --confirm` | | Two-phase file upload | `audio create-upload ... --confirm` then `audio confirm-upload --meeting-id ID --confirm` | | Run an exact current/future API operation | `query --document GRAPHQL --variables JSON` | | Verify a delivered webhook locally | `webhook verify --secret SECRET --signature sha256=... --body FILE` | ## Safe Mutation Workflow 1. Identify the Fireflies object ID and present the intended change. 2. Confirm target, scope, and rollback path with the user. Deletion may not be reversible. 3. Preview the exact document using `--dry-run`; this makes no HTTP request. 4. Run exactly the approved mutation with `--confirm`. 5. Return the response without printing credentials. Examples: ```bash scripts/fireflies meetings rename transcript-id --title "Weekly product review" --dry-run --json scripts/fireflies meetings rename transcript-id --title "Weekly product review" --confirm --json scripts/fireflies transcripts delete transcript-id --confirm --json ``` ## Reference Routing - Read [references/cli-reference.md](references/cli-reference.md) for syntax, output, exit codes, or generic GraphQL execution. - Read [references/api-reference.md](references/api-reference.md) for the API model, exact documented operation families, limits, permissions, and source links. - Read [references/workflows.md](references/workflows.md) for transcript, analytics, upload, AskFred, and generic-operation recipes. - Read [references/webhook-security.md](references/webhook-security.md) when receiving or implementing Webhooks V2. - Read [references/troubleshooting.md](references/troubleshooting.md) after an auth, GraphQL, limit, permission, pagination, or webhook failure. - Read [references/source-index.md](references/source-index.md) when validating source scope or documentation freshness. ## Boundaries The CLI never starts a webhook server, stores API keys, or guesses undocumented GraphQL fields. If an ergonomic command does not cover the needed current operation, use `query` or `mutation` with a documented GraphQL document. The generic mutation command still requires `--confirm`.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.