sequence-load
Sequence Load Skill
<quick_start>
Trigger: Monday 7:15 AM ET (runs after prospect-refresh at 6:30 AM)
Manual Trigger: "Load prospects into sequences" or "Enroll new leads"
Dependencies: Requires Apollo credits, prospect-refresh output, HubSpot portal 21530819
Output: Enrollment confirmation per prospect, sequence assignment, contact IDs for HubSpot creation
</quick_start>
<success_criteria>
- Read prospect-refresh output (30 net-new prospects from Gmail drafts + HTML report)
- Query HubSpot to verify prospect not already enrolled in any sequence
- Validate phone number exists (from prospect-enrich or Apollo)
- Apply Golden Rules (skip customers, channel, device owners, NEVER ATL)
- Find target Apollo sequence(s) by ICP vertical
- Get email account ID (tim@epiphan.com or primary)
- Batch add prospects to sequence (max 100 per request)
- Confirm enrollments (check Apollo contact.sequence_id)
- Create HubSpot contacts if not exist (pull contact IDs for HubSpot sync)
- Report: total enrolled, per-sequence breakdown, any failures </success_criteria>
Stage 1: Read Prospect Refresh Output
Input Source: prospect-refresh HTML report + Gmail drafts
Extract from HTML Report:
- Prospect name, email, title, company, vertical, ATL/BTL tier
- Apollo person_id, organization_id, phone
- Company revenue, headcount, funding
Validation:
- Email format is valid
- Phone exists (from enrichment)
- Vertical is one of: Higher Ed, Courts, Gov, Corporate AV, Healthcare, HoW, K-12
Output: Structured list of 30 prospects (or fewer if filtering applied)
Stage 2: Deduplicate Against HubSpot + Apollo
Step A: Check HubSpot for existing contact
MCP Tool: search_crm_objects (HubSpot)
objectType: "contacts"
filterGroups: [{
filters: [
{ propertyName: "email", operator: "EQ", value: prospect.email }
]
}]
properties: ["email", "phone", "hs_lead_status"]
Decision Logic:
- If contact exists + NOT in "Sequenced" or "Active Sequence" status → REUSE contact (get contact ID)
- If contact exists + in sequence → SKIP (avoid re-enrollment)
- If contact does not exist → PROCEED to Stage 3 (create new)
Step B: Check Apollo for duplicate enrollments
MCP Tool: apollo_contacts_search
q_keywords: prospect.email
per_page: 10
Decision Logic:
- If Apollo contact exists + already in a sequence → SKIP
- If Apollo contact exists + no sequence → PROCEED (re-enroll)
- If Apollo contact does not exist → PROCEED to Stage 3 (create new)
Dedup Output: List of unique prospects (typically 60-70% of input after dedup)
Stage 3: Validate Golden Rules + Phone Exists
Golden Rules (Hard Exclusions):
Skip if:
- Email domain matches known customer domain (cross-check crm_customers)
- Contact tagged "Channel Partner" in HubSpot
- Contact tagged "Device Owner" in HubSpot
- Company in "Product Page Engagers" tag
- Title in NEVER ATL list: Warehouse Manager, Network Manager, Systems Admin, AV Technician, Graphic Design Instructor, Program Administrator, Web Designer, Classroom Support, Lab Coordinator, Maintenance, Building Engineer, Multimedia Services Manager, Video Production Specialist, Streaming Crew
Phone Validation:
- Phone must be non-null (from Apollo enrichment or prospect-enrich)
- Format: +1-XXX-XXX-XXXX (US) or +1-XXX-XXX-XXXX (Canada)
- If phone missing → LOG as "Phone Missing" but DO NOT ENROLL (requires phone for Apollo compliance)
Output: Verified prospect list (typically 80-90% after phone validation)
Stage 4: Map Prospects to Target Sequences
MCP Tool: apollo_emailer_campaigns_search
Search for sequences by vertical + template:
| Vertical | Sequence Name Pattern | Target Sequence ID |
|---|---|---|
| Higher Ed | BDR_HigherEd_OutboundX | [search by name] |
| Courts | BDR_Courts_OutboundX | [search by name] |
| Government | BDR_Government_OutboundX | [search by name] |
| Corporate AV | BDR_CorporateAV_OutboundX | [search by name] |
| Healthcare | BDR_Healthcare_OutboundX | [search by name] |
| Houses of Worship | BDR_HoW_OutboundX | [search by name] |
| K-12 | BDR_K12_OutboundX | [search by name] |
Query Logic:
per_page: 100
# Search returns: id, name, status, num_contacts, num_enrolled
Sequence Selection Rule:
- Map prospect.vertical to sequence_name_pattern
- Choose sequence with LOWEST current enrollment (load balancing)
- Fallback: default "BDR_Outbound_General" if vertical-specific not found
Output: Prospect + sequence_id mapping
Stage 5: Create Apollo Contacts (If New)
MCP Tool: apollo_contacts_search
For new prospects (not found in Stage 2):
per_page: 10
q_keywords: prospect.email
If not found, create contact via:
MCP Tool: apollo_contacts_create
email: prospect.email
first_name: prospect.first_name
last_name: prospect.last_name
title: prospect.title
organization_name: prospect.company
phone: prospect.phone (if available)
label_names: [prospect.vertical, "BDR_Prospect", "2026_Q1"]
Output: New Apollo contact ID (apollo_contact_id)
Stage 6: Get Email Account ID
MCP Tool: apollo_email_accounts_index
Query all linked email accounts:
# Returns: [{ id, email, status, daily_send_limit, ... }]
Selection Logic:
- Look for email containing "epiphan" or "tkipper"
- Use primary email account if multiple
- If none found → ERROR (cannot proceed without email account)
Output: email_account_id (e.g., "6633baaece5fbd01c791d7ca")
Stage 7: Batch Add to Sequences
MCP Tool: apollo_emailer_campaigns_add_contact_ids
For each sequence (group prospects by sequence_id):
id: sequence_id (from Stage 4 mapping)
emailer_campaign_id: sequence_id (same as id)
send_email_from_email_account_id: email_account_id (from Stage 6)
contact_ids: [contact_id_1, contact_id_2, ..., contact_id_N] (max 100 per batch)
sequence_active_in_other_campaigns: true (allow multi-sequence enrollment)
sequence_unverified_email: false (require verified emails)
status: "active" (start sequence immediately)
Batch Logic:
- Group prospects by target sequence
- Send 100 contacts max per API call
- Retry if rate limited
Expected Success Rate: 95%+ (most failures due to email validation)
Stage 8: Confirm Enrollments
MCP Tool: apollo_contacts_search
For each enrolled contact, verify enrollment:
q_keywords: contact.email
per_page: 1
Check response:
- contact.sequences (should contain sequence_id from Stage 7)
- contact.last_sequence_enrollment_date (should be "today")
- contact.status (should be "active")
Log Enrollment:
✓ jane@acme.com → BDR_HigherEd_Outbound (sequence_id: abc123)
✗ bob@example.com → Phone validation failed
✗ carol@company.com → Already enrolled in BDR_Corporate_AV
Stage 9: Create HubSpot Contacts (Sync)
For newly created Apollo contacts, create HubSpot counterparts:
MCP Tool: manage_crm_objects (HubSpot createRequest)
objectType: "contacts"
properties: {
firstname: prospect.first_name,
lastname: prospect.last_name,
email: prospect.email,
phone: prospect.phone,
jobtitle: prospect.title,
company: prospect.company_name,
lifecyclestage: "subscriber",
hs_lead_status: "open",
custom_apollo_contact_id: apollo_contact_id,
custom_apollo_sequence: sequence_name,
custom_atl_btl_tier: prospect.atl_btl_tier,
custom_prospect_vertical: prospect.vertical,
custom_prospect_icp_score: prospect.icp_score
}
associations: [
{
targetObjectId: company_id, # Find or create company in HubSpot
targetObjectType: "companies"
}
]
Company Association Logic:
- Search HubSpot for company by domain
- If found: use company_id from existing record
- If not found: create company via manage_crm_objects
Output: Created contact IDs (to link Gmail drafts)
Stage 10: Output Enrollment Report
Format: Markdown table + summary stats
Enrollment Table:
| Prospect | Phone | Vertical | Sequence | Apollo ID | HubSpot ID | Status | |
|---|---|---|---|---|---|---|---|
| Jane Smith | jane@acme.com | +1-555-0123 | Higher Ed | BDR_HigherEd_1 | apollo-123 | hs-456 | ✓ Enrolled |
| Bob Jones | bob@example.com | null | Courts | - | - | - | ✗ Phone missing |
| Carol White | carol@corp.com | +1-555-0124 | Corp AV | BDR_CorporateAV_1 | apollo-789 | hs-012 | ✓ Enrolled |
Summary Stats:
- Total prospects processed: X
- Successfully enrolled: Y (Y% of X)
- Phone validation failures: Z
- Duplicate/already enrolled: W
- HubSpot synced: Y (new contacts)
- Per-sequence breakdown:
- BDR_HigherEd_1: 5 enrolled
- BDR_Courts_1: 4 enrolled
- BDR_CorporateAV_1: 5 enrolled
- etc.
Next Steps: Prospects now enrolled in Apollo sequences. Gmail drafts (from prospect-refresh) ready for manual review + send-from-draft workflow.
Emit Outcome Sidecar
As the final step, write to ~/.claude/skill-analytics/last-outcome-sequence-load.json:
{"ts":"[UTC ISO8601]","skill":"sequence-load","version":"1.0.0","variant":"default",
"status":"[success|partial|error]","runtime_ms":[estimated ms from start],
"metrics":{"contacts_loaded":[n],"sequences_updated":[n],"duplicates_skipped":[n],"phone_validation_failures":[n]},
"error":null,"session_id":"[YYYY-MM-DD]"}
Use status "partial" if some stages failed but results were produced. Use "error" only if no output was generated.
Skill Metadata
Version: 1.0 Last Updated: 2026-03-19 Author: Tim Kipper Status: Production Integration: Apollo + HubSpot (portal 21530819) Tier: P1 (Core BDR Automation) Triggers: Scheduled (Monday 7:15 AM) + Manual ("Load sequences") Dependencies: prospect-refresh (6:30 AM) → sequence-load (7:15 AM) → morning-brief (7:30 AM)