Skip to content
Marketing
Skill

/07-campaign-sheet

Turn a terminal records.jsonl into an owner-readable campaign sheet plus a HubSpot-import-shaped CSV - the chain's exit door. Use when a run is ready to hand to an owner: "build the campaign sheet", "export these for HubSpot", "who do I contact first", or as the final step of

From plugin
headless-gtm
2917 skills

Claude Code

$ npx -y skills add Zevenue/headless-gtm --skill 07-campaign-sheet --agent claude-code

SKILL.md

07-campaign-sheet.SKILL.md
name: 07-campaign-sheet
description: >-
  Turn a terminal records.jsonl into an owner-readable campaign sheet plus a
  HubSpot-import-shaped CSV - the chain's exit door. Use when a run is ready to
  hand to an owner: "build the campaign sheet", "export these for HubSpot", "who
  do I contact first", or as the final step of run-first-campaign. Reads
  whatever stage produced the records (qualified, signal-ranked, or fully
  resolved) and degrades gracefully on missing fields - no email column until 06
  has run. Nothing sends and nothing downstream reads its output: the sheet and
  CSV are the human deliverable, approved on screen. To add verified emails
  first, run 06-resolution-email-person; to rank, 05-signal-builder.

Campaign sheet (07)

The last step in the chain. Everything upstream produced `records.jsonl`; this turns those records into what an owner actually uses - a ranked "who to contact first and why" sheet, plus a CSV shaped for a one-click HubSpot import.

It **adds no fields to the chain and nothing reads its output.** The CSV is a derived, human-facing export (`headless-gtm-shared/CONVENTIONS.md`) - the canonical record stays `records.jsonl`, and re-running any upstream skill never depends on this sheet.

What it reads

A terminal `records.jsonl` from any stage. The script tolerates every shape and degrades instead of failing:

  • **Company name** from `company` (or `name`), **site** from `website` (or

`https://` + `domain`), **phone / city** from wherever the discovery step put them.

  • **Ranking** from 05's `signals[]` - the highest-score signal per record

decides call order; its `signal_type`, `signal_sentence`, and `source_url` become the "why now" line, and its `approach` (or `fallback_approach`) the recommended angle. Records with no signals stay in the sheet, ranked last.

  • **Contacts** from 06's `person_name` / `matched_title` / `email` /

`verification_status`. When 06 has not run, the contact columns are dropped entirely rather than shipped empty.

  • Rows with no website ride through on their listing fields - the sheet does not

drop them (a registry row with `domain` empty is still a real target).

What it writes

`runs/<run-id>/` (via `common.runs_base()`, step prefix `07`):

  • **`campaign-sheet.md`** - owner-readable. Ranked cards for the top targets

(company, city, why now with source, approach, contact or "not resolved"), then a compact table for the rest, then a "before you send" note.

  • **`campaign-sheet.csv`** - HubSpot default contact-import headers: `First

Name, Last Name, Email, Company Name, Website URL, Phone Number, Lifecycle Stage, Notes`. Every row is set to **Lifecycle Stage = Lead**; `Notes` carries the top signal and the recommended approach. First/Last/Email appear only when at least one record is resolved, and a `Listing URL` column appears only when at least one record carries one (directory-sourced runs) - so a phone-only registry row still exports a working link. The headers are HubSpot's defaults because it is the most common import target; every field is a plain column, so any other CRM (Salesforce, Pipedrive, Attio) takes the same file after a one-time header remap on import.

Run it

python3 scripts/campaign_sheet.py --records ./runs/<run-id>/records.jsonl --title "HVAC - Ontario"

Stdlib only - no API keys, no network. It runs on a synthetic fixture with zero accounts, so it is safe to demo before any real data exists.

The approval step (must hold)

Present both files on screen. **Nothing is send-ready until the owner approves the sheet there.** Nothing in this repo sends email - if asked to "just send it", say so plainly and point at the approved sheet and the drafted sequence. The import, and any send, is the owner's step after approval.

Optional: stage into HubSpot (only when a connector is present)

The default path is the CSV import - the owner uploads `campaign-sheet.csv` in HubSpot and maps the columns (all default properties, so mapping is automatic).

If a HubSpot MCP or connector is available in the session, offer an alternative: stage the approved rows directly as contacts with `lifecyclestage = lead`. Before any write, confirm the batch size and get an explicit go-ahead, then stage in batches and report what was created. Never create or modify HubSpot records without that on-screen approval, and never stage rows the owner has not approved on the sheet. If no connector is present, do not ask for one - the CSV is the complete path.

Output

End with a one-paragraph recap: rows written, how many carry a resolved contact, the top 3 targets with their one-line reason, and the paths to the two files.

Read more
@zevenue
Ships withheadless-gtm

GTM without the SaaS layer. An outbound pipeline built as agent skills for Claude Code and Codex: describe an ICP in plain English and the chain takes it from company discovery to verified, signal-ranked contacts - every step running on raw vendor APIs, not

Stars on GitHub

Momentum

stars in total
29
Star history of Zevenue/headless-gtm on GitHub, a chart by star-history.com

Other skills on headless-gtm.

00-gtm-router

00-gtm-router

Plan and run the full API-first GTM chain - the orchestrator (step 00) that turns an ICP description plus optional budget,…

01-icp-qualify

01-icp-qualify

The qualification gate of the GTM chain - judges every discovered company against the client's ICP before any paid enrichment, so…

01-prospeo-discover

01-prospeo-discover

B2B company discovery and list building via the Prospeo search API. Use when the user wants to find companies from an ICP, build…

01-prospeo-lookalike

01-prospeo-lookalike

Seed-based B2B company discovery via the Prospeo company-lookalike API. Use when the user has example companies and wants more…

More skills from other plugins.

seo

seo

Comprehensive SEO analysis for any website or business type. Full site audits, single-page analysis, technical SEO (crawlability,…

@agricidaniel@agricidanielInstall Skill
brag

brag

Turn the current project website into a short, polished, shareable launch video using Hyperframes. Use when someone says "/brag",…

brag2w21k
@latent-spaces@latent-spacesInstall Skill
geo-audit

geo-audit

Full website GEO+SEO audit with parallel subagent delegation. Orchestrates a comprehensive Generative Engine Optimization audit…

Questions about 07-campaign-sheet.

Which coding agents can use 07-campaign-sheet?
07-campaign-sheet installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.