← Cycles
Market Analyst · c6

sagetap-integration

archivedcreated 2026-02-25· last activity 2026-03-23

Integrate Sagetap call transcripts into the extraction pipeline alongside Avoma.

Why

Sagetap is a second source of prospect evaluation calls. These are structured buyer-vendor sessions facilitated through the Sagetap platform — a distinct channel from Avoma's recorded meetings. Two sample transcripts are already in data/raw-sagetap/ and contain real prospect signals (game dev company evaluating discovery tooling, cybersecurity architect at global logistics enterprise looking for Linux visibility/SIEM integration).

However, these transcripts are much rawer than Avoma:

  • CSV format with generic speaker labels (Speaker 1, Speaker 2) — no names
  • Zero metadata (no date, company, attendees, subject)
  • Poor transcription quality ("Centimeters" for SIEM, "Keypoint" for QPoint)
  • Speaker numbering inconsistent across files

Objectives

Phase 1: Multi-source pipeline (make pipeline source-aware) — DONE

  • Add MeetingSource type to schema ('avoma' | 'sagetap')
  • Add source? to CallMetadata, source to CallIndexEntry
  • Detect Sagetap uploads in avoma.ts via purpose.label / type.label
  • Remove sagetap from VENDOR_PATTERNS, add early return in filter
  • Build sidecar attendees from transcription.speakers (not meeting.attendees) for uploads
  • Parse structured subject into SagetapSubjectHints (contactName, role, industry, companySize, location)
  • Source-aware metadata injection in extract.ts and batch.ts (date shows "Unknown" for uploads)
  • Skip date backfill for sagetap in extract.ts, batch.ts, build-index.ts
  • Propagate source through all pipeline stages to index

Phase 2: Test end-to-end — DONE

  • Fetch RobertK call via npm run fetch -- --external
  • Verify sidecar: source: "sagetap", correct attendees, parsed hints
  • Extract: metadata context shows Sagetap hints, date left as null
  • Processed JSON: metadata.source: "sagetap", attendees from transcript
  • Index: call appears with source: "sagetap" (19 calls total)

Phase 3: Dashboard source indicators — DONE

  • Add source column to calls list (AV grey / ST sky blue pills)
  • Add source badge to call detail header
  • Add source to useCallData.ts CallMetadata interface
  • Backfill all 22 existing processed calls with source: "avoma"

Phase 4: Automated upload script — DONE

  • pipeline/upload-sagetap.ts — npm run upload:sagetap automates video upload to Avoma via POST /v1/calls/
  • Parses structured filename for metadata (reuses parseSagetapSubject() logic)
  • Local HTTP server + localtunnel for file access (zero config, no account)
  • Subject prefixed with Sagetap: for pipeline detection
  • isSagetapUpload() updated to also match subject.startsWith('Sagetap: ')
  • parseSagetapSubject() strips Sagetap: prefix before parsing
  • --dry-run mode for testing, --rep / --date flags, directory batch support
  • SAGETAP_REP_EMAIL env var for default rep email

Phase 5: Remaining work

  • Process second Sagetap call (Cliff — cybersecurity architect, logistics enterprise)
  • Fix location double-space from subject parsing ("Location Poland" → "Poland")
  • Handle company.name: null gracefully in dashboard (Sagetap anonymity)
  • Consider: backfill actual call dates from Sagetap platform manually
  • Consider: transcription correction dictionary for known errors in extraction prompt
  • Consider: replace localtunnel with S3 presigned URLs (--s3 flag)

Current State Assessment

Pipeline is Avoma-centric. Key coupling points:

  • extract.ts / batch.ts inject metadata header as "from Avoma"
  • .meta.json sidecar assumes Avoma-shaped fields (avomaId, fetchedAt)
  • CallMetadata schema has avomaId but no generic source field
  • No source attribution in aggregated index or dashboard
  • Extraction prompt itself (extract-call.md) is source-agnostic — good news

Sagetap has no public API. Help center and vendor portal docs show no API, export, webhook, or developer integration features. Metadata visible on the Sagetap platform page (dates, participant info) is only accessible through the UI. Options: manual metadata entry, or ask Sagetap support about programmatic access.

Approach Decision: Avoma-as-middleware (not CSV preprocessing)

Rather than building a separate CSV preprocessor, Sagetap calls are uploaded as video recordings to Avoma, which handles transcription and speaker assignment. The pipeline then fetches them via the existing Avoma API with source-aware routing.

Why this approach won:

  • Avoma transcription + speaker assignment is already a solved problem
  • No need for CSV parsing, speaker heuristics, or correction dictionaries
  • Metadata encoded in structured subject line, parsed automatically
  • Unified raw directory (data/raw/) — no separate data/raw-sagetap/ needed for pipeline
  • Keeps the pipeline simple: one fetch path, source-aware branching

Manual steps required per Sagetap call:

  1. Download video from Sagetap platform
  2. Upload to Avoma → set purpose/type to "sagetap"
  3. Set structured subject: {Name} - {Role}, Industry: {X}, Company Size: {X}, HQ: {Location}
  4. Manually assign speakers in Avoma (names + is_rep flags)

Key Decisions & Learnings

Filter routing

  • isSagetapUpload() checks meeting.purpose?.label, meeting.type?.label for "sagetap", and subject.startsWith('Sagetap: ') (added in Phase 4)
  • Early return in isExternalProspectCall() bypasses attendee count check (uploads only have uploader in meeting.attendees)

Sidecar attendees

  • Avoma uploads only populate meeting.attendees with the uploader
  • After manual speaker assignment, transcription.speakers has the correct participants with name, email, is_rep
  • The transcription object is already fetched before sidecar construction, so speakers are available

Date handling

  • Upload timestamp ≠ actual call date; pipeline marks date as "Unknown" in Claude prompt context
  • Date backfill skipped in all three paths (extract, batch, build-index) when source === 'sagetap'
  • Claude extracted date: null as expected — validation warns but doesn't fail

Subject parsing

  • Format: "Name - Role, Industry: X, Company Size: Y, HQ: Location Z"
  • Regex parser splits on - then comma-separated key-value pairs
  • Double-space in "HQ: Location Poland" is a data entry issue, not a parser bug

Extraction quality

  • Claude correctly identified RobertK as cybersecurity persona, used industry hint
  • Interest 3/5 seems right for an early evaluation call
  • Company name extracted as null (anonymous) — correct behavior for Sagetap
  • Avoma transcription quality is good enough without preprocessing corrections

Open Questions

  1. Sagetap metadata access — Resolved: manual upload to Avoma is the workflow
  2. Transcript quality floor — Resolved: Avoma transcription is good enough
  3. Volume — Resolved: upload-sagetap.ts automates upload (supports directory batch). Manual step reduced to speaker assignment only.
  4. Speaker mapping confidence — Resolved: manual assignment in Avoma solves this
  5. Actual call dates — Can we get the real dates from the Sagetap platform for backfill?

Dashboard SSR gotcha

  • v-if conditions that depend on data not present in the server-rendered HTML can silently fail — the span never appears after hydration
  • Fix: use unconditional rendering with :class binding instead of v-if for source pills
  • useCallData.ts has its own interface copies — new schema fields must be added there too

Files Changed

  • schemas/call.ts — MeetingSource type, source on CallMetadata + CallIndexEntry
  • pipeline/avoma.ts — SagetapSubjectHints, isSagetapUpload(), parseSagetapSubject(), filter bypass, speaker-based sidecar, subject prefix detection (Phase 4)
  • pipeline/upload-sagetap.ts — New (Phase 4) — automated Sagetap video upload to Avoma via API
  • pipeline/extract.ts — expanded sidecar type, source-aware metadata context, source propagation, date backfill guard
  • pipeline/batch.ts — same source-aware metadata context and backfill guards
  • pipeline/build-index.ts — date backfill guard, source in index entry
  • site/composables/useCallData.ts — added source to CallMetadata interface
  • site/pages/calls/index.vue — source column (AV/ST pills)
  • site/pages/calls/[id].vue — source badge in header

Notes

  • Sagetap sessions are anonymized buyer evaluations — the prospect came specifically to evaluate Qpoint. This is a higher-intent signal than cold outbound calls.
  • The two sample CSV transcripts in data/raw-sagetap/ are kept for reference only — the actual pipeline path is through Avoma uploads.
  • Sagetap's own vendor page mentions "structured buyer feedback on relevance, clarity, timing, and trust" — worth checking if that structured feedback is available alongside transcripts.

Qpoint Brand Style Guide