Back to Lead Magnet & Landing Page Follow-up

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

Lead Magnet & Landing Page Follow-up

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

This document gives the FullSpec build team everything needed to construct, wire, and test the Lead Magnet and Landing Page Follow-up automation end to end. It covers the current-state step map, full agent specifications with IO contracts, the complete data flow from Typeform trigger to Slack alert, and the recommended build stack with estimated hours. All decisions about tooling, field names, and agent boundaries have been confirmed against the process template. The FullSpec team owns the build in its entirety; the sections below define exactly what that build must produce.

01Process snapshot

Step
Name
Description
1
Check Form Submissions
Marketing Coordinator logs into Typeform or checks email notification for new submissions, typically once or twice a day. Time cost: 10 min/submission batch.
2
Copy Contact Details to CRM
Coordinator manually copies name, email, and captured fields into HubSpot, first checking for an existing record. Time cost: 8 min/contact.
3
Apply List Tag and Lead Source
BOTTLENECK: Correct list, lead source tag, and lead magnet label applied manually in HubSpot. Errors here cause downstream segmentation failures. Time cost: 5 min/contact.
4
Locate Asset Download Link
Coordinator opens Google Drive to find the correct lead magnet PDF version and copies the shareable link. Time cost: 4 min/contact.
5
Send Asset Delivery Email
BOTTLENECK: Delivery email composed and sent manually in Mailchimp with the asset link. No template applied consistently, leading to variable delay and quality. Time cost: 10 min/contact.
6
Log Submission in Tracking Sheet
Submission details and send date logged in Google Sheets to track form volume and delivery status. Time cost: 5 min/contact.
7
Schedule Follow-up Reminder
Coordinator adds a calendar reminder or personal task to follow up in two to three days. No shared system used. Time cost: 5 min/contact.
8
Send Day-3 Follow-up Email
BOTTLENECK: Three days after delivery, coordinator manually writes or adapts a follow-up email and sends it. This step is frequently skipped under high volume. Time cost: 12 min/contact.
9
Check for Reply or Engagement
Coordinator reviews email opens and link clicks in HubSpot to decide whether to pass the contact to sales or continue nurturing. Time cost: 7 min/contact.
10
Notify Sales of Warm Leads
If lead has opened multiple emails or clicked a key link, coordinator sends a Slack message or email to the sales team manually. Time cost: 5 min/contact.
Time cost summary: Total manual time per cycle is 71 minutes per submission. At ~120 submissions/month (~30/week), this equates to approximately 5 hours of manual effort per week and 22 hours per 30-day period. The automation replaces steps 1, 2, 3, 4, 5, 6, 7, 8, 9, and 10 in full, retaining only a human exception review for bounced or data-quality-flagged contacts that fall outside the automated path.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Lead Capture and Enrichment Agent

This agent is the entry point of the workflow. It receives the raw Typeform submission payload the moment a new response is posted, extracts all field values, and performs a deduplication lookup against HubSpot before creating or updating the contact record. All CRM properties, list memberships, lead source tags, and lead magnet labels are applied in this agent without any human input. A confirmation row is then appended to the Google Sheets tracking log. The agent must handle multi-magnet contacts gracefully: if an existing HubSpot record is found, it updates properties and adds the new magnet label rather than creating a duplicate. Estimated build time is 10 hours at Moderate complexity.

Trigger
New Typeform submission event received by the automation platform via Typeform webhook (response.created event).
Tools
Typeform (webhook source), HubSpot (Contacts API: upsert, properties, list membership), Google Sheets (Sheets API v4: appendRow).
Replaces steps
Steps 1, 2, 3, and 6 (Check Form Submissions, Copy Contact Details to CRM, Apply List Tag and Lead Source, Log Submission in Tracking Sheet).
Estimated build
10 hours / Moderate complexity.
// Input: Typeform webhook payload
event.form_response.answers[*].field.ref  // all question refs
event.form_response.answers[*].text        // raw answer values
event.form_response.submitted_at           // ISO 8601 timestamp
event.form_response.token                  // unique submission ID

// Extracted fields passed downstream
contact.email          // from email field ref
contact.first_name     // from short_text ref: 'first_name'
contact.last_name      // from short_text ref: 'last_name'
contact.lead_magnet_id // from hidden field or choice ref: 'magnet_id'
contact.form_id        // Typeform form_id from event metadata
contact.submission_ts  // submitted_at normalised to UTC

// HubSpot upsert result
hubspot.contact_id     // hs_object_id of created or updated record
hubspot.is_new_contact // boolean: true = created, false = updated
hubspot.lists_enrolled // array of list IDs the contact was added to
hubspot.tags_applied   // array: [lead_source, lead_magnet_label]

// Google Sheets log row appended
sheet.row.contact_email
sheet.row.contact_name
sheet.row.lead_magnet_id
sheet.row.submission_ts
sheet.row.hubspot_contact_id
sheet.row.delivery_status  // set to 'pending' at this stage
  • HubSpot tier must be at least Starter (Marketing Hub) to allow list membership writes and custom property updates via API. Confirm the active subscription tier before build.
  • All HubSpot contact properties used (lead_magnet_label, lead_source, magnet_id, nurture_sequence_id) must be created and named exactly as specified in the field mapping table before the agent is wired. Any inconsistency in property names will cause silent write failures.
  • The deduplication logic uses email address as the unique key via HubSpot's contacts/v1/contact/createOrUpdate/:email endpoint. If a matching record exists, properties are merged and new list memberships appended; no duplicate record is created.
  • The lead magnet mapping table (magnet_id to HubSpot list ID, tag value, and Mailchimp sequence ID) must be supplied by the marketing team before build begins. This table drives all routing decisions downstream.
  • The Google Sheets tracking sheet must have a named range or fixed tab name confirmed before wiring (suggested: 'Submissions_Log'). The service account credential must have Editor access to the sheet.
  • If the Typeform payload does not contain a recognisable magnet_id (e.g. hidden field missing or empty), the agent must route the contact to a fallback HubSpot list named 'Unclassified_Submissions' and append a flag in the Sheets row rather than failing silently.
  • Confirm before build: does the client have more than one active Typeform form? If yes, the webhook must filter by form_id so submissions from unrelated forms do not enter this workflow.
Asset Delivery and Nurture Agent

This agent is triggered immediately after the Lead Capture and Enrichment Agent confirms the HubSpot contact has been created or updated. It retrieves the correct Google Drive asset link for the submitted lead magnet, sends the asset delivery email via Mailchimp transactional within 60 seconds of the original form submission, enrols the contact in the correct Mailchimp nurture automation sequence, and monitors HubSpot engagement data to determine when the warm-lead threshold is crossed. When the threshold is met (two email opens plus one link click recorded in HubSpot), it posts a structured Slack alert to the sales channel. Estimated build time is 10 hours at Moderate complexity. The remaining 2 hours cover end-to-end integration testing across both agents.

Trigger
Lead Capture and Enrichment Agent emits a confirmed contact record (hubspot.contact_id present, hubspot.is_new_contact or update confirmed).
Tools
Mailchimp (Transactional API / Mandrill: messages/send-template; Automations API: add subscriber to automation), Google Drive (Drive API v3: files.get for shareable link), HubSpot (Contacts API: get engagement timeline, update lead_score property), Slack (Incoming Webhook: chat.postMessage to #sales-warm-leads).
Replaces steps
Steps 4, 5, 7, 8, 9, and 10 (Locate Asset Download Link, Send Asset Delivery Email, Schedule Follow-up Reminder, Send Day-3 Follow-up Email, Check for Reply or Engagement, Notify Sales of Warm Leads).
Estimated build
10 hours / Moderate complexity.
// Input: from Lead Capture Agent
contact.email
contact.first_name
contact.lead_magnet_id
hubspot.contact_id
hubspot.nurture_sequence_id  // resolved from magnet mapping table
hubspot.is_new_contact

// Google Drive lookup
drive.file_id         // resolved from magnet_id via asset mapping table
drive.shareable_link  // webViewLink with 'anyone with link can view'

// Mailchimp transactional send
mailchimp.template_name     // e.g. 'asset-delivery-v2'
mailchimp.merge_vars.FNAME  // contact.first_name
mailchimp.merge_vars.ASSET_LINK  // drive.shareable_link
mailchimp.message.to[0].email    // contact.email
mailchimp.send_result.status     // 'sent' | 'queued' | 'rejected'
mailchimp.send_result.message_id // unique message ID for tracking

// Mailchimp nurture enrolment
mailchimp.automation_id        // from hubspot.nurture_sequence_id lookup
mailchimp.enrolment_status     // 'subscribed' confirmation

// Google Sheets delivery_status update
sheet.row.delivery_status  // updated to 'sent' on success
sheet.row.message_id       // mailchimp message ID logged

// Warm-lead check (polled via HubSpot engagement timeline)
hubspot.engagement.email_opens   // integer count
hubspot.engagement.link_clicks   // integer count
hubspot.warm_threshold_met       // boolean: opens >= 2 AND clicks >= 1

// On warm threshold: Slack alert payload
slack.channel    // #sales-warm-leads
slack.text       // formatted message string
slack.blocks[0].contact_name
slack.blocks[1].lead_magnet_id
slack.blocks[2].hubspot_contact_url
slack.blocks[3].engagement_summary  // 'X opens, Y clicks'

// On bounced or flagged contact: routed to manual exception step
exception.reason          // 'bounce' | 'invalid_email' | 'unsubscribed'
exception.hubspot_id      // contact ID for coordinator review
exception.sheets_row_ref  // row index in Submissions_Log for update
  • Mailchimp Transactional (Mandrill) is a separate add-on to standard Mailchimp and requires its own API key distinct from the main Mailchimp marketing API key. Confirm both keys are provisioned before build.
  • The asset delivery email template ('asset-delivery-v2' or the agreed name) must be created and approved in Mailchimp before the agent is wired. FullSpec cannot write email copy; the marketing team must supply the template content and merge variable names.
  • Each lead magnet must have a corresponding Mailchimp automation sequence ID and a Google Drive file ID recorded in the magnet mapping table. If any magnet lacks a sequence, the agent must enrol the contact in a generic fallback sequence ('nurture-default') rather than failing.
  • The warm-lead threshold check (2 opens + 1 click) is implemented by polling the HubSpot contact's engagement timeline on a schedule (suggested: every 4 hours) or by listening to HubSpot workflow-triggered webhooks if the account tier supports it. Confirm HubSpot tier before choosing the polling vs. webhook approach.
  • The Slack Incoming Webhook URL must be scoped to the #sales-warm-leads channel specifically. Do not use a general workspace webhook. Confirm the channel name and webhook URL during tool audit.
  • If the Mailchimp transactional send returns a 'rejected' status (e.g. the email is on the suppression list), the agent must flag the contact as an exception in Google Sheets and not attempt a retry. The marketing coordinator handles these cases in the manual exception review step.
  • Google Drive asset links must be set to 'anyone with the link can view' sharing. The build must verify this permission via Drive API before embedding the link in the email. If the file is not publicly accessible, the agent raises an exception rather than sending a broken link.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full data flow: Typeform trigger to Slack warm-lead alert, with field names at each stage
// ===================================================================
// TRIGGER: New Typeform submission received
// ===================================================================
TYPEFORM_WEBHOOK_POST /webhook/lead-magnet-followup
  payload.event_type           = 'form_response'
  payload.form_response.token  = '<unique_submission_id>'
  payload.form_response.submitted_at = '2024-04-19T09:14:32Z'
  payload.form_response.answers[0].field.ref = 'email'
  payload.form_response.answers[0].email      = 'alex@example.com'
  payload.form_response.answers[1].field.ref = 'first_name'
  payload.form_response.answers[1].text       = 'Alex'
  payload.form_response.answers[2].field.ref = 'last_name'
  payload.form_response.answers[2].text       = 'Jordan'
  payload.form_response.hidden.magnet_id      = 'magnet_seo_checklist'
  payload.form_response.form_id               = 'tf_form_abc123'

// --- AGENT 1 BEGINS: Lead Capture and Enrichment Agent ---

// Step 1: Extract and normalise fields
contact.email          = answers['email'].email           // 'alex@example.com'
contact.first_name     = answers['first_name'].text       // 'Alex'
contact.last_name      = answers['last_name'].text        // 'Jordan'
contact.lead_magnet_id = hidden.magnet_id                 // 'magnet_seo_checklist'
contact.form_id        = payload.form_response.form_id   // 'tf_form_abc123'
contact.submission_ts  = payload.form_response.submitted_at

// Step 2: HubSpot deduplication and upsert
POST /contacts/v1/contact/createOrUpdate/email/alex@example.com
  body.properties[].property = 'firstname'        value = 'Alex'
  body.properties[].property = 'lastname'         value = 'Jordan'
  body.properties[].property = 'lead_source'      value = 'typeform_landing_page'
  body.properties[].property = 'lead_magnet_label' value = 'SEO Checklist'
  body.properties[].property = 'magnet_id'        value = 'magnet_seo_checklist'
  body.properties[].property = 'nurture_sequence_id' value = 'mc_seq_seo_001'
  body.properties[].property = 'hs_lead_status'   value = 'NEW'
response.vid            -> hubspot.contact_id     // e.g. 12345678
response.isNew          -> hubspot.is_new_contact // true

// Step 3: HubSpot list membership write
POST /contacts/v1/lists/<list_id_seo_checklist>/add
  body.vids = [hubspot.contact_id]
response.updated[]      -> hubspot.lists_enrolled // ['list_seo_leads']

// Step 4: Google Sheets row append
POST sheets/v4/spreadsheets/<SHEET_ID>/values/Submissions_Log!A:J:append
  values = [
    contact.email,
    contact.first_name + ' ' + contact.last_name,
    contact.lead_magnet_id,
    contact.submission_ts,
    hubspot.contact_id,
    'pending'   // delivery_status
    ''          // message_id: populated in Agent 2
  ]
response.updates.updatedRange -> sheet.row_ref   // e.g. 'Submissions_Log!A47:J47'

// --- AGENT 1 COMPLETE: emits {contact.*, hubspot.*, sheet.row_ref} ---
// --- AGENT 2 BEGINS: Asset Delivery and Nurture Agent ---

// Step 5: Resolve asset link from Google Drive
GET drive/v3/files/<drive_file_id_seo_checklist>?fields=webViewLink,permissions
  drive_file_id resolved via magnet_id -> asset mapping table
response.webViewLink     -> drive.shareable_link
response.permissions[]   -> assert 'anyone' role = 'reader'  // else raise exception

// Step 6: Mailchimp transactional asset delivery email
POST https://mandrillapp.com/api/1.0/messages/send-template
  body.template_name        = 'asset-delivery-v2'
  body.template_content[]
  body.message.to[0].email  = contact.email
  body.message.to[0].name   = contact.first_name + ' ' + contact.last_name
  body.message.merge_vars[0].name = 'FNAME'       content = contact.first_name
  body.message.merge_vars[1].name = 'ASSET_LINK'  content = drive.shareable_link
  body.message.merge_vars[2].name = 'MAGNET_NAME' content = 'SEO Checklist'
response[0].status           -> mailchimp.send_result.status   // 'sent'
response[0]._id              -> mailchimp.send_result.message_id

// Step 7: Update Google Sheets delivery_status
PUT sheets/v4/spreadsheets/<SHEET_ID>/values/sheet.row_ref
  values[5] = 'sent'
  values[6] = mailchimp.send_result.message_id

// Step 8: Mailchimp nurture sequence enrolment
POST https://us1.api.mailchimp.com/3.0/automations/<mc_seq_seo_001>/emails/<email_1_id>/queue
  body.email_address = contact.email
response.id           -> mailchimp.enrolment_status  // 'subscribed'

// Step 9: Warm-lead threshold check (polled every 4 hours via HubSpot engagement timeline)
GET /engagements/v1/engagements/associated/CONTACT/<hubspot.contact_id>/paged
  filter: type = 'EMAIL'
  aggregate: email_opens  = count(engagement.metadata.openedAt != null)
  aggregate: link_clicks  = count(engagement.metadata.linkClicked != null)
hubspot.warm_threshold_met = (email_opens >= 2 AND link_clicks >= 1)

// Step 10 (conditional): Post warm-lead Slack alert
IF hubspot.warm_threshold_met == true:
  POST https://hooks.slack.com/services/<SLACK_WEBHOOK_PATH>
    body.channel  = '#sales-warm-leads'
    body.blocks[0].type = 'section'
      body.blocks[0].text = 'Warm lead alert: Alex Jordan (alex@example.com)'
    body.blocks[1].text = 'Lead magnet: SEO Checklist'
    body.blocks[2].text = 'Engagement: 2 opens, 1 click'
    body.blocks[3].text = 'HubSpot: https://app.hubspot.com/contacts/<hubspot.contact_id>'
  response.ok -> slack.alert_sent = true

// Step 10 (exception path): Bounce or rejected email
IF mailchimp.send_result.status IN ['rejected','bounced']:
  exception.reason         = mailchimp.send_result.reject_reason
  exception.hubspot_id     = hubspot.contact_id
  exception.sheets_row_ref = sheet.row_ref
  // Flag row in Submissions_Log, update delivery_status = 'exception'
  // No retry attempted; routed to Marketing Coordinator manual review

// --- AGENT 2 COMPLETE ---
// ===================================================================
// END OF AUTOMATED FLOW
// ===================================================================
// Remaining manual step: Marketing Coordinator reviews exception contacts
// flagged in Submissions_Log (delivery_status = 'exception') and corrects
// or removes records in HubSpot before re-triggering delivery if appropriate.
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool with one workflow per agent (Workflow A: Lead Capture and Enrichment; Workflow B: Asset Delivery and Nurture). Both workflows share a single credential store so all API keys and OAuth tokens are managed centrally and rotated without touching workflow logic. Workflows are chained via an internal trigger or message queue: Workflow A emits a payload on successful HubSpot upsert, which fires Workflow B. No direct coupling between workflow files.
Webhook configuration
Typeform webhook configured to POST to the automation platform's unique inbound webhook URL for Workflow A. Event filter: form_response only. Signature verification using Typeform's SHA-256 HMAC header (Typeform-Signature) must be implemented to reject unsigned or replayed requests. A test submission must be fired during tool audit to confirm payload shape before build proceeds.
Templating approach
The magnet-to-asset mapping table (magnet_id, drive_file_id, hubspot_list_id, hubspot_tag_value, mailchimp_sequence_id, mailchimp_template_name) is stored as a JSON lookup object within the workflow or as a referenced Google Sheets tab ('Magnet_Map') that both agents can read. Using a Sheets tab is preferred because the marketing team can update it without accessing the automation platform. The asset delivery email template is managed entirely within Mailchimp; no email HTML is stored in the automation layer.
Error logging
All unhandled exceptions (Drive permission failure, Mailchimp rejected status, HubSpot API 4xx/5xx, Slack webhook failure) write a structured error row to a dedicated Supabase table (table: automation_errors, columns: workflow_id, step_name, error_code, error_message, contact_email, submission_ts, resolved BOOLEAN). A Slack alert is sent to #automation-alerts for any error row where resolved = false after 30 minutes. The FullSpec team reviews this channel during the stabilisation period post-launch.
Testing approach
All agents are built and tested in a sandbox environment first: a separate Typeform test form, a HubSpot sandbox account (or a designated test contact prefix, e.g. test+*@gofullspec.com), a Mailchimp test audience, and a private Slack channel (#automation-test). Live credentials are not introduced until the end-to-end QA phase confirmed in the Test and QA Plan. Duplicate submission tests, bounce simulation, and warm-lead threshold crossing are all run in sandbox before cutover.
Estimated total build time
Lead Capture and Enrichment Agent: 10 hours. Asset Delivery and Nurture Agent: 10 hours. End-to-end integration testing and QA: 2 hours. Total: 22 hours. This matches the confirmed build_effort_hours in the process template. The build is scoped at Moderate overall complexity, with a one-off build cost of $2,600 at the Standard tier.
Before the build starts, the FullSpec team requires the following from your team: confirmed HubSpot property names and list IDs for every active lead magnet, the completed magnet mapping table (magnet_id, Drive file ID, Mailchimp sequence ID), Mailchimp transactional (Mandrill) API key, Mailchimp marketing API key, the asset delivery email template approved and live in Mailchimp, Google Drive file IDs for each asset with 'anyone with link' sharing confirmed, the Typeform webhook secret for HMAC verification, the Slack Incoming Webhook URL for #sales-warm-leads, and Editor access for the automation service account on the Google Sheets tracking spreadsheet. Any item not in place on build start day will extend the delivery timeline beyond the four-week estimate.
Support and handover: once the automation is live, your designated contact for ongoing changes, new lead magnet additions, and exception handling questions is the FullSpec team at support@gofullspec.com. The marketing coordinator's responsibilities post-launch are limited to reviewing the Submissions_Log rows flagged with delivery_status = 'exception' and taking any corrective action on those contacts in HubSpot.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Lead Magnet & Landing Page Follow-up.

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