ClientsFlow Pipeline · Owner deliverable · 2026-06-24
Fireflies HYBRID „smart relay" · EBO + implementation + provider rec · merged to main, deployed v102
One line, then the deciding factors for THIS codebase.
| Recommendation | Why (for this repo) |
|---|---|
| Fireflies HYBRID (not Fathom Business) | Repo is 100% Fireflies-wired; native Hungarian AI summaries; client_reference_id
deal-id round-trip already passed by ingest_call_audio; CI-able uploadAudio
test seam. Fathom Business has no HU summaries (6 non-HU langs), no external-id
passthrough, and no upload seam → a full client/handler/HMAC/desktop rewrite for a strictly worse fit.
Fathom only wins on bot-free >3 coverage, which the bot relay handles. |
Capture → webhook → match → store → AI → surface. The smart relay chooses SDK (≤3) vs bot (>3).
route_capture() + handle_participant_joined(store=);
bot dispatch single-flighted via put_if_absent (NOT set(ttl=)).clientReferenceId read FIRST, then the email→CRM→meeting_id chain.| ID | Scenario | Verify layer | Behavior |
|---|---|---|---|
| S1 | ≤3 call → bot-free Meet SDK capture (common case) | 🟡 RESIDUAL (capture) / 🟢 SEAM (rest) | Bot-free SDK records, webhook → match → store → HU summary → board pill → Notion row. |
| S2 | >3 call → Notetaker BOT auto-invited (the relay) | 🟢 BUILD/SEAM / 🟡 RESIDUAL (join) | route_capture → 'bot' → addToLiveMeeting once (single-flight). LIVE-verified: success:true. |
| S3 | No-bot audio upload (record-anything → public URL) | 🟢 LIVE/SEAM | ingest_call_audio → uploadAudio with client_reference_id = deal_id. Already wired. |
| S4 | Manual paste (bridge for any transcriber) | 🟢 LIVE + BUILD (Notion row) | process_pasted_transcript → extract + HU summary + NEW Notion calls-row (Source=Upload). |
| S5 | Idempotent webhook (duplicate delivery) | 🟢 SEAM | Second delivery is a no-op; Notion row deduped on Transcript ID (query-before-create). |
| S6 | HU AI summary as CRM NOTES only (human-gate) | 🟢 LIVE/BUILD | Summary + items are notes; nothing auto-sent to the lead, no auto-task. Test asserts it. |
| S7 | Board card pill reflects state | 🟢 LIVE | 📞 Sales call megvolt · N perc + Transcript: betöltés…/megvan/hiányzik. Pre-built. |
| S8 | Notion calls-DB row complete + Lead-linked | 🟢 BUILD | Name/Lead→CRM/Recording/Date/Duration/Source/Transcript ID/status/AI summary + markdown body. |
| ID | Scenario | Verify layer | Behavior |
|---|---|---|---|
| N1 | No CRM match, real external attendee | 🟢 LIVE/SEAM | ffseen marked + Slack #ai-new-lead flag; no silent drop, no auto-link. |
| N2 | Webhook before transcript ready (early) | 🟢 LIVE/SEAM | get_transcript raises → not marked done → 10-min backup poll retries. |
| N3 | Transcription never arrives (missing) | 🟢 LIVE/BUILD | Pill stays 'hiányzik'; Notion status not flipped to done; nothing fabricated. |
| N4 | >3 escalation when SDK would truncate | 🟢 BUILD/SEAM / 🟡 RESIDUAL | 4th joiner re-fires the event → route 'bot', single-flight; ≤3 SDK is the floor. |
| N5 | Fireflies API/key failure | 🟢 LIVE/SEAM | Structured {ok:false,error}; no unhandled raise; backup poll + paste remain. |
| N6 | Duplicate bot dispatch / rate limit | 🟢 BUILD/SEAM | put_if_absent('meet_bot_sent:{url}') → exactly one dispatch. LIVE single-flight verified. |
| N7 | Consent: NO consent UX (Q5) | 🟢 DESIGN | Zero consent code added — explicit non-requirement (spoken consent verbally). |
| N8 | Account-level Fireflies webhook missing | 🟢 LIVE | Per-upload webhook + 10-min poll cover it; owner registers account webhook (checklist). |
| N9 | SA lacks Meet/Workspace-Events scopes | 🟡 DESIGN/RESIDUAL | SDK + participant.joined impossible until owner adds scopes; build FAILS CLOSED, no crash. |
| Change | File | EBO |
|---|---|---|
route_capture() smart relay + handle_participant_joined(store=) single-flight | app/transcription.py | S1/S2/N4/N6 |
handle_meet_participant_joined() + POST /meet/participant-joined (dormant) | app/flows_calls.py · app/modal_app.py | S1/S2/N9 |
clientReferenceId-first deal match | app/flows_inbound.py | §4 #1 / S3 |
write_call_record() idempotent Notion calls-DB writer, wired into both handlers | app/flows_calls.py | S8/S5/N3 |
get_transcript requests transcript_url/audio_url | app/clients/fireflies.py | S8 (Recording) |
NOTION_DB['call_recordings'] wired to the live DB | app/config.py | S8 |
Landmine fix: store().set(ttl=) → put_if_absent | TRANSCRIPTION_WIRING.md | S5/N6 |
Live bug fixed: AddToLiveMeeting selection = success only | app/transcription.py | S2 (live) |
TDD: +27 tests (test_capture_router, test_call_record_notion, test_meet_participant_route) red→green; legacy test_transcription_s6 updated to the HYBRID contract. Suite 558 green.
| Now (live on v102) | Blocked on an owner account action |
|---|---|
Dash + full funnel render (200, badge c88ec77-dirty+v102) | ≤3 bot-free SDK capture — Workspace admin approval + Fireflies SDK plan |
| Upload route (S3) + manual paste (S4) → Notion calls row | Account-level Fireflies webhook (so bot/SDK transcripts fire) |
/meet/participant-joined: ≤3→sdk, >3→bot dispatch (success:true) | Workspace-Events participant.v2.joined subscription (for live trigger) |
| Duplicate-webhook idempotency + Slack no-match flag | One real ≤3 + one real >3 golden-journey Meet (after SDK approval) |
| # | Action | Unlocks |
|---|---|---|
| 1 | Confirm FIREFLIES_API_KEY valid (Pro for the floor; Business for the bot-free SDK) | All routes |
| 2 | Register the account-level Fireflies webhook → <PUBLIC_BASE_URL>/fireflies/transcription + set the x-hub-signature shared secret | Bot/SDK transcripts fire (N8) |
| 3 | Confirm PUBLIC_BASE_URL in the Modal env (matyas--clientsflow-pipeline-web.modal.run) | Per-upload webhook + form links |
| 4 | Google Workspace admin approval for the Fireflies Meet SDK + enable the integration | ≤3 bot-free capture (S1) |
| 5 | (For the live >3 trigger) Workspace-Events sub for meet.participant.v2.joined → /meet/participant-joined (Meet scopes on DWD + Pub/Sub topic + publisher role + renewal cron). Lighter: Fireflies calendar auto-join with „wait for ≥2". | Automatic >3 relay (S2) |
| 6 | Sign the Fireflies DPA (GDPR; EU Private Storage = Enterprise). Consent stays verbal — no code. | Compliance |
EBO: plans/fireflies-ebo/EBO.md · Implementation: plans/fireflies-ebo/IMPLEMENTATION.md
· Wiring + landmine fix: TRANSCRIPTION_WIRING.md · Notion calls DB:
„Call Recordings & Transcripts" (92cae219-a975-4a34-ae2e-a8981982a22c) ·
Live dash: https://matyas--clientsflow-pipeline-web.modal.run/dash · git: merged to local main, NOT pushed.