Back to Meeting & Decision Documentation

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Meeting and Decision Documentation

[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]

This document gives the FullSpec build team everything needed to construct, wire, and verify the Meeting and Decision Documentation automation. It covers the current manual process in full, the specification for each of the three agents, the end-to-end data flow between them, and the recommended build stack with total effort hours. The owner and their team are not expected to action anything in this document; it is a technical reference for the people building and configuring the system.

01Process snapshot

Step
Name
Description
1
Locate Meeting Recording or Raw Notes
The Operations Manager retrieves the Zoom cloud recording or locates handwritten notes. If recording access is restricted, a manual request to the host is required. Time cost: 10 min.
2 BOTTLENECK
Listen Back or Review Raw Notes
The Operations Manager replays part or all of the recording, or reads rough notes, to reconstruct discussion and decisions. This is where the majority of per-meeting time is consumed. Time cost: 35 min.
3
Write Summary in a Document
A freeform summary is typed into Notion covering agenda items, discussion points, and outcomes. Format varies by person and meeting type. Time cost: 20 min.
4 BOTTLENECK
Extract Action Items Manually
The writer re-reads the summary to identify tasks, then assigns owner names and rough due dates based on recall. High inconsistency and omission rate. Time cost: 15 min.
5
Create Tasks in Project Tool
Action items are entered into ClickUp one by one, with assignees and due dates set manually. Time cost: 15 min.
6
Draft Follow-Up Email
A summary email is written from scratch in Gmail, reproducing key points and actions copied from the document. Time cost: 12 min.
7
Send Email to Attendees
The email is addressed to all attendees pulled manually from the calendar invite or a contact list, then sent. Time cost: 5 min.
8
Post Summary to Slack Channel
A shortened version of the summary is pasted into the relevant Slack channel for team members not present. Time cost: 8 min.
9
File Document in Notion
The finished summary is moved or linked into the correct Notion section, tagged with date, project, and attendees for searchability. Time cost: 8 min.
Time cost summary: Total manual time per meeting cycle is 128 minutes (approx. 2 hours 8 minutes). At 20 meetings per month and a typical run rate of 5 to 6 meetings per week, this equates to approximately 6 hours of manual effort every week and 300 hours per year. The automation replaces steps 1, 2, 3, 4, 5, 6, 7, 8, and 9 entirely, reducing owner involvement to a single optional review checkpoint of under 5 minutes before distribution.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Transcription Agent

Connects to the Zoom API to retrieve the cloud recording for a completed meeting session. Converts the audio file to a full text transcript with speaker labels and timestamps. Designed to handle variable audio quality and meeting lengths up to 90 minutes. Output is a clean, structured transcript string passed directly to the Summary and Action Extraction Agent. Estimated build time: 6 hours. Complexity: Moderate.

Trigger
Zoom meeting session end event detected by the orchestration layer via Zoom webhook (event type: recording.completed).
Tools
Zoom (cloud recording retrieval, recording download URL, meeting metadata)
Replaces steps
Steps 1 and 2: Locate Recording / Raw Notes + Listen Back or Review Raw Notes
Estimated build
6 hours | Moderate
// Input
zoom_webhook_payload: {
  event: 'recording.completed',
  payload.object.uuid: string,          // Zoom meeting UUID
  payload.object.host_id: string,
  payload.object.topic: string,         // Meeting title
  payload.object.start_time: ISO8601,
  payload.object.duration: integer,     // Minutes
  payload.object.recording_files: [
    { file_type: 'MP4'|'M4A', download_url: string, status: 'completed' }
  ]
}

// Output
transcript_payload: {
  meeting_uuid: string,
  meeting_title: string,
  meeting_start_time: ISO8601,
  meeting_duration_minutes: integer,
  host_id: string,
  transcript_text: string,              // Full timestamped speaker-labelled text
  speaker_segments: [
    { speaker: string, timestamp: string, text: string }
  ],
  transcript_status: 'success'|'partial'|'failed',
  error_reason: string|null
}
  • Zoom API access requires OAuth 2.0 app credentials with scopes: recording:read:admin and meeting:read:admin. Server-to-Server OAuth (account-level) is preferred over user-level OAuth to avoid re-auth on token expiry.
  • Zoom Cloud Recording must be enabled on the account. Some Zoom plans (Basic free tier) do not support cloud recording. Confirm the account tier before build starts.
  • The webhook endpoint must be registered in the Zoom Marketplace app config and must respond with HTTP 200 within 3 seconds to avoid retry storms. Use a queue-backed receiver.
  • Audio transcription should use the Zoom-provided VTT transcript file where available (file_type: 'TRANSCRIPT') before falling back to audio processing. If the VTT file is absent, the audio file (M4A preferred over MP4) is fetched and passed to the transcription service.
  • Meetings longer than 90 minutes must be chunked into segments before transcription to avoid timeout errors. Implement a chunk size of 20 minutes with overlap of 30 seconds.
  • If transcript_status returns 'failed', the agent must write an error record to the logging table and send a Slack alert to the designated ops channel. Do not pass a failed transcript downstream.
  • Dedupe: store the meeting_uuid in the logging table on first receipt. If the same UUID arrives again (Zoom retries), discard and return 200 without re-processing.
  • Confirm the Zoom account's data residency region before build. Recording download URLs include a signed token with a 24-hour expiry; the agent must fetch and process immediately.
Summary and Action Extraction Agent

Receives the transcript payload from the Transcription Agent and uses a structured prompt to produce a formatted meeting summary covering key discussion points, decisions reached, and action items with proposed owners and due dates. Applies a consistent output schema every time regardless of meeting type or facilitator. Writes the summary to a new Notion database page and creates one ClickUp task per action item. Estimated build time: 9 hours. Complexity: Complex.

Trigger
Transcript payload received and transcript_status is 'success' or 'partial'. A 'partial' transcript proceeds with a warning flag appended to the Notion page.
Tools
Notion (page creation in target database), ClickUp (task creation per action item)
Replaces steps
Steps 3, 4, 5, and 9: Write Summary + Extract Action Items + Create Tasks in ClickUp + File Document in Notion
Estimated build
9 hours | Complex
// Input
transcript_payload: {
  meeting_uuid: string,
  meeting_title: string,
  meeting_start_time: ISO8601,
  meeting_duration_minutes: integer,
  transcript_text: string,
  speaker_segments: [ { speaker, timestamp, text } ],
  transcript_status: 'success'|'partial'
}

// Output (after LLM extraction)
structured_summary: {
  meeting_title: string,
  meeting_date: ISO8601,
  attendees_inferred: string[],          // Speaker labels from transcript
  key_discussion_points: string[],
  decisions_reached: string[],
  action_items: [
    {
      action_text: string,
      proposed_owner: string,
      proposed_due_date: ISO8601|null,
      clickup_task_id: string            // Populated after ClickUp write
    }
  ],
  notion_page_id: string,               // Populated after Notion write
  notion_page_url: string,
  partial_transcript_flag: boolean
}

// On approval (passed to Distribution Agent)
distribution_payload: {
  meeting_uuid: string,
  meeting_title: string,
  meeting_date: ISO8601,
  notion_page_url: string,
  summary_text: string,                 // Human-readable paragraph form
  action_items: [ { action_text, proposed_owner, proposed_due_date } ],
  approval_status: 'approved'|'edited_and_approved',
  approved_by: string,
  approved_at: ISO8601
}
  • Notion integration requires a Notion internal integration token with access granted to the target database. The database ID must be stored as an environment variable (NOTION_DB_MEETING_SUMMARIES). The page schema must include properties: Meeting Title (title), Date (date), Attendees (multi-select or rich text), Status (select: Draft / Approved / Sent), Notion Page URL (url), ClickUp Link (url).
  • ClickUp API requires a personal API token or OAuth app with tasks:write scope. The target List ID must be stored as an environment variable (CLICKUP_LIST_ID_ACTIONS). Task creation must include: name (action_text), assignee (resolved from proposed_owner to ClickUp member ID via /team/{team_id}/member), due date (proposed_due_date), and a custom field 'Source Meeting' linked to the meeting_uuid.
  • Owner name to ClickUp member ID resolution: maintain a mapping table (JSON or Supabase row) of display name to ClickUp user ID. If a proposed_owner cannot be matched, create the task with no assignee and append '[UNASSIGNED]' to the task name. Do not fail the step.
  • The LLM prompt must enforce a strict JSON output schema. Use a system prompt that instructs the model to return only valid JSON matching the structured_summary shape. Validate the response with a JSON schema check before writing to Notion or ClickUp.
  • If the LLM returns malformed JSON on the first attempt, retry once with a simplified prompt requesting only the action_items array. Log a warning if the retry is used.
  • Partial transcript flag: if transcript_status was 'partial', prepend a banner to the Notion page body: 'NOTE: This summary was generated from a partial transcript. Manual review is recommended.'
  • The Notion page must be created in 'Draft' status. Status is updated to 'Approved' only after the ops manager approves via the review gate. Confirm the Notion database structure (property names and types) with the owner before build.
  • Dedupe: check the logging table for meeting_uuid before writing to Notion. If a page already exists for that UUID, update it rather than creating a duplicate.
Distribution Agent

Receives the approved distribution payload and handles all outbound communication. Retrieves the full attendee list from the Google Calendar event linked to the meeting UUID, composes and sends a formatted follow-up email via Gmail to all attendees, and posts a condensed summary to the designated Slack channel. Operates only after the human review gate returns an 'approved' or 'edited_and_approved' status. Estimated build time: 5 hours. Complexity: Moderate.

Trigger
Distribution payload received with approval_status set to 'approved' or 'edited_and_approved' by the Ops Manager review step.
Tools
Google Calendar (attendee list retrieval), Gmail (follow-up email send), Slack (team channel post)
Replaces steps
Steps 6, 7, and 8: Draft Follow-Up Email + Send Email to Attendees + Post Summary to Slack Channel
Estimated build
5 hours | Moderate
// Input
distribution_payload: {
  meeting_uuid: string,
  meeting_title: string,
  meeting_date: ISO8601,
  notion_page_url: string,
  summary_text: string,
  action_items: [ { action_text, proposed_owner, proposed_due_date } ],
  approval_status: 'approved'|'edited_and_approved',
  approved_by: string,
  approved_at: ISO8601
}

// Output
distribution_result: {
  email_sent: boolean,
  email_recipients: string[],           // Resolved from Google Calendar event
  gmail_message_id: string,
  slack_post_ts: string,               // Slack message timestamp (thread ID)
  slack_channel_id: string,
  notion_status_updated_to: 'Sent',
  distribution_completed_at: ISO8601,
  errors: string[]|null
}
  • Google Calendar API requires OAuth 2.0 with scope: https://www.googleapis.com/auth/calendar.readonly. The meeting UUID must be cross-referenced to a Google Calendar event ID. Store the mapping in the logging table at the point the Zoom webhook is received (Zoom provides the calendar event ID in the webhook payload if Calendar integration is active on the Zoom account). Confirm this integration is enabled before build.
  • If the Google Calendar event ID is not available in the Zoom payload, fall back to matching on meeting start time plus host email against Calendar events in a 10-minute window. Log a warning if the fallback is used.
  • Gmail API requires OAuth 2.0 with scope: https://www.googleapis.com/auth/gmail.send. Use a service account with domain-wide delegation if the sender must appear as the Operations Manager's address rather than a generic sender. Confirm the preferred sender identity before build.
  • The Gmail email body must be rendered from an HTML template. Template variables: {{meeting_title}}, {{meeting_date}}, {{summary_text}}, {{action_items_list}} (rendered as an HTML ordered list), {{notion_page_url}}. Store the template as an environment variable or Supabase record for easy editing without a code deploy.
  • Slack API requires a bot token (xoxb-) with scopes: chat:write and channels:read. The target channel ID must be stored as an environment variable (SLACK_CHANNEL_MEETING_SUMMARIES). The Slack message must use Block Kit with a header block (meeting title), a section block (condensed 3-sentence summary), and a bullet list of action items with owners. Include a button linking to the Notion page.
  • Rate limits: Gmail API allows 250 quota units per second per user; a single send is 100 units. Slack Web API allows 1 message per second on chat.postMessage. Both are well within the expected volume of 20 meetings per month.
  • If email send fails, retry once after 60 seconds. If the retry also fails, log the error, update the Notion page status to 'Email Failed', and post an alert to the ops Slack channel. Do not silently swallow errors.
  • The Slack channel and Notion database mapping must be confirmed per meeting type before go-live. Initial build uses a single default channel (SLACK_CHANNEL_MEETING_SUMMARIES) and a single Notion database. Routing rules can be extended post-launch.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with agent handoffs and field names
// ─────────────────────────────────────────────────────────────────
// TRIGGER: Zoom meeting session ends
// ─────────────────────────────────────────────────────────────────
ZOOM_WEBHOOK_EVENT: recording.completed
  -> meeting_uuid         : string   (Zoom payload.object.uuid)
  -> meeting_title        : string   (Zoom payload.object.topic)
  -> meeting_start_time   : ISO8601  (Zoom payload.object.start_time)
  -> meeting_duration     : integer  (Zoom payload.object.duration, minutes)
  -> host_id              : string   (Zoom payload.object.host_id)
  -> calendar_event_id    : string   (Zoom payload.object.calendar_event_id, if present)
  -> recording_files[]    : array    (file_type, download_url, status)

  // Dedupe check: meeting_uuid written to log table
  // If UUID already exists: discard, return HTTP 200

// ─────────────────────────────────────────────────────────────────
// AGENT 1: Transcription Agent
// ─────────────────────────────────────────────────────────────────
INPUT:
  recording_url           : string   (signed download URL, expiry 24h)
  preferred_file_type     : 'TRANSCRIPT'|'M4A'|'MP4'  (priority order)

PROCESSING:
  1. Fetch VTT/TRANSCRIPT file if available
  2. If absent: fetch M4A audio -> send to transcription service in 20-min chunks
  3. Merge chunks with 30-second overlap deduplication
  4. Parse speaker labels and timestamps into speaker_segments[]

OUTPUT -> transcript_payload:
  meeting_uuid            : string
  meeting_title           : string
  meeting_start_time      : ISO8601
  meeting_duration_minutes: integer
  host_id                 : string
  transcript_text         : string   (full merged text)
  speaker_segments[]      : [ { speaker, timestamp, text } ]
  transcript_status       : 'success'|'partial'|'failed'
  error_reason            : string|null

  // On 'failed': write error to log table, alert Slack ops channel, STOP
  // On 'partial': continue with partial_transcript_flag = true

// ─────────────────────────────────────────────────────────────────
// HANDOFF 1: transcript_payload -> Summary and Action Extraction Agent
// ─────────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────────
// AGENT 2: Summary and Action Extraction Agent
// ─────────────────────────────────────────────────────────────────
INPUT:
  transcript_text         : string
  speaker_segments[]      : [ { speaker, timestamp, text } ]
  transcript_status       : 'success'|'partial'
  meeting_title           : string
  meeting_start_time      : ISO8601

PROCESSING:
  1. Send transcript_text to LLM with structured JSON output prompt
  2. Validate LLM response against structured_summary JSON schema
  3. If validation fails: retry with simplified prompt, log warning
  4. Write Notion page to NOTION_DB_MEETING_SUMMARIES (status: 'Draft')
     -> notion_page_id, notion_page_url returned
  5. For each action_items[n]:
     a. Resolve proposed_owner -> ClickUp member ID via name mapping table
     b. POST task to CLICKUP_LIST_ID_ACTIONS
     c. Store clickup_task_id on action_items[n]
  6. Update Notion page with ClickUp task URLs
  7. If partial_transcript_flag: prepend warning banner to Notion page body

OUTPUT -> structured_summary (stored, awaiting approval):
  meeting_title           : string
  meeting_date            : ISO8601
  attendees_inferred[]    : string[]
  key_discussion_points[] : string[]
  decisions_reached[]     : string[]
  action_items[]          : [ { action_text, proposed_owner,
                               proposed_due_date, clickup_task_id } ]
  notion_page_id          : string
  notion_page_url         : string
  partial_transcript_flag : boolean

// ─────────────────────────────────────────────────────────────────
// REVIEW GATE: Human approval step (Ops Manager)
// ─────────────────────────────────────────────────────────────────
  Ops Manager receives draft notification (Slack DM or email)
  Reviews Notion page draft
  Action: Approve | Edit then Approve
  approval_status         : 'approved'|'edited_and_approved'
  approved_by             : string
  approved_at             : ISO8601
  Notion page status updated to 'Approved'

// ─────────────────────────────────────────────────────────────────
// HANDOFF 2: distribution_payload -> Distribution Agent
// ─────────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────────
// AGENT 3: Distribution Agent
// ─────────────────────────────────────────────────────────────────
INPUT:
  meeting_uuid            : string
  meeting_title           : string
  meeting_date            : ISO8601
  notion_page_url         : string
  summary_text            : string
  action_items[]          : [ { action_text, proposed_owner, proposed_due_date } ]
  calendar_event_id       : string   (from log table, set at trigger)

PROCESSING:
  1. Fetch Google Calendar event by calendar_event_id
     -> attendee_emails[] : string[] (from event.attendees[].email)
  2. If calendar_event_id absent: match by start_time +/- 10 min + host email
  3. Render Gmail HTML template with meeting_title, meeting_date,
     summary_text, action_items_list (HTML ol), notion_page_url
  4. Send email via Gmail API to attendee_emails[]
     -> gmail_message_id returned
  5. Compose Slack Block Kit message:
     header_block: meeting_title
     section_block: summary_text (condensed, max 3 sentences)
     bullet_list: action_items[] with proposed_owner
     button: 'View Full Notes' -> notion_page_url
  6. POST to SLACK_CHANNEL_MEETING_SUMMARIES
     -> slack_post_ts, slack_channel_id returned
  7. Update Notion page status to 'Sent'

OUTPUT -> distribution_result:
  email_sent              : boolean
  email_recipients[]      : string[]
  gmail_message_id        : string
  slack_post_ts           : string
  slack_channel_id        : string
  notion_status_updated_to: 'Sent'
  distribution_completed_at: ISO8601
  errors[]                : string[]|null

  // On email failure: retry once after 60s
  // On second failure: log error, set Notion status 'Email Failed',
  //                    alert Slack ops channel
// ─────────────────────────────────────────────────────────────────
// END: All records written to Supabase log table for monitoring
// ─────────────────────────────────────────────────────────────────
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool with one workflow per agent (three workflows total): Transcription Workflow, Summary and Action Extraction Workflow, Distribution Workflow. All three workflows share a single credential store for Zoom, Notion, ClickUp, Google Calendar, Gmail, and Slack. Each workflow is triggered by an internal queue event rather than chained directly, so failures in one agent do not block the others from receiving their next run.
Webhook configuration
A public HTTPS endpoint is registered as the Zoom webhook receiver (event: recording.completed). The endpoint must respond HTTP 200 within 3 seconds. Webhook secret verification (ZOOM_WEBHOOK_SECRET_TOKEN environment variable) must be implemented to validate the x-zm-signature header on every inbound request. The endpoint writes the raw payload to a Supabase queue table before returning 200, so processing happens asynchronously and Zoom never times out.
Templating approach
Gmail email body: HTML template stored as a Supabase record (table: email_templates, row: meeting_summary_v1). Template engine resolves {{meeting_title}}, {{meeting_date}}, {{summary_text}}, {{action_items_list}}, and {{notion_page_url}} at send time. Slack Block Kit message: constructed dynamically in the Distribution Workflow using a reusable block-builder function. LLM system prompt: stored as a Supabase record (table: prompt_templates, row: meeting_summary_extraction_v1) so it can be tuned without a code deploy.
Error logging
All agent runs write a record to a Supabase table (automation_run_log) with fields: run_id (uuid), meeting_uuid, agent_name, status ('success'|'partial'|'failed'), error_message, created_at. On any 'failed' status, the orchestration layer triggers an alert to a designated Slack ops channel (SLACK_CHANNEL_OPS_ALERTS) with the run_id, agent name, and error message. A Supabase dashboard view surfaces the last 30 days of run logs for monitoring.
Testing approach
All three agents are built and tested against sandbox credentials first: Zoom sandbox account with test recordings, a separate Notion database (NOTION_DB_MEETING_SUMMARIES_TEST), a ClickUp test list (CLICKUP_LIST_ID_ACTIONS_TEST), and a Slack test channel. Production credentials are introduced only after sandbox end-to-end passes. Live testing is conducted across a minimum of three real meetings before go-live, covering short meetings (under 20 min), standard meetings (30 to 60 min), and a meeting with multiple speakers.
Estimated total build time
Transcription Agent: 6 hours. Summary and Action Extraction Agent: 9 hours. Distribution Agent: 5 hours. End-to-end integration testing and environment setup: 2 hours. Total: 22 hours.
Environment variables required before build starts: ZOOM_OAUTH_CLIENT_ID, ZOOM_OAUTH_CLIENT_SECRET, ZOOM_WEBHOOK_SECRET_TOKEN, NOTION_INTEGRATION_TOKEN, NOTION_DB_MEETING_SUMMARIES, CLICKUP_API_TOKEN, CLICKUP_LIST_ID_ACTIONS, CLICKUP_TEAM_ID, GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GMAIL_SENDER_ADDRESS, SLACK_BOT_TOKEN, SLACK_CHANNEL_MEETING_SUMMARIES, SLACK_CHANNEL_OPS_ALERTS, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY. All variables must be stored in the orchestration platform's shared credential store. None should appear in code or workflow definitions.
Contact the FullSpec team at support@gofullspec.com for any questions on this handover pack, credential setup, or build sequencing. Do not begin production wiring until sandbox testing for each agent is signed off.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Meeting & Decision Documentation.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View