Back to Event & Webinar Promotion

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

Event & Webinar Promotion Automation

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

This document gives the FullSpec build team everything needed to construct, connect, and deploy the three-agent Event and Webinar Promotion automation. It covers the current-state step map with bottlenecks identified, full agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. All implementation decisions, tool tiers, and field names referenced here are the confirmed facts from the discovery and mapping phase. The process owner's responsibilities are limited to approving social post copy at the human review step and confirming event brief rows in Google Sheets; everything else is handled by the automation.

01Process snapshot

Step
Name
Description
1
Confirm Event Details and Brief
Coordinator collects event title, date, time, speaker details, and key messaging into a shared Google Sheet used as the event brief. Time cost: 30 min.
2
Build the Registration Landing Page
Team member manually sets up the Zoom webinar registration form, copies the link, and updates or requests a landing page. Often delays the first announcement by several days. Time cost: 45 min.
3
Draft Announcement Email
Coordinator writes a promotional email in HubSpot, applies the brand template, adds the registration link, and schedules it to the relevant contact list. Time cost: 40 min.
4
Create Social Promotion Graphics
Coordinator or designer builds event graphics in Canva for LinkedIn and other channels; each graphic is downloaded and uploaded manually. Time cost: 35 min.
5
Schedule Social Posts
Coordinator manually writes LinkedIn post copy for announcement, mid-cycle reminder, and day-of post, then schedules each one individually. Time cost: 30 min.
6
Set Up Reminder Email Sequence
Reminder sequence (one week out, one day out, one hour before) is manually built in HubSpot as a workflow. If the event date changes the sequence must be rebuilt from scratch. Time cost: 50 min.
7
Notify Internal Team of Event Launch
Coordinator sends a Slack message to sales and customer success teams with the live event link. Time cost: 10 min.
8
Monitor Registrations and Update CRM
Coordinator periodically exports the registrant list from Zoom and manually imports it into HubSpot, tagging contacts and updating lifecycle stage. Time cost: 40 min.
9
Send Post-Event Follow-Up Email
Coordinator manually segments attendees from no-shows in HubSpot and sends different follow-up emails to each group including the replay link. Time cost: 45 min.
10
Log Event Performance to Reporting Sheet
Registration count, attendance rate, and email open rates are manually pulled from Zoom and HubSpot and entered into the reporting Google Sheet. Time cost: 25 min.
Time cost summary: Total manual time per event cycle is 350 minutes (5 hours 50 min). At 3 to 6 events per month this represents 17.5 to 35 hours of coordinator time monthly, or approximately 8 hours per week across a normalised run rate. Steps 2, 3, 5, 6, 7, 8, 9, and 10 are replaced by the three agents. Steps 1 (event brief entry) and 4 (Canva graphics) remain as intentional human steps. The three confirmed bottlenecks are Step 2 (45 min, Zoom setup), Step 6 (50 min, reminder sequence rebuild), and Step 8 (40 min, registrant CRM sync).
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Event Setup Agent

Reads a newly confirmed event row from the Google Sheets event calendar and orchestrates the two most time-expensive setup tasks automatically: creating the Zoom webinar registration and activating the pre-built HubSpot announcement plus reminder email sequence. Send dates for the sequence are calculated dynamically from the event date field in the brief row. This agent eliminates the manual Zoom form build, the copy-paste of the registration link, and the HubSpot sequence rebuild that currently stalls promotion launch by up to four days. Complexity: Moderate.

Trigger
New row added to the Google Sheets event calendar where the Status column value equals 'Confirmed'.
Tools
Google Sheets, Zoom, HubSpot
Replaces steps
Step 2 (Zoom registration build, 45 min), Step 3 (announcement email draft and schedule, 40 min), Step 6 (reminder sequence build, 50 min)
Estimated build
18 hours, Moderate complexity
// Input (from Google Sheets confirmed event row)
event_id          : string   // unique row identifier
event_title       : string
event_date        : ISO 8601 date
event_start_time  : HH:MM (24h, UTC offset stored in sheet)
event_duration_min: integer
speaker_name      : string
speaker_bio       : string
hubspot_list_id   : string   // target contact list for email sequence
sequence_template_id: string // HubSpot sequence to clone and activate

// Output
zoom_webinar_id        : string
zoom_registration_url  : string   // passed to Social Promotion Agent
hubspot_sequence_id    : string   // newly activated sequence
announcement_email_id  : string
reminder_1_send_date   : ISO 8601 datetime  // event_date minus 7 days
reminder_2_send_date   : ISO 8601 datetime  // event_date minus 1 day
reminder_3_send_date   : ISO 8601 datetime  // event_date minus 1 hour
setup_status           : 'success' | 'partial' | 'error'
setup_error_message    : string | null
  • Google Sheets trigger: poll interval should be set to 5 minutes maximum. The monitored sheet tab name must be confirmed before build ('Event Calendar' is the assumed default). The Status column header value must match exactly; case sensitivity applies.
  • Zoom API tier: a Zoom Pro or higher account is required. Basic accounts do not support webinar registration endpoints. Confirm the account tier before build starts. Use OAuth 2.0 server-to-server app credentials, not JWT (deprecated).
  • HubSpot sequence activation: the sequence template must already exist in HubSpot and must be cloneable via the Sequences API. The agent clones the template and updates send dates rather than creating a sequence from scratch. Confirm the sequence_template_id with the marketing coordinator before build.
  • Date calculation: all event_date values in the sheet must be stored in ISO 8601 format (YYYY-MM-DD). If the sheet contains formatted date strings (e.g. '5 May 2025'), a normalisation step must be added to the agent before the Zoom API call.
  • Fallback on Zoom error: if the Zoom webinar creation call fails, the agent must write 'ZOOM_ERROR' to the sheet Status column and post an alert to the designated Slack error channel. The agent must not proceed to HubSpot sequence activation without a confirmed zoom_registration_url.
  • Dedupe: if a row with the same event_id has already been processed (check against a processed_events log table), the agent must skip and log a duplicate warning rather than creating a second Zoom webinar.
Social Promotion Agent

Triggered immediately after the Event Setup Agent confirms a valid zoom_registration_url. Uses an AI content generation step to draft LinkedIn post copy for three scheduled slots: announcement (day of setup), mid-cycle reminder (event_date minus 7 days), and day-of post (event_date minus 1 day). The coordinator reviews and approves or revises the drafts before the agent schedules them via the LinkedIn Pages API. After approval, the agent also fires a formatted Slack message to the internal launch channel. This agent removes the manual writing and scheduling load while keeping a human in the loop for brand quality control. Complexity: Moderate.

Trigger
Activated by the Event Setup Agent on successful output (setup_status equals 'success'). Receives zoom_registration_url and event brief fields as context payload.
Tools
LinkedIn (Pages API), Slack
Replaces steps
Step 5 (LinkedIn post writing and scheduling, 30 min), Step 7 (Slack internal notification, 10 min)
Estimated build
14 hours, Moderate complexity
// Input (from Event Setup Agent output payload)
event_id               : string
event_title            : string
event_date             : ISO 8601 date
event_start_time       : HH:MM
speaker_name           : string
zoom_registration_url  : string
linkedin_page_id       : string   // LinkedIn Page URN, stored in credential store
slack_channel_id       : string   // stored in credential store

// Output (after AI draft generation, before approval)
draft_post_announcement : string  // copy for immediate scheduling
draft_post_midcycle     : string  // copy for event_date minus 7 days
draft_post_dayof        : string  // copy for event_date minus 1 day
approval_request_url    : string  // link sent to coordinator for review

// On approval
linkedin_post_id_1      : string  // scheduled announcement post UGC ID
linkedin_post_id_2      : string  // scheduled mid-cycle post UGC ID
linkedin_post_id_3      : string  // scheduled day-of post UGC ID
slack_message_ts        : string  // Slack message timestamp
social_status           : 'scheduled' | 'pending_approval' | 'error'
  • LinkedIn API access: scheduling posts requires a LinkedIn Page (not a personal profile) with the connected app granted the w_member_social and r_organization_social OAuth scopes. The Page admin must authorise the app. Confirm Page admin credentials are available before build.
  • AI copy generation: the content generation step must receive the event_title, speaker_name, event_date, and zoom_registration_url as prompt variables. The system prompt must enforce the brand voice guidelines provided by the marketing coordinator. Store the system prompt as an environment variable so it can be updated without a code change.
  • Human approval gate: the approval step must pause the workflow and send the three draft posts to the coordinator via email or an internal Slack DM with an approve/revise link. The workflow must not proceed to LinkedIn scheduling until approval_status equals 'approved'. Set a 24-hour timeout; if no approval is received, escalate to the marketing manager via Slack and hold the posts in draft.
  • Revision loop: if the coordinator returns a 'revise' response, the agent must regenerate the copy with the coordinator's comments appended to the prompt and re-request approval. Limit revision loops to three iterations before flagging for manual handling.
  • LinkedIn rate limits: the LinkedIn Pages API enforces a limit of 100 post creations per day per app. For the expected volume of 3 to 6 events per month this is not a constraint, but the agent should log each API call response code for audit purposes.
  • Slack notification: the internal Slack message must include event_title, event_date, event_start_time, and zoom_registration_url. Use a Block Kit formatted message, not plain text, for readability. The target slack_channel_id must be confirmed with the marketing coordinator before build.
Post-Event Sync Agent

Runs on a schedule triggered one hour after the event end time recorded in the Google Sheets brief row. Connects to the Zoom Reports API to pull the full attendee and no-show list, updates each contact's HubSpot record with an attendance tag and lifecycle stage change, triggers the correct segmented follow-up email sequence in HubSpot (attendee replay email or no-show re-invite), and writes registration count, attendance count, and email engagement figures back to the reporting row in Google Sheets. This agent closes the post-event data gap that currently leaves CRM records inaccurate and follow-ups delayed by one to two days. Complexity: Complex.

Trigger
Scheduled execution at event_end_time plus 60 minutes, derived from event_date plus event_start_time plus event_duration_min, stored at Event Setup Agent completion.
Tools
Zoom, HubSpot, Google Sheets
Replaces steps
Step 8 (registrant CRM sync, 40 min), Step 9 (post-event follow-up emails, 45 min), Step 10 (metrics log to reporting sheet, 25 min)
Estimated build
20 hours, Complex complexity
// Input
event_id              : string
zoom_webinar_id       : string   // from Event Setup Agent output
event_end_datetime    : ISO 8601 datetime
hubspot_list_id       : string
attendee_sequence_id  : string   // HubSpot sequence for confirmed attendees
noshow_sequence_id    : string   // HubSpot sequence for no-shows
sheet_row_id          : string   // Google Sheets row reference for metrics write-back

// Output
attendee_count        : integer
noshow_count          : integer
registrant_count      : integer
contacts_updated      : integer  // HubSpot records touched
attendee_tag          : string   // e.g. 'event-attended-[event_id]'
noshow_tag            : string   // e.g. 'event-noshow-[event_id]'
followup_sent_attendees: boolean
followup_sent_noshows : boolean
metrics_written       : boolean
sync_status           : 'success' | 'partial' | 'error'
sync_error_message    : string | null
  • Zoom Reports API availability: attendance data is only available after the host has formally ended the session using the 'End Meeting for All' control. If attendance data returns empty, the agent must wait 15 minutes and retry up to three times before writing a 'DATA_PENDING' status to the sheet and alerting via Slack. A manual override option must be documented in the runbook.
  • HubSpot contact matching: match Zoom registrants to HubSpot contacts by email address. If no match is found, create a new HubSpot contact with the attendee tag and source set to 'webinar-registration'. Do not overwrite existing lifecycle stage if it is already 'Customer' or 'Evangelist'.
  • Segmentation logic: a contact is classified as an attendee if their attended_duration_minutes is greater than or equal to 50% of event_duration_min. All other registrants are classified as no-shows. This threshold must be configurable as an environment variable.
  • HubSpot rate limits: the HubSpot Contacts API allows 100 requests per 10 seconds on Marketing Hub Professional. For events with large registrant lists, implement a batched update loop using the Batch Contacts Update endpoint (up to 100 contacts per call) rather than individual PATCH calls.
  • Google Sheets write-back: write to the specific row identified by sheet_row_id. Fields to write: registrant_count, attendee_count, noshow_count, and email open rate (fetched from HubSpot Campaign API after 24 hours). The open rate write-back should be a separate scheduled step triggered 24 hours after follow-up emails are sent.
  • Complexity justification: this agent is rated Complex due to the conditional segmentation logic, multi-tool data joins, retry handling, and the two-phase write-back requirement for metrics.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with field names and agent handoff points
// ============================================================
// EVENT & WEBINAR PROMOTION AUTOMATION -- END-TO-END DATA FLOW
// ============================================================

// TRIGGER
// Google Sheets: 'Event Calendar' tab, Status column = 'Confirmed'
TRIGGER google_sheets.row_added OR google_sheets.row_updated
  WHERE sheet.Status == 'Confirmed'
  AND   sheet.event_id NOT IN processed_events_log

  EXTRACT fields:
    event_id, event_title, event_date, event_start_time,
    event_duration_min, speaker_name, speaker_bio,
    hubspot_list_id, sequence_template_id

// ============================================================
// AGENT 1: EVENT SETUP AGENT
// ============================================================

// Step A: Create Zoom Webinar
CALL zoom.webinars.create
  PAYLOAD: { topic: event_title, start_time: event_date + event_start_time,
             duration: event_duration_min, host_email: [zoom_host_email env var],
             settings: { registrants_email_notification: true,
                         registration_type: 1 } }
  ON SUCCESS: zoom_webinar_id, zoom_registration_url
  ON ERROR:   write 'ZOOM_ERROR' to sheet Status column
              -> POST alert to Slack #automation-errors
              -> HALT agent

// Step B: Clone and activate HubSpot email sequence
CALL hubspot.sequences.clone
  PAYLOAD: { sequence_template_id: sequence_template_id }
  RETURNS: new_sequence_id

CALL hubspot.sequences.update_steps
  PAYLOAD: { sequence_id: new_sequence_id,
             steps: [
               { type: 'EMAIL', send_at: event_date - 7 days },   // reminder_1
               { type: 'EMAIL', send_at: event_date - 1 day },    // reminder_2
               { type: 'EMAIL', send_at: event_date - 60 min }    // reminder_3
             ],
             registration_url: zoom_registration_url }
  RETURNS: hubspot_sequence_id, announcement_email_id

CALL hubspot.sequences.enroll
  PAYLOAD: { sequence_id: hubspot_sequence_id,
             contact_list_id: hubspot_list_id }

// Write event_id to processed_events_log
LOG processed_events_log.insert: { event_id, zoom_webinar_id,
                                   hubspot_sequence_id,
                                   processed_at: now() }

// OUTPUT HANDOFF: Event Setup Agent -> Social Promotion Agent
EMIT payload:
  { event_id, event_title, event_date, event_start_time,
    speaker_name, zoom_registration_url,
    linkedin_page_id: [env var], slack_channel_id: [env var] }

// ============================================================
// AGENT 2: SOCIAL PROMOTION AGENT
// ============================================================

// Step C: Generate LinkedIn post copy via AI content step
CALL ai_content.generate
  PROMPT_VARS: { event_title, event_date, speaker_name, zoom_registration_url }
  SYSTEM_PROMPT: [BRAND_VOICE_PROMPT env var]
  RETURNS:
    draft_post_announcement  // slot: now
    draft_post_midcycle      // slot: event_date - 7 days
    draft_post_dayof         // slot: event_date - 1 day

// Step D: Human approval gate
SEND approval_request TO coordinator_email
  BODY: { draft_post_announcement, draft_post_midcycle, draft_post_dayof,
          approval_link, revise_link }
  TIMEOUT: 24 hours
  ON TIMEOUT: POST escalation to Slack #marketing-ops, HOLD workflow
  ON REVISE:  append coordinator_comments to prompt, regenerate (max 3 loops)
  ON APPROVE: continue to Step E

// Step E: Schedule LinkedIn posts
CALL linkedin.ugcPosts.create  // x3
  PAYLOAD_1: { author: linkedin_page_id, text: draft_post_announcement,
               scheduled_publish_time: now() + 30 min }
  PAYLOAD_2: { author: linkedin_page_id, text: draft_post_midcycle,
               scheduled_publish_time: event_date - 7 days at 09:00 }
  PAYLOAD_3: { author: linkedin_page_id, text: draft_post_dayof,
               scheduled_publish_time: event_date - 1 day at 09:00 }
  RETURNS: linkedin_post_id_1, linkedin_post_id_2, linkedin_post_id_3

// Step F: Slack internal launch notification
CALL slack.chat.postMessage
  PAYLOAD: { channel: slack_channel_id,
             blocks: Block_Kit_template {
               event_title, event_date, event_start_time,
               zoom_registration_url } }
  RETURNS: slack_message_ts

// ============================================================
// AGENT 3: POST-EVENT SYNC AGENT
// ============================================================

// Triggered at: event_date + event_start_time + event_duration_min + 60 min

// Step G: Pull Zoom attendance report
CALL zoom.report.webinar_participants
  PAYLOAD: { webinar_id: zoom_webinar_id }
  RETRY: up to 3x at 15 min intervals if response is empty
  ON PERSISTENT_EMPTY: write 'DATA_PENDING' to sheet, alert Slack #automation-errors
  RETURNS: participants[] { email, name, attended_duration_minutes }

// Step H: Classify attendees vs no-shows
FOR EACH contact IN participants:
  IF contact.attended_duration_minutes >= (event_duration_min * ATTENDANCE_THRESHOLD)
    // ATTENDANCE_THRESHOLD default 0.5, stored as env var
    tag = 'event-attended-' + event_id
    sequence_to_enroll = attendee_sequence_id
  ELSE:
    tag = 'event-noshow-' + event_id
    sequence_to_enroll = noshow_sequence_id

// Step I: Update HubSpot contacts in batches of 100
CALL hubspot.contacts.batch_upsert
  MATCH_BY: email
  ON_NO_MATCH: create new contact
  PAYLOAD_PER_CONTACT: { tag, lifecycle_stage: 'Lead' (skip if Customer/Evangelist),
                         event_source: 'webinar-registration' }
  RETURNS: contacts_updated

// Step J: Enroll contacts in segmented HubSpot follow-up sequences
CALL hubspot.sequences.enroll (attendees -> attendee_sequence_id)
CALL hubspot.sequences.enroll (noshows  -> noshow_sequence_id)

// Step K: Write metrics to Google Sheets reporting row
CALL google_sheets.rows.update
  ROW: sheet_row_id
  FIELDS: { registrant_count, attendee_count, noshow_count,
            contacts_updated, sync_status, synced_at: now() }

// Step L: Scheduled 24h later -- write email open rate
CALL hubspot.campaigns.get_stats
  PAYLOAD: { campaign_id: [derived from announcement_email_id] }
  RETURNS: open_rate_pct
CALL google_sheets.rows.update
  ROW: sheet_row_id
  FIELDS: { email_open_rate_pct: open_rate_pct }

// ============================================================
// FLOW COMPLETE
// ============================================================
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool (orchestration platform to be confirmed at project kickoff). Build one workflow per agent: 'Event Setup Workflow', 'Social Promotion Workflow', and 'Post-Event Sync Workflow'. Use a shared credential store so all three workflows reference the same HubSpot API key, Zoom OAuth token, LinkedIn Page token, Slack bot token, and Google Sheets service account without duplication. All credentials are stored as environment variables, never hardcoded.
Webhook configuration
The Event Setup Agent uses a polling trigger against the Google Sheets API at a 5-minute interval (no inbound webhook required). The Social Promotion Agent is triggered by an internal workflow call from the Event Setup Agent on successful completion, not a public webhook. The Post-Event Sync Agent uses a scheduled/delayed execution trigger set dynamically at event_end_datetime plus 60 minutes. No public-facing webhook endpoints are exposed in this build.
Templating approach
HubSpot email sequence bodies use HubSpot personalisation tokens ({{contact.firstname}}, {{event.title}}, {{event.registration_url}}) populated from contact properties and custom event properties. LinkedIn post copy is generated at runtime by the AI content step using a system prompt stored as an environment variable (BRAND_VOICE_PROMPT). The prompt template is version-controlled and must be reviewed against brand guidelines before go-live. Slack messages use LinkedIn Block Kit JSON templates stored as static configuration files in the workflow.
Error logging
All agent errors (Zoom API failures, HubSpot sequence errors, LinkedIn scheduling rejections, Sheets write failures) are written to a dedicated error log table in a Supabase project. Each row captures: event_id, agent_name, error_code, error_message, failed_at timestamp, and retry_count. A Slack alert is posted to #automation-errors for any error where sync_status equals 'error' or retry_count reaches the maximum. The FullSpec team monitors this channel during the first 30 days post-launch.
Testing approach
All agents are built and tested in a sandbox environment first. Zoom sandbox webinars, HubSpot sandbox portal (separate from production), and a dedicated 'STAGING' tab in the Google Sheet are used throughout development. LinkedIn does not offer a full sandbox, so post scheduling tests use a private LinkedIn Page created for testing purposes. End-to-end QA is run with a real upcoming event row in staging before any production credentials are connected.
Estimated total build time
Event Setup Agent: 18 hours. Social Promotion Agent: 14 hours. Post-Event Sync Agent: 20 hours. End-to-end integration testing and QA: 10 hours (covered in the delivery Week 5 QA stage, not per-agent). Discovery and tool access setup: included in Week 1 delivery stage, not counted in agent build hours. Total estimated build effort: 52 hours, consistent with the confirmed build_effort_hours in the project snapshot.
Pre-build blockers: the following items must be confirmed before any agent build begins. (1) HubSpot account tier: Marketing Hub Professional or higher is required for sequence cloning via API and batch contact updates. (2) Zoom account tier: Pro or higher is required for webinar registration endpoints. (3) LinkedIn Page admin access: the connected app must be authorised by a Page admin, not a personal profile owner. (4) Google Sheets template layout: the column header names used in the event calendar must match the field names in this specification exactly, or a mapping step must be added. (5) HubSpot sequence template ID: the coordinator must confirm which existing sequence template is the source for cloning. Raise any blocker to support@gofullspec.com before the Week 2 build start date.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Event & Webinar Promotion.

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