Back to Lead Capture & Routing

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 Capture & Routing

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

This document gives the FullSpec build team everything needed to configure, wire, and ship the Lead Capture and Routing automation. It covers the current-state process map with bottlenecks identified, full agent specifications with IO contracts, an end-to-end data flow trace, and the recommended build stack. All build decisions and integrations are handled by FullSpec. The process owner's responsibilities are limited to supplying the scoring rubric, confirming territory rules, and approving go-live after the test pass. Reach the FullSpec team at support@gofullspec.com for any pre-build queries.

01Process snapshot

Step
Name
Description
01 BOTTLENECK
Check All Lead Sources
Sales Coordinator manually checks web form inbox, ad platform dashboards, and email aliases for new leads. Leads can wait hours between checks. Time cost: 20 min/lead batch.
02
Duplicate Check in CRM
Coordinator searches HubSpot by email and phone to confirm the lead does not already exist. A missed duplicate causes double-working and reporting noise. Time cost: 5 min/lead batch.
03
Create or Update CRM Contact
Coordinator manually types lead details into HubSpot across name, company, email, phone, source, and notes. Typos and inconsistent field mapping are common. Time cost: 8 min/lead batch.
04
Log Lead in Tracking Sheet
Coordinator copies the lead into a shared Google Sheet for the sales manager. This is a redundant double-entry step caused by the manager not using HubSpot daily. Time cost: 5 min/lead batch.
05 BOTTLENECK
Assess Lead Quality Manually
Coordinator makes an informal quality judgement based on form answers and company name, with no consistent rubric. Output quality varies by day and by person. Time cost: 7 min/lead batch.
06 BOTTLENECK
Decide Which Rep to Assign
Coordinator checks a sheet or asks over Slack to find an available rep covering the right territory. Informal routing creates uneven workloads. Time cost: 8 min/lead batch.
07
Assign Lead in CRM
Coordinator opens the HubSpot contact record and manually assigns it to the chosen rep, then sets the lifecycle stage. Time cost: 4 min/lead batch.
08
Notify Rep via Slack or Email
Coordinator sends a Slack message or forwarded email to the rep with a link to the HubSpot record. The rep must open the record separately to read context. Time cost: 5 min/lead batch.
09
Send Initial Acknowledgement Email
A generic confirmation email is manually sent from a shared Gmail inbox, often delayed by hours. Template is copy-pasted and first name filled by hand. Time cost: 6 min/lead batch.
10
Create Follow-Up Task for Rep
Coordinator creates a HubSpot task on the contact record. This step is frequently skipped during high-volume periods. Time cost: 4 min/lead batch.
Time cost summary: Total manual time per lead batch is 72 minutes. At approximately 90 leads per month (roughly 3 batches per week given batch processing behaviour), this equates to 6 hours of coordinator time per week and 312 hours per year. Steps 1, 2, 4, and 5 are fully replaced by the Lead Scoring and Enrichment Agent. Steps 3, 6, 7, 8, and 10 are fully replaced by the Lead Routing and Notification Agent. Step 9 (acknowledgement email) is also automated. Step 5 (manual quality review) is retained only for Low Priority leads flagged by the scoring agent as an exception path. This reduces coordinator involvement to exception handling only.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Lead Scoring and Enrichment Agent

This agent is the first node in the automated flow. It fires the instant a new lead submission is detected from any connected source, including Typeform webhook, Gmail inbound alias polling, or a direct ad-platform webhook. It checks HubSpot for an existing contact by exact email match and normalised phone match, merges or updates if a duplicate is found, and creates a new contact record if none exists. It then applies the configured numeric scoring rubric against firmographic fields (company size, industry, geography) and form-answer fields (service interest, budget indicator, urgency signal) to produce a total score. Based on configurable score thresholds, it tags the lead as Hot, Warm, or Low Priority and emits a structured enriched-lead payload for the routing agent. Low Priority leads are flagged for a human review step before the routing agent acts on them. All other leads pass straight through.

Trigger
Fires immediately when a new lead submission is detected from any configured source (Typeform webhook POST, Gmail alias polling interval max 5 min, ad-platform webhook event).
Tools
HubSpot (Contacts API v3 for search, create, update, merge), Typeform (Responses webhook), Google Sheets (Append row via Sheets API v4 for manager log).
Replaces steps
Steps 1 (source checking), 2 (duplicate check), 4 (Google Sheets log entry), and 5 (manual quality assessment).
Estimated build
10 hours. Complexity: Moderate.
// Input
typeform_response_id: string
submission_timestamp: ISO8601
lead_source: enum [typeform, gmail_alias, webhook]
first_name: string
last_name: string
email: string
phone: string | null
company_name: string | null
company_size: string | null
service_interest: string
budget_indicator: string | null
urgency_signal: string | null
raw_form_answers: object

// Output
hubspot_contact_id: string
is_duplicate: boolean
duplicate_action: enum [merged, updated, created]
score_total: integer (0-100)
score_tier: enum [Hot, Warm, Low Priority]
enriched_company_size: string | null
enriched_industry: string | null
lifecycle_stage: enum [Lead, MarketingQualifiedLead]
requires_human_review: boolean
sheets_row_appended: boolean
enriched_payload: object
  • HubSpot tier requirement: the Contacts API v3 search endpoint (POST /crm/v3/objects/contacts/search) is available on HubSpot Starter and above. Confirm the connected account tier before build begins. If the account is on the free tier, search and merge endpoints are unavailable and the build cannot proceed without an upgrade.
  • Deduplication logic: match first by email (exact, lowercased), then by normalised phone (E.164 format, strip spaces and dashes). If both email and phone return different existing contacts, flag as a conflict and route to human review rather than auto-merging.
  • Scoring rubric: the numeric model cannot be built until the sales manager provides a documented rubric mapping field values to score weights. This is the single hardest pre-build dependency. Build must not start on this agent until the rubric document is signed off.
  • Score threshold configuration: Hot/Warm/Low Priority thresholds must be stored as environment variables in the orchestration platform, not hardcoded, so they can be adjusted post-launch without a rebuild.
  • Typeform webhook security: validate the Typeform-Signature header on every inbound POST using the shared HMAC secret from the Typeform account. Reject and log any request that fails signature verification.
  • Gmail alias polling: if the Gmail inbound alias trigger is used, the polling interval must not exceed 5 minutes. Use a processed-message ID store (keyed by Gmail message ID) to prevent duplicate processing of the same email across polling cycles.
  • Google Sheets append: the Sheets API v4 append call writes to the first empty row after the last populated row in the configured sheet. Confirm the sheet tab name and column order match the manager's existing reporting view before wiring.
  • Fallback on enrichment failure: if a third-party enrichment call (if added later) times out, the agent must still proceed with the data from the form submission alone. Do not block the lead pipeline on enrichment.
Lead Routing and Notification Agent

This agent receives the enriched-lead payload from the Lead Scoring and Enrichment Agent (or from the human review exception step for Low Priority leads that have been approved). It reads the current territory configuration and rep workload data from HubSpot, applies the defined routing rules to select the best available rep, writes the rep assignment and lifecycle stage update to the HubSpot contact record, creates a prioritised follow-up task with a due date matched to the lead's score tier, posts a formatted Slack message to the assigned rep's direct channel or a designated channel, sends a personalised acknowledgement email to the prospect via Gmail within the two-minute SLA, and finally appends a completion status to the Google Sheets log row written by the first agent.

Trigger
Fires after the Lead Scoring and Enrichment Agent emits a completed enriched-lead payload with requires_human_review: false, or after a human reviewer approves a Low Priority lead via the configured review interface.
Tools
HubSpot (Contacts API v3, Tasks API, Owners API), Slack (Web API chat.postMessage), Gmail (Gmail API v1 users.messages.send via OAuth2 service account).
Replaces steps
Steps 3 (CRM contact create/update), 6 (rep routing decision), 7 (assign lead in CRM), 8 (notify rep), and 10 (create follow-up task). Also automates step 9 (acknowledgement email) in full.
Estimated build
10 hours. Complexity: Moderate.
// Input
enriched_payload: object (from Agent 1 output)
hubspot_contact_id: string
score_total: integer
score_tier: enum [Hot, Warm, Low Priority]
lifecycle_stage: string
requires_human_review: boolean
human_review_approved: boolean | null
territory_config: object (loaded from environment or HubSpot property)

// Output
assigned_rep_hubspot_owner_id: string
assigned_rep_name: string
assigned_rep_slack_user_id: string
hubspot_contact_updated: boolean
follow_up_task_id: string
follow_up_task_due_date: ISO8601
slack_message_ts: string
slack_channel_id: string
gmail_message_id: string
acknowledgement_sent_at: ISO8601
sheets_status_updated: boolean

// On approval (Low Priority exception path only)
human_reviewer_id: string
review_decision: enum [approved, rejected]
review_timestamp: ISO8601
rejection_reason: string | null
  • Territory routing rules: rules must be provided as a structured JSON document before build starts, covering rep HubSpot owner IDs, territory or product-line assignments, and workload cap (maximum open leads per rep). The agent reads this config from a versioned environment variable, not from a live sheet, to avoid runtime dependency on Google Sheets availability.
  • Workload balancing: the agent calls the HubSpot Owners API and counts open contacts assigned to each eligible rep. If a rep is at their workload cap, the agent routes to the next eligible rep in priority order. If all eligible reps are at cap, the lead is held and a Slack alert is posted to the sales manager's channel.
  • Slack message format: the message must include prospect first name, company, score tier, lead source, a one-line summary of key form answers, and a deep link to the HubSpot contact record. Format using Slack Block Kit (section blocks plus a button action). Do not use legacy attachments.
  • Slack channel targeting: route to the rep's personal Slack DM by default (looked up via Slack users.lookupByEmail using the rep's work email stored in HubSpot). Confirm all rep email addresses in HubSpot match their Slack workspace emails before build.
  • Gmail OAuth2 scope: the Gmail send scope (https://www.googleapis.com/auth/gmail.send) is sufficient. Do not request broader scopes. The connected Gmail account must be the designated shared sales inbox, not a personal account.
  • Acknowledgement email template: a plain-text-and-HTML template must be agreed and supplied before build. The template uses first_name and service_interest as the only merge fields initially. Store the template as an environment variable or a versioned file in the credential store.
  • Follow-up task due date logic: Hot leads get a task due 1 business day from routing time; Warm leads 2 business days; Low Priority approved leads 5 business days. Business day calculation must account for weekends. Implement with a simple offset function, not an external calendar API.
  • HubSpot lifecycle stage write: set lifecycle stage to MarketingQualifiedLead for Warm and Hot leads on contact create. Do not overwrite lifecycle stage if the contact already exists at a later stage (e.g. Opportunity or Customer).
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with field names at each agent handoff
// ─────────────────────────────────────────────────────────────────
// TRIGGER: New lead detected
// ─────────────────────────────────────────────────────────────────
SOURCE: Typeform webhook POST  →  /webhook/typeform/lead-capture
  OR: Gmail alias poll         →  INBOX:sales@[YourCompany.com]
  OR: Ad platform webhook POST →  /webhook/adplatform/lead-event

TRIGGER PAYLOAD (normalised by orchestration layer):
  typeform_response_id  : string   // unique per submission
  submission_timestamp  : ISO8601
  lead_source           : enum [typeform, gmail_alias, webhook]
  first_name            : string
  last_name             : string
  email                 : string   // lowercased before all downstream calls
  phone                 : string | null
  company_name          : string | null
  company_size          : string | null
  service_interest      : string
  budget_indicator      : string | null
  urgency_signal        : string | null
  raw_form_answers      : object

// ─────────────���───────────────────────────────────────────────────
// AGENT 1 HANDOFF: Lead Scoring and Enrichment Agent
// ─────────────────────────────────────────────────────────────────
STEP 1.1 — HubSpot duplicate check
  POST /crm/v3/objects/contacts/search
  filter: email eq <normalised_email>
  if match → is_duplicate: true, action: update or merge
  if no match → is_duplicate: false, action: create

STEP 1.2 — Score computation
  score_total = sum(
    weight_company_size[company_size],
    weight_service_interest[service_interest],
    weight_budget_indicator[budget_indicator],
    weight_urgency_signal[urgency_signal],
    weight_lead_source[lead_source]
  )
  score_tier = Hot   if score_total >= HOT_THRESHOLD   (env var)
             = Warm  if score_total >= WARM_THRESHOLD  (env var)
             = Low Priority otherwise

STEP 1.3 — HubSpot contact write
  POST /crm/v3/objects/contacts  (create)
  OR PATCH /crm/v3/objects/contacts/{id}  (update)
  fields written:
    firstname, lastname, email, phone, company,
    hs_lead_source (custom), lead_score (custom),
    lead_score_tier (custom), lifecyclestage

STEP 1.4 — Google Sheets append
  POST spreadsheets/{SHEET_ID}/values/{RANGE}:append
  row: [submission_timestamp, first_name, last_name, email,
        company_name, lead_source, score_total, score_tier,
        hubspot_contact_id, 'Pending routing']

AGENT 1 OUTPUT PAYLOAD (passed to Agent 2):
  hubspot_contact_id    : string
  is_duplicate          : boolean
  duplicate_action      : enum [merged, updated, created]
  score_total           : integer
  score_tier            : enum [Hot, Warm, Low Priority]
  lifecycle_stage       : string
  requires_human_review : boolean  // true when score_tier == Low Priority
  sheets_row_appended   : boolean
  enriched_payload      : object   // full normalised lead record

// ─────────────────────────────────────────────────────────────────
// DECISION: Low Priority branch
// ─────────────────────────────────────────────────────────────────
if requires_human_review == true:
  → post alert to sales manager Slack channel (LOW_PRIORITY_CHANNEL env var)
  → await human_review_approved flag (webhook callback or manual trigger)
  → if rejected: update HubSpot contact tag to 'Disqualified', end flow
  → if approved: continue to Agent 2 with human_review_approved: true

// ─────────────────────────────────────────────────────────────────
// AGENT 2 HANDOFF: Lead Routing and Notification Agent
// ─────────────────────────────────────────────────────────────────
STEP 2.1 — Rep selection
  GET /crm/v3/owners  // load all active reps
  apply territory_config (env var JSON):
    match lead geography / service_interest → eligible_reps[]
  count open contacts per eligible rep via HubSpot search
  select rep with lowest open count below WORKLOAD_CAP (env var)
  if all reps at cap → alert sales manager, hold lead

STEP 2.2 — HubSpot contact update (assign + task)
  PATCH /crm/v3/objects/contacts/{hubspot_contact_id}
    hubspot_owner_id: assigned_rep_hubspot_owner_id
    lifecyclestage: MarketingQualifiedLead (Hot or Warm only)
  POST /crm/v3/objects/tasks
    hs_task_subject: 'Follow up: {first_name} {last_name} ({score_tier})'
    hs_task_type: CALL
    hs_timestamp: follow_up_task_due_date
    hubspot_owner_id: assigned_rep_hubspot_owner_id
    associations: [{contactId: hubspot_contact_id}]

STEP 2.3 — Slack notification
  POST https://slack.com/api/chat.postMessage
    channel: assigned_rep_slack_user_id (DM)
    blocks: Block Kit (section: prospect name, score tier, source,
            key form answers summary; button: 'Open in HubSpot'
            url: https://app.hubspot.com/contacts/{portal_id}/contact/{hubspot_contact_id})

STEP 2.4 — Gmail acknowledgement
  POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
    to: lead email
    from: sales@[YourCompany.com]
    subject: 'Thanks for reaching out, {first_name}'
    body: personalised template (merge: first_name, service_interest)
    sent within 120 seconds of trigger timestamp

STEP 2.5 — Google Sheets status update
  PATCH spreadsheets/{SHEET_ID}/values/{ROW_RANGE}
    update column 'routing_status' to assigned_rep_name
    update column 'acknowledgement_sent' to acknowledgement_sent_at

// ─────────────────────────────────────────────────────────────────
// FINAL OUTPUT
// ─────────────────────────────────────────────────────────────────
assigned_rep_name         : string
hubspot_contact_updated   : boolean
follow_up_task_id         : string
slack_message_ts          : string
gmail_message_id          : string
acknowledgement_sent_at   : ISO8601
sheets_status_updated     : boolean
// END OF FLOW
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool (platform TBD at build kick-off) running one workflow per agent. Agent 1 and Agent 2 are separate workflows connected by a structured payload pass. All credentials are stored in a shared credential store within the platform, never hardcoded in workflow nodes. Each workflow is version-controlled with a descriptive change log entry on every edit.
Webhook configuration
Typeform: configure a webhook in the Typeform account dashboard pointing to the orchestration platform's inbound webhook URL. Enable HMAC signature verification using the Typeform webhook secret stored as a credential. Ad platform webhooks: configure equivalent POST endpoints per platform. Gmail alias: use a polling trigger with a 5-minute maximum interval and a processed-message-ID deduplication store keyed on Gmail message ID. All webhook endpoints must use HTTPS only.
Templating approach
Slack messages use Slack Block Kit JSON templates stored as workflow-level variables. Gmail acknowledgement emails use a plain-text-plus-HTML template stored as an environment variable, with merge fields delimited as {{first_name}} and {{service_interest}}. Scoring rubric weights and tier thresholds are stored as structured JSON environment variables (HOT_THRESHOLD, WARM_THRESHOLD, SCORE_WEIGHTS) so they can be updated without editing workflow logic.
Error logging
All agent errors (API failures, validation failures, unmatched territory, workload cap reached) write a structured error record to a Supabase table (table: automation_errors, fields: workflow_id, agent_name, error_code, error_message, lead_email, timestamp, payload_snapshot). Any error record with severity='critical' triggers an immediate Slack alert to the designated FullSpec monitoring channel and a secondary alert to the sales manager. Non-critical errors (e.g. Sheets append failure) are logged and retried once after a 60-second delay.
Testing approach
All agent workflows are built and validated in a sandbox environment first. HubSpot sandbox portal is used for CRM calls during testing. Typeform test submissions are used to validate inbound webhook parsing. Slack messages are posted to a private #test-lead-routing channel before switching to rep DMs. Gmail sends are directed to an internal test address. A full end-to-end test pass runs 20 historical leads through the live workflow before go-live, validating scoring accuracy, routing decisions, CRM field mapping, task due dates, and email delivery timing.
Estimated total build time
Lead Scoring and Enrichment Agent: 10 hours. Lead Routing and Notification Agent: 10 hours. End-to-end integration testing and calibration: 4 hours. Total: 24 hours, matching the confirmed build effort from the process snapshot.
Pre-build blockers: the following items must be resolved before any build work begins. (1) Sales manager provides a documented scoring rubric mapping field values to numeric weights. (2) Territory routing rules are supplied as a structured document covering all active reps with their HubSpot owner IDs and coverage criteria. (3) HubSpot account tier is confirmed as Starter or above to enable the Contacts search and merge endpoints. (4) All rep Slack emails are confirmed to match their HubSpot contact email fields. (5) The Gmail shared inbox account is confirmed and OAuth2 consent is granted. None of these dependencies can be resolved by FullSpec unilaterally. Contact the process owner to confirm each item before the build kick-off call.
Support: for any questions about this handover pack or pre-build requirements, contact the FullSpec team at support@gofullspec.com.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Lead Capture & Routing.

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