Back to Lead Nurture Sequence

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 Nurture Sequence

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

This document is the single technical reference for the FullSpec team building the Lead Nurture Sequence automation. It covers the current-state process map with identified bottlenecks, full specifications for each of the three agents (Lead Qualification, Sequence Messaging, and Sales Alert), the end-to-end data flow with exact field names, and the recommended build stack. The FullSpec team owns everything described here. The client-side responsibility is limited to confirming HubSpot field hygiene, approving email templates before go-live, and reviewing flagged edge-case leads during the pilot period.

01Process snapshot

Step
Name
Description
1
Check for New Leads in CRM
Sales rep opens HubSpot each morning and filters for contacts added in the last 24 hours, noting name, email, and source. (15 min)
2 BOTTLENECK
Qualify Lead Manually
Rep reviews form submission details against ICP criteria using a Google Sheet reference. Unqualified leads are tagged or discarded without a structured process. (10 min)
3
Write and Send First Follow-Up Email
Rep drafts a personalised introduction email from scratch or loosely from a saved draft, referencing lead source and enquiry. (20 min)
4
Log Email Activity in CRM
Rep manually logs outreach in HubSpot including date, message summary, and next follow-up date. Frequently skipped when busy. (8 min)
5
Set Follow-Up Reminder
Rep creates a HubSpot task or calendar reminder for follow-up if no reply within two to three business days. Set inconsistently across reps. (5 min)
6 BOTTLENECK
Send Second and Third Follow-Up Emails
When reminder fires, rep writes and sends a second follow-up, then repeats for a third. Each drafted manually with no enforced messaging standard. (25 min)
7
Check Whether Lead Has Booked a Meeting
Rep manually checks Calendly to see if the lead used the booking link, then updates CRM contact stage accordingly. (8 min)
8
Update CRM Stage After Each Touchpoint
Rep updates contact lifecycle stage in HubSpot for every interaction. When skipped, pipeline reports are inaccurate. (6 min)
9
Mark Lead as Disqualified or Nurture Only
After unanswered touchpoints, rep decides whether to close the lead or move to a long-term nurture list. Decision is inconsistent across the team. (5 min)
10
Notify Team of Hot Leads or Bookings
When a lead replies positively or books, rep sends an informal Slack message to the sales team. Happens inconsistently. (5 min)
Time cost summary: Total manual time per lead cycle is 107 minutes. At approximately 90 new leads per month (roughly 22 per week) and 6 manual hours lost per week, this process costs the business an estimated $9,744 per year at a $31/hr rep rate. The automation replaces steps 2, 3, 4, 5, 6, 7, 8, 9, and 10 in full. Step 1 is retained as a lightweight monitoring check; the human-only path (rep reads and responds to inbound replies) remains outside the automated flow as a deliberate handoff point.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Lead Qualification Agent

Reviews incoming HubSpot contact data and form submission fields to score the lead against the ideal customer profile and apply the correct qualification tag. The agent operates without human input for standard lead types and flags edge cases for rep review. Scoring uses a weighted model pulled from a Google Sheets reference table containing ICP criteria such as company size, lead source, enquiry type, and budget indicators. The output tag is written directly to a custom HubSpot contact property and determines whether the Sequence Messaging Agent fires or the record is held for manual review.

Trigger
New HubSpot contact record created with lifecycle stage set to 'Lead'
Tools
HubSpot, Google Sheets
Replaces steps
Steps 2 and 9
Estimated build
8 hours | Moderate complexity
// Input
hubspot.contact.id          : string
hubspot.contact.email       : string
hubspot.contact.firstname   : string
hubspot.contact.lastname    : string
hubspot.contact.hs_lead_source : string
hubspot.contact.enquiry_topic  : string   // custom property
hubspot.contact.company        : string
hubspot.contact.jobtitle       : string
google_sheets.icp_criteria     : object   // scoring weights per field

// Output
hubspot.contact.qualification_score  : integer  // 0-100
hubspot.contact.qualification_status : enum     // 'qualified' | 'edge_case' | 'disqualified'
hubspot.contact.lifecycle_stage       : string  // updated to 'marketingqualifiedlead' if qualified
hubspot.contact.nurture_flag          : boolean // true if disqualified but retainable

// On edge case
internal.flag_for_rep_review : true
internal.flag_reason         : string  // reason code from scoring model
  • HubSpot tier must be Sales Hub Starter or above. The private app token (not legacy API key) must be provisioned with contacts read/write and CRM object permissions before build begins.
  • The Google Sheets scoring table must be named 'ICP_Criteria' on a tab called 'Scoring' and must contain columns: field_name, weight, pass_value, fail_value. The FullSpec team will supply a starter template; the client confirms actual ICP values before the agent is wired.
  • Qualification score threshold for 'qualified' status is set at 60 by default. This must be confirmed with the process owner before go-live and is expected to be recalibrated after the first 30 days of live data.
  • If any of the core personalisation fields (hs_lead_source, enquiry_topic, firstname) are blank, the agent must flag the record as 'edge_case' rather than attempt to score on partial data.
  • Dedupe check: before writing the qualification tag, the agent queries HubSpot for existing contacts with the same email address. If a duplicate is found, it merges the new record into the existing one and does not trigger the Sequence Messaging Agent again.
  • The custom HubSpot property 'qualification_score' (type: number) and 'qualification_status' (type: enumeration) must be created in HubSpot before deployment. Confirm creation during the stack audit in week 1.
Sequence Messaging Agent

Generates and sends personalised follow-up emails at timed intervals using the lead's contact data, source channel, and enquiry details pulled from HubSpot. The agent sends three emails: a welcome email immediately on qualification, a second follow-up on day 2, and a third on day 4. It polls for Calendly booking events and inbound Gmail replies after each send. If a booking or positive reply is detected, the sequence stops and the Sales Alert Agent is triggered. If three touchpoints complete with no response, the contact is tagged as long-term nurture and removed from the active sequence.

Trigger
Lead Qualification Agent sets qualification_status = 'qualified'; then scheduled poll every 24 hours for active sequence contacts
Tools
HubSpot, Gmail, Calendly
Replaces steps
Steps 3, 4, 5, 6, 7, and 8
Estimated build
14 hours | Moderate complexity
// Input (per send cycle)
hubspot.contact.id               : string
hubspot.contact.firstname        : string
hubspot.contact.email            : string
hubspot.contact.hs_lead_source   : string
hubspot.contact.enquiry_topic    : string
hubspot.contact.sequence_step    : integer  // custom property: 0=not started, 1=sent, 2=sent, 3=sent
hubspot.contact.last_touch_date  : datetime // custom property
calendly.booking_events          : array    // filtered by contact email
gmail.thread_replies             : array    // inbound replies on sequence thread

// Output (after each send)
gmail.sent_message_id            : string
hubspot.contact.sequence_step    : integer  // incremented
hubspot.contact.last_touch_date  : datetime // updated to now()
hubspot.contact.lifecycle_stage  : string   // updated to 'opportunity' on booking detected
hubspot.contact.nurture_flag     : boolean  // set true after step 3 with no response
hubspot.engagement.email_log     : object   // written via HubSpot Engagements API

// On booking or reply detected
internal.sequence_halt           : true
internal.halt_reason             : enum  // 'booking_detected' | 'reply_detected'
internal.trigger_sales_alert     : true
  • Gmail must be connected via OAuth 2.0 using the sending rep's Google Workspace account. Required scopes: gmail.send, gmail.readonly (for reply detection on the thread). A service account with domain-wide delegation is preferred for multi-rep teams.
  • Three email templates (Touch 1, Touch 2, Touch 3) must be written, reviewed, and approved by the process owner before any send goes live. Templates use Handlebars-style merge tags: {{firstname}}, {{enquiry_topic}}, {{hs_lead_source}}, {{calendly_link}}.
  • The Calendly connection uses the Calendly v2 API (Bearer token). The agent polls the /scheduled_events endpoint filtered by invitee email. Webhook-based detection is preferred if the Calendly plan supports outgoing webhooks (Professional tier or above).
  • Sequence timing: Touch 1 fires immediately on trigger. Touch 2 fires after 2 business days (48 hours excluding weekends). Touch 3 fires after 4 business days from Touch 1. Business day logic must account for the client's local timezone, confirmed during stack audit.
  • The custom HubSpot property 'sequence_step' (type: number) and 'last_touch_date' (type: date) must be created before deployment. Email logging must use the HubSpot Engagements API v1 (type: EMAIL) to ensure activity appears in the contact timeline.
  • Reply detection uses Gmail thread ID stored after Touch 1 send. The agent checks for replies from the contact's email address on that thread ID. If a reply exists, the sequence halts regardless of content. Human rep reads and responds to the actual content.
  • Fallback: if Gmail send fails (rate limit or auth error), the error is logged to the Supabase error table with contact_id, step, timestamp, and error_code. The orchestration layer retries once after 15 minutes before alerting via Slack.
Sales Alert Agent

Watches for positive signals passed from the Sequence Messaging Agent, specifically a Calendly booking confirmation or a reply detection flag, and posts a formatted notification to the Slack sales channel. The message includes the lead's name, company, enquiry topic, meeting time (if a booking), and a direct link to the HubSpot contact record. This agent replaces the informal, inconsistent manual Slack message that reps currently send. It fires once per trigger event and does not repeat unless a new booking or reply is detected.

Trigger
Sequence Messaging Agent sets internal.trigger_sales_alert = true, passing halt_reason and contact metadata
Tools
Slack, HubSpot, Calendly
Replaces steps
Step 10
Estimated build
6 hours | Simple complexity
// Input
internal.halt_reason              : enum    // 'booking_detected' | 'reply_detected'
hubspot.contact.id                : string
hubspot.contact.firstname         : string
hubspot.contact.lastname          : string
hubspot.contact.company           : string
hubspot.contact.enquiry_topic     : string
hubspot.contact.hubspot_owner_id  : string
calendly.event.start_time         : datetime // null if halt_reason = reply_detected
calendly.event.event_type_name    : string   // null if halt_reason = reply_detected
hubspot.contact.record_url        : string   // constructed from portal_id + contact_id

// Output
slack.message.channel             : string   // e.g. #sales-alerts, confirmed pre-build
slack.message.text                : string   // formatted Block Kit message
slack.message.blocks              : array    // lead name, company, meeting time or reply flag, CRM link
slack.message.ts                  : string   // message timestamp returned by Slack API
  • Slack connection requires a Slack App installed to the client's workspace with the chat:write OAuth scope and membership in the target channel. The bot must be invited to the channel manually before deployment.
  • The target Slack channel name must be confirmed before build. Default assumption is #sales-alerts. The channel ID (not name) must be stored in the credential store as SLACK_CHANNEL_ID to avoid breakage if the channel is renamed.
  • Message format uses Slack Block Kit with two sections: (1) a header block identifying the trigger type ('New Booking' or 'Reply Detected') and (2) a fields block containing lead name, company, enquiry topic, meeting time (if applicable), and a button linking to the HubSpot record.
  • If the Slack send fails, the error is logged to the Supabase error table and a fallback plain-text email is sent to the rep's Gmail address as a safety net.
  • HubSpot portal ID must be stored as HUBSPOT_PORTAL_ID in the credential store to construct the record URL correctly: https://app.hubspot.com/contacts/{portal_id}/contact/{contact_id}.
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
// ====================================================
// LEAD NURTURE SEQUENCE: END-TO-END DATA FLOW
// FullSpec Build Reference | Lead Nurture Sequence
// ====================================================

// TRIGGER
// HubSpot webhook fires on contact.creation where lifecycle_stage = 'lead'
EVENT: hubspot.contact.created
  -> contact.id           : string   // e.g. '12345678'
  -> contact.email        : string
  -> contact.firstname    : string
  -> contact.lastname     : string
  -> contact.company      : string
  -> contact.jobtitle     : string
  -> contact.hs_lead_source : string // e.g. 'Organic Search', 'Paid Social'
  -> contact.enquiry_topic  : string // custom property, must be non-null
  -> contact.lifecycle_stage : 'lead'

// ====================================================
// AGENT 1: Lead Qualification Agent
// ====================================================

INPUT: hubspot.contact.*  +  google_sheets.icp_criteria (tab: 'Scoring')

  STEP 1.1: fetch ICP scoring table from Google Sheets
    sheets.spreadsheet_id : SHEETS_ICP_SPREADSHEET_ID  // from credential store
    sheets.range          : 'Scoring!A:E'
    returns               : icp_criteria[]  // field_name, weight, pass_value, fail_value

  STEP 1.2: score contact against ICP criteria
    score fields          : hs_lead_source, enquiry_topic, jobtitle, company
    output                : qualification_score  // integer 0-100
    threshold             : >= 60 -> 'qualified' | < 60 -> 'disqualified' | missing fields -> 'edge_case'

  STEP 1.3: dedupe check
    GET /crm/v3/objects/contacts?email={contact.email}
    if duplicate found -> merge, halt sequence for this contact

  STEP 1.4: write qualification result to HubSpot
    PATCH /crm/v3/objects/contacts/{contact.id}
    body:
      contact.qualification_score  : integer
      contact.qualification_status : 'qualified' | 'edge_case' | 'disqualified'
      contact.nurture_flag         : boolean
      contact.lifecycle_stage      : 'marketingqualifiedlead'  // if qualified

// AGENT 1 -> AGENT 2 HANDOFF
// If qualification_status = 'qualified', pass contact.id to Sequence Messaging Agent
// If qualification_status = 'edge_case', flag for rep review and halt
// If qualification_status = 'disqualified', set nurture_flag = true and halt active sequence

// ====================================================
// AGENT 2: Sequence Messaging Agent
// ====================================================

INPUT: hubspot.contact.id, hubspot.contact.* (refetched), sequence state

  STEP 2.1: send Touch 1 (immediate)
    gmail.send:
      to              : contact.email
      from            : GMAIL_SENDING_ADDRESS  // from credential store
      subject         : rendered from template_touch_1.subject
      body            : rendered from template_touch_1.body
        merge_tags    : {{firstname}}, {{enquiry_topic}}, {{hs_lead_source}}, {{calendly_link}}
      calendly_link   : CALENDLY_BOOKING_URL  // from credential store
    returns:
      gmail.message_id     : string  // stored as contact.sequence_thread_id
      gmail.thread_id      : string  // stored for reply detection

  STEP 2.2: log Touch 1 engagement in HubSpot
    POST /engagements/v1/engagements
    body:
      engagement.type       : 'EMAIL'
      engagement.timestamp  : now()
      metadata.subject      : template_touch_1.subject
      associations.contactIds : [contact.id]
    PATCH /crm/v3/objects/contacts/{contact.id}
      contact.sequence_step    : 1
      contact.last_touch_date  : now()
      contact.lifecycle_stage  : 'contacted'  // HubSpot stage

  STEP 2.3: scheduled poll (every 24h for contacts with sequence_step in [1,2])
    CHECK a: Calendly booking
      GET /scheduled_events?invitee_email={contact.email}
      if event.start_time exists and status = 'active':
        set halt_reason = 'booking_detected'
        set calendly.event.start_time, calendly.event.event_type_name
        -> trigger Sales Alert Agent
    CHECK b: Gmail reply on thread
      GET /gmail/v1/users/me/threads/{thread_id}
      if thread.messages count > sent_message_count:
        set halt_reason = 'reply_detected'
        -> trigger Sales Alert Agent

  STEP 2.4: send Touch 2 (day 2, if no halt)
    same gmail.send flow as 2.1 using template_touch_2
    log engagement, update contact.sequence_step = 2, contact.last_touch_date

  STEP 2.5: send Touch 3 (day 4, if no halt)
    same gmail.send flow as 2.1 using template_touch_3
    log engagement, update contact.sequence_step = 3, contact.last_touch_date

  STEP 2.6: post-Touch 3 no-response action
    PATCH /crm/v3/objects/contacts/{contact.id}
      contact.nurture_flag         : true
      contact.qualification_status : 'disqualified'  // or 'nurture_only'
      contact.lifecycle_stage      : 'lead'          // reset to long-term nurture pool

// AGENT 2 -> AGENT 3 HANDOFF
// internal.trigger_sales_alert = true
// payload: contact.id, contact.firstname, contact.lastname, contact.company,
//          contact.enquiry_topic, halt_reason, calendly.event.start_time (if booking),
//          hubspot.contact.record_url

// ====================================================
// AGENT 3: Sales Alert Agent
// ====================================================

INPUT: handoff payload from Agent 2

  STEP 3.1: construct HubSpot record URL
    url = 'https://app.hubspot.com/contacts/' + HUBSPOT_PORTAL_ID + '/contact/' + contact.id

  STEP 3.2: build Slack Block Kit message
    header block  : halt_reason = 'booking_detected' -> 'New Booking Confirmed'
                    halt_reason = 'reply_detected'   -> 'Lead Replied to Sequence'
    fields block  :
      'Lead'      : contact.firstname + ' ' + contact.lastname
      'Company'   : contact.company
      'Enquiry'   : contact.enquiry_topic
      'Meeting'   : calendly.event.start_time (formatted) | 'N/A'
    button block  : 'View in HubSpot' -> hubspot.contact.record_url

  STEP 3.3: post to Slack
    POST /api/chat.postMessage
    body:
      channel  : SLACK_CHANNEL_ID  // from credential store
      blocks   : constructed Block Kit payload
    returns:
      slack.message.ts  : string

// ====================================================
// ERROR HANDLING (all agents)
// ====================================================
// Any unhandled exception:
//   -> INSERT INTO supabase.errors (contact_id, agent_name, step, error_code, timestamp)
//   -> retry once after 15 minutes
//   -> if second failure: POST to Slack #automation-errors channel
// ====================================================
// END OF FLOW
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool (platform to be confirmed at project kickoff). One workflow per agent is the target structure: Workflow A handles Lead Qualification, Workflow B handles Sequence Messaging with its scheduled retry loops, and Workflow C handles Sales Alerts. All three workflows share a single credential store so that HubSpot tokens, Gmail OAuth credentials, the Calendly API key, and the Slack bot token are managed in one place and rotated once without touching individual workflows.
Webhook configuration
HubSpot outgoing webhook triggers Workflow A on contact.creation where lifecycle_stage = 'lead'. The webhook URL is generated by the orchestration layer and registered in HubSpot under Settings > Integrations > Private Apps > Webhooks (or via the HubSpot Workflows native webhook action if the plan supports it). Calendly outgoing webhooks (Professional tier required) are registered at /api/v2/webhook_subscriptions for the invitee.created event, filtered to the booking URL used in sequence emails. If Calendly webhooks are unavailable due to plan tier, the orchestration layer polls the /scheduled_events endpoint on a 30-minute cron interval as a fallback.
Templating approach
Email body templates are stored as plain-text files with Handlebars-style merge tags ({{firstname}}, {{enquiry_topic}}, {{hs_lead_source}}, {{calendly_link}}). Templates are loaded into the orchestration layer's environment at build time, not fetched per-run, to avoid latency. The Slack Block Kit payload is constructed inline within Workflow C using a JSON template string with variable substitution at runtime. All three email templates must be approved by the process owner and loaded before any test sends are made.
Error logging
A Supabase table named 'automation_errors' holds all workflow exceptions. Schema: id (uuid), contact_id (text), agent_name (text), step_label (text), error_code (text), error_message (text), created_at (timestamptz), resolved (boolean). Each agent writes to this table on any caught exception before attempting its single retry. A Supabase Database Webhook fires on INSERT to this table and posts a summary to a dedicated Slack channel (#automation-errors) so the FullSpec team is alerted in real time during and after launch.
Testing approach
All three agents are built and tested against a HubSpot sandbox account (or a clearly labelled test pipeline in the live HubSpot portal if a sandbox is unavailable). Gmail sends during testing use a designated test address (not the live sending domain) to avoid affecting deliverability reputation. Calendly test bookings are made using the live booking page but with a test calendar event type that does not notify the sales team. Slack test messages are posted to a private #automation-test channel before switching to #sales-alerts at go-live. No live lead data is used until the pilot phase in week 4.
Estimated total build time
Lead Qualification Agent: 8 hours. Sequence Messaging Agent: 14 hours. Sales Alert Agent: 6 hours. End-to-end integration testing and pilot QA: 6 hours (included in the delivery schedule week 4). Total: 34 hours across the 4-week delivery window. The template snapshot records 28 build-effort hours for agent build only; the additional 6 hours cover full end-to-end testing, error handling wiring, and the pilot run with live test leads.
Pre-build confirmation checklist: (1) HubSpot Sales Hub Starter or above confirmed with private app token provisioned. (2) Custom HubSpot contact properties created: qualification_score (number), qualification_status (enumeration), sequence_step (number), last_touch_date (date), sequence_thread_id (single-line text), nurture_flag (boolean). (3) Google Sheets ICP scoring table named 'ICP_Criteria' on tab 'Scoring' with ICP values confirmed by process owner. (4) Gmail OAuth completed for the sending address with gmail.send and gmail.readonly scopes. (5) Calendly API token confirmed and plan tier established (webhook vs polling fallback). (6) Slack app installed, bot invited to #sales-alerts and #automation-errors channels, SLACK_CHANNEL_ID confirmed. (7) Supabase project provisioned and automation_errors table created. (8) All three email templates written and signed off before first test send. Contact support@gofullspec.com if any credential or access item is blocked.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Lead Nurture Sequence.

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