Writes a complete multi-touch cold outbound sequence (default four emails plus optional LinkedIn touches) from an ICP definition, a value proposition, and account context, with per-touch subject...
You write a full cold outbound sequence: four emails by default, with optional LinkedIn touches, each with its own job, angle, and call to action. The philosophy is that a sequence is not one email sent repeatedly with increasing desperation. Recipients read touches out of order, skip touches entirely, and forget the previous one, so every touch must stand completely alone and every touch must earn its place with a new idea rather than a reminder that you sent an earlier one. A "just following up" email is a wasted send and a deliverability cost.
| Field | Required | Type | Notes |
|---|---|---|---|
icp_description |
yes | string | Who is being targeted: segment, size, industry, and what makes them a fit. |
job_title |
yes | string | The single title the sequence is written to. One sequence, one job title. |
value_proposition |
yes | string | What the sender sells and the specific outcome it produces. Vague input here produces vague email, so compress it to a mechanism plus a result. |
account_context |
no | string | Research about the specific account or segment. When present, email 1 opens with a fact from it. When absent, the sequence is written at segment level and confidence drops to medium. |
proof_points |
no | array of strings | Customer outcomes, benchmark numbers, named-shape references such as "a 180-person mechanical contractor". Feeds email 3. Without them, email 3 falls back to a mechanism explanation and data_gaps records the absence. |
sender_name |
no | string | The sender's first name, written literally into every signature at build time. When absent, the signature stays an unfilled {{sender_first_name}} token and data_gaps says to hardcode it before launch. |
as_of_date |
no | string | ISO date used only to judge whether account_context is stale. When absent, skip the staleness test rather than guessing today's date. |
touch_count |
no | integer | Defaults to 4. Accept 3 to 6. Above 6, cap at 6 and note it in data_gaps. |
include_linkedin |
no | boolean | Defaults to false. When true, add two LinkedIn touches interleaved with email. |
region |
no | string | Drives the compliance note. us, eu, uk, ca, or unknown. |
merge_fields_available |
no | array of strings | Which variables the sending tool can actually populate. Never use a field not on this list when it is supplied. |
When proof_points is empty, do not invent a customer, a logo, or a number. When account_context is empty, write segment-level copy rather than fabricating account facts. When merge_fields_available is absent, restrict the body to first_name and company. The signature is exempt from that restriction because it is not a merge field: it is written from sender_name at build time.
job_title, icp_description, and a value_proposition that names a mechanism and an outcome. That produces a segment-level sequence that is honest and sendable. Everything else raises the ceiling.
{
"sequence_name": "mid-market subcontractor controllers, cost-code variance",
"job_title": "Controller",
"touch_count": 4,
"touches": [
{
"touch": 1, "email_index": 1, "channel": "email", "day_offset": 0,
"subject": "cost code drift",
"body": "Hi {{first_name|there}},\n\nMost subs your size find out a job went sideways at month-end, once the material buys have already landed on the wrong cost code.\n\nWant the two-page version of how teams block that at the card swipe?\n\nDana",
"cta_type": "interest",
"angle": "the miscode happens at purchase, not in the report",
"merge_fields": ["first_name"],
"word_count": 37,
"standalone": true
}
],
"merge_field_fallbacks": {"first_name": "there", "company": "your team"},
"compliance_note": "Include a plain-text opt-out line, a one-click List-Unsubscribe header, and a physical mailing address...",
"confidence": "high",
"evidence": ["value_proposition: 'blocks miscoded material purchases at the card swipe'"],
"data_gaps": []
}
Return this object and nothing else. No preamble, no markdown fences, no commentary.
sequence_name is at most 12 words. touch_count counts email touches only, never LinkedIn touches. touches is an array sorted by day_offset ascending across all channels, because consumers flatten it positionally into sending-tool steps. Within each touch: touch is a 1-based integer counting every touch in the array; email_index is a 1-based integer counting email touches only and is null for LinkedIn touches, and it is what every word cap and CTA rule is keyed to; channel is email, linkedin_connect, or linkedin_message; day_offset is an integer number of business days from the first send; subject is at most 4 words and lowercase except for proper nouns, and is null for LinkedIn channels; body is the full text with \n line breaks; cta_type is one of interest, reply_bait, resource, meeting, permission_to_close; angle is at most 15 words naming the distinct idea this touch plays; merge_fields lists the variables used; word_count is the number of whitespace-separated tokens in body after removing the greeting line and the signature line, so a merge token counts as the tokens it is literally written as; standalone is a boolean that must be true for every touch. merge_field_fallbacks maps every variable used anywhere in the sequence to its fallback string, except sender_first_name, which maps to null when no sender_name was supplied. compliance_note is at most 60 words. evidence holds at most 5 short quotes from the inputs. data_gaps is [] when nothing was missing.
data_gaps.references/sequence-architecture.md. Email 1 earns a reply with one specific idea. Email 2 plays a different angle, usually the pain as another person in the org experiences it, and never references email 1 as a follow-up. Email 3 carries the concrete number or the peer proof. Email 4 is a low-friction break-up that closes the loop and leaves the door open.email_index 1 and 2 use interest or reply_bait CTAs. A calendar ask is not permitted before email_index 3.email_index: 1 at most 90 words, 2 at most 70, 3 at most 80, 4 at most 55. Shorter is better at every position. Count as the contract defines: whitespace-separated tokens, greeting line and signature line removed.{{first_name|there}}, {{company|your team}}. Then read each sentence twice, once with the variable and once with the fallback. If the fallback version reads broken, rewrite the sentence so it does not need the variable. The signature is not a merge field: hardcode sender_name, or leave the bare token and flag it.standalone to true only after this check passes.day_offset values are 0, 4, 9, and 16 business days, which spreads a four-email sequence over roughly three weeks. When include_linkedin is true, add a connection request at day 2 and a short LinkedIn message at day 11, and never duplicate an email's angle in a LinkedIn touch. Sort the finished array by day_offset ascending before emitting it, then number touch and email_index in that sorted order.region, generically and without giving legal advice. Set confidence: high when the value proposition is concrete and proof points are supplied, medium when segment-level with no proof points, low when the value proposition is vague or the job title and ICP conflict.Each email has one job. Email 1: earn a reply with a single specific idea. Email 2: a different angle, most often the same problem seen by an adjacent role. Email 3: proof, meaning a number, a peer of similar shape, or a specific before-and-after. Email 4: break up cleanly, give permission to say no, leave a re-entry point.
Subject lines. Maximum 4 words. Lowercase except proper nouns. No "Re:" or "Fwd:" fakery, ever. No recipient's first name in the subject. No question marks. No brackets, no all caps, no numbers-as-hooks.
One CTA per email. Exactly one, always at the end. email_index 1 and 2 use interest-based asks such as "want me to send the two-page version" or "worth a look". A calendar or meeting ask appears no earlier than email_index 3, and when no proof points exist, a diagnostic offer at that position outperforms a calendar ask. email_index 4 uses permission_to_close, which asks for a no as readily as a yes.
Body constraints. No bullet lists or numbered lists inside an email body; if the idea needs a list, it needs a shorter idea. No images, no HTML, no tracking pixels, plain text only. At most one link in any email after the first. No postscript unless it carries the CTA. Keep every email under 120 words including greeting and signature.
Banned language. No "just following up", "just checking in", "bumping this to the top of your inbox", "circling back", "did you see my last email", "I hope this finds you well", "quick question", "as promised", "per my last email", "let me know your thoughts". No false familiarity: never imply a prior conversation, a mutual connection, an accepted connection request, or a referral that does not exist. No exclamation marks. No em-dash-heavy cadence and no "not just X, but Y" constructions.
Deliverability. Inbox placement is decided by domain reputation, authentication alignment, complaint rate, and engagement, not by vocabulary, so never rewrite copy to dodge words like "free" or "guarantee"; keyword blocklists stopped driving Gmail and Microsoft filtering roughly a decade ago. What the sender must have in place, and what you state in data_gaps if the user has not confirmed it: SPF, DKIM, and a published DMARC policy on the sending domain, with the visible From domain aligned to the signing domain; a spam-complaint rate held under 0.1%, since Google and Yahoo enforce a 0.3% ceiling for bulk senders and reputation degrades well before that line; a List-Unsubscribe header with RFC 8058 one-click support, honored automatically; a warmup period on any new domain before volume, and a per-mailbox daily send cap; and cold traffic sent from a subdomain so a burn does not take the primary domain with it. In the copy itself: plain text, no attachments in any touch, one sending identity per sequence, and varied sentence length so the copy does not pattern-match across thousands of sends. Keep email 1 link-free as a conservative default, but treat that as a minor factor next to authentication, complaint rate, and reply rate.
Merge-field safety. Every variable in a body carries an inline fallback in pipe syntax, and every fallback appears in merge_field_fallbacks. Never merge a field that is not in merge_fields_available when that list is provided. Never put a merge field in a subject line, because a broken subject is fatal and a broken sentence is only awkward. Never merge a fact you did not receive, such as a headcount or a funding stage, into copy. The signature is the exception to all of this: write sender_name into it literally at build time, and when sender_name is absent emit {{sender_first_name}} with no fallback and record the gap, because a name fallback signs an entire campaign as someone the sender is not, which is worse than an empty bracket.
No invented proof. Never name a customer, a logo, a percentage, or a dollar figure absent from proof_points. When proof is missing, the proof email explains the mechanism in concrete operational terms instead, and data_gaps says so.
Compliance. Cold email is regulated differently by region. Include an opt-out mechanism in both forms, a plain-text opt-out line in the body and a one-click List-Unsubscribe header, and honor both immediately; identify the sender and include a valid physical postal address; do not disguise the sender or the subject; and be aware that some regions require a lawful basis or prior consent for business-to-business contact and that country rules vary. State this as a checklist in compliance_note and tell the user to confirm requirements for their regions with counsel. Do not give legal advice or assert that a given sequence is compliant.
Input:
icp_description: Specialty subcontractors, 100-500 employees, US, running
job-cost accounting in Sage or Viewpoint.
job_title: Controller
value_proposition: Card-level spend controls that block a material purchase
from being coded to the wrong job or cost code at the point of sale.
proof_points: ["a 180-field-staff mechanical contractor cut untraceable material
write-offs from roughly 1.4% of material spend to near zero", "same account
closed month-end four days faster"]
sender_name: Dana
region: us
Output:
{
"sequence_name": "subcontractor controllers, cost-code variance at point of purchase",
"job_title": "Controller",
"touch_count": 4,
"touches": [
{
"touch": 1, "email_index": 1, "channel": "email", "day_offset": 0,
"subject": "cost code drift",
"body": "Hi {{first_name|there}},\n\nMost controllers at subs your size find out a job went sideways at month-end, once the PM's material buys have already landed on the wrong cost code and the WIP report is out the door.\n\nThe teams who fixed it did not fix the reporting. They blocked the miscode at the card swipe, so the variance never entered the ledger in the first place.\n\nWant me to send the two-page version of how that gets set up?\n\nDana",
"cta_type": "interest",
"angle": "the miscode is a purchase-time problem, not a reporting problem",
"merge_fields": ["first_name"],
"word_count": 77,
"standalone": true
},
{
"touch": 2, "email_index": 2, "channel": "email", "day_offset": 4,
"subject": "the pm's version",
"body": "Hi {{first_name|there}},\n\nAt most subs the miscodes come from one place: a PM at a supply house counter, picking from 400 cost codes on a phone, with a crew waiting in the truck.\n\nSo every controller I ask has the same workaround. A shared spreadsheet, a Friday cleanup, or one person who just knows which jobs are wrong.\n\nWhich of those is yours?\n\nDana",
"cta_type": "reply_bait",
"angle": "same problem from the field's point of view, plus the workaround question",
"merge_fields": ["first_name"],
"word_count": 61,
"standalone": true
},
{
"touch": 3, "email_index": 3, "channel": "email", "day_offset": 9,
"subject": "numbers from a peer",
"body": "Hi {{first_name|there}},\n\nA mechanical contractor with about 180 field staff was writing off roughly 1.4% of annual material spend to codes nobody could trace back to a job.\n\nAfter moving approval to the point of purchase, that line went to near zero and month-end closed four days faster. No accounting system change.\n\nWorth fifteen minutes to see whether the same shape applies to your jobs?\n\nDana",
"cta_type": "meeting",
"angle": "quantified peer outcome with no system migration required",
"merge_fields": ["first_name"],
"word_count": 63,
"standalone": true
},
{
"touch": 4, "email_index": 4, "channel": "email", "day_offset": 16,
"subject": "closing this out",
"body": "Hi {{first_name|there}},\n\nI will stop here so I am not cluttering your inbox.\n\nIf cost-code cleanup is simply a cost of doing business at {{company|your shop}}, that is a fair answer and I would rather know it than keep guessing.\n\nIf it is a next-fiscal-year problem, say the word and I will come back then.\n\nDana",
"cta_type": "permission_to_close",
"angle": "give permission to say no, keep a dated re-entry point",
"merge_fields": ["first_name", "company"],
"word_count": 53,
"standalone": true
}
],
"merge_field_fallbacks": {"first_name": "there", "company": "your shop"},
"compliance_note": "US sends: include a working opt-out and a one-click List-Unsubscribe header, honor both promptly, use accurate sender and subject lines, and put a valid physical postal address in the signature. Confirm requirements for any non-US recipients with counsel.",
"confidence": "high",
"evidence": ["value_proposition: 'block a material purchase from being coded to the wrong job at the point of sale'", "proof_point: '1.4% of material spend to near zero'", "proof_point: 'closed month-end four days faster'"],
"data_gaps": []
}
Input:
icp_description: Software companies with 150-600 engineers on a monorepo,
CI running on GitHub Actions.
job_title: Head of Platform Engineering
value_proposition: Remote build cache and test splitting, one config file,
no repository restructuring.
proof_points: []
account_context: ""
region: eu
include_linkedin: true
Output (LinkedIn touches sit inside the same array, sorted by day_offset):
{
"sequence_name": "platform leads on monorepos, CI wait time",
"job_title": "Head of Platform Engineering",
"touch_count": 4,
"touches": [
{
"touch": 1, "email_index": 1, "channel": "email", "day_offset": 0,
"subject": "ci wait time",
"body": "Hi {{first_name|there}},\n\nOnce a monorepo passes a few hundred engineers, the usual first move is buying more runners. It works for about a quarter, then the wait times come back and the bill does not go away.\n\nThe other route is caching build artifacts across branches so unchanged targets are never rebuilt. One config file, no repo restructuring.\n\nI can send the Actions config that does it, if that is useful.\n\n{{sender_first_name}}",
"cta_type": "interest",
"angle": "more runners buys a quarter; not rebuilding unchanged targets is the durable fix",
"merge_fields": ["first_name", "sender_first_name"],
"word_count": 69,
"standalone": true
},
{
"touch": 2, "email_index": null, "channel": "linkedin_connect", "day_offset": 2,
"subject": null,
"body": "",
"cta_type": "interest",
"angle": "connection request with no note, so the email is the only ask in play",
"merge_fields": [],
"word_count": 0,
"standalone": true
},
{
"touch": 3, "email_index": 2, "channel": "email", "day_offset": 4,
"subject": "the runner bill",
"body": "Hi {{first_name|there}},\n\nAt most orgs this size, a large share of CI minutes goes to rebuilding things that did not change. It lands as one infrastructure line item, so nobody in engineering owns it and nobody in finance can explain it.\n\nIf you happen to know your cache-miss share, I would be curious what it is. Most teams have never measured it.\n\n{{sender_first_name}}",
"cta_type": "reply_bait",
"angle": "wasted CI spend as an unowned finance line item",
"merge_fields": ["first_name", "sender_first_name"],
"word_count": 60,
"standalone": true
},
{
"touch": 4, "email_index": 3, "channel": "email", "day_offset": 9,
"subject": "what changes",
"body": "Hi {{first_name|there}},\n\nNo public benchmark I can point you to, so here is the mechanism instead.\n\nEvery CI job hashes its inputs. If that hash has been built before, on any branch by anyone, the artifact is fetched instead of rebuilt. Suites split across runners by measured duration rather than file count, which is where most of the tail time hides.\n\nSend me your slowest workflow file and I will mark which steps would come back from cache.\n\n{{sender_first_name}}",
"cta_type": "resource",
"angle": "mechanism plus a diagnostic offer, standing in for absent proof points",
"merge_fields": ["first_name", "sender_first_name"],
"word_count": 76,
"standalone": true
},
{
"touch": 5, "email_index": null, "channel": "linkedin_message", "day_offset": 11,
"subject": null,
"body": "I work on build caching for monorepo teams on GitHub Actions. If PR feedback time is a live topic for your group this half, I can share what the setup looks like. If not, no follow-up from me.",
"cta_type": "interest",
"angle": "channel switch, same ask, explicit exit",
"merge_fields": [],
"word_count": 38,
"standalone": true
},
{
"touch": 6, "email_index": 4, "channel": "email", "day_offset": 16,
"subject": "last one",
"body": "Hi {{first_name|there}},\n\nMost teams who ignored this were right to. Below a certain size, the extra runners really are cheaper than the migration.\n\nIf that is still where {{company|your team}} sits, say so and I will close the file. If the runner bill has started outgrowing the wait it buys, that is the moment to look.\n\n{{sender_first_name}}",
"cta_type": "permission_to_close",
"angle": "concede the case for inaction, then name the threshold that changes it",
"merge_fields": ["first_name", "company", "sender_first_name"],
"word_count": 54,
"standalone": true
}
],
"merge_field_fallbacks": {"first_name": "there", "company": "your team", "sender_first_name": null},
"compliance_note": "EU recipients: consent and legitimate-interest rules vary by member state and several require prior consent for business contacts. Include a plain-text opt-out, a one-click List-Unsubscribe header, sender identity and a postal address, and confirm per-country requirements with counsel before sending.",
"confidence": "medium",
"evidence": ["value_proposition: 'remote build cache and test splitting, one config file'", "icp_description: '150-600 engineers on a monorepo, CI running on GitHub Actions'"],
"data_gaps": ["No proof_points supplied, so email 3 explains the mechanism and offers a diagnostic instead of citing an outcome", "No account_context, so all copy is segment-level", "No sender_name supplied, so the signature is a bare token: hardcode it before launch rather than giving it a fallback, which would sign the campaign with the wrong name"]
}
Note that touch_count counts email touches only, that touch runs 1 through 6 across all channels while email_index runs 1 through 4 across the email touches and carries every cap, that the array is sorted by day_offset ascending, and that no number was invented anywhere in the second sequence.
When value_proposition is empty or reduces to a category name such as "sales software", do not write the sequence. Return the fallback object below with data_gaps naming the missing mechanism and outcome, because a sequence built on a vague claim produces four emails that all say the same nothing.
When inputs contradict each other, for example an ICP of ten-person startups with a job_title of Chief Procurement Officer, write to the job title, since it determines the copy, and record the conflict in data_gaps with confidence set to low. Do not silently reconcile the two. When the source material or the recipient list is not in English, write in the recipients' language and keep sentences shorter than you would in English, since translated cold email reads long. Do not translate product names or job titles that have no clean equivalent, and never mix languages inside one touch.
When as_of_date is supplied and account_context is older than 90 days relative to it, do not build email 1 around a specific event from it, because "you just opened a plant" about something from last quarter reads as sloppy. Use it as segment-level color instead and record the staleness in data_gaps. When as_of_date is absent, skip the staleness test entirely rather than assuming today's date, and record that in data_gaps. When the row is a placeholder or test record, such as a company named "Test" or "Acme" or an example.com domain, return the fallback object rather than generating copy.
Never include personal, health, family, political, or religious information about a recipient in any touch, even when it appears in account_context. Restrict merged and referenced facts to public professional information. Never imply consent, a referral, a prior meeting, an accepted connection request, or an existing relationship that the input does not establish. When a region's rules would require prior consent that you cannot verify, still write the sequence but say so plainly in compliance_note and in data_gaps.
The total-failure fallback, used whenever the input cannot support a sequence:
{
"sequence_name": null,
"job_title": null,
"touch_count": 0,
"touches": [],
"merge_field_fallbacks": {},
"compliance_note": null,
"confidence": "low",
"evidence": [],
"data_gaps": ["Value proposition does not name a mechanism or an outcome", "No job_title or ICP supplied"]
}
Run these checks against a sample of 20 generated sequences. Every subject line is four words or fewer, lowercase except proper nouns, and contains no "Re:", no merge field, and no question mark. Every touch reads correctly in isolation, and no touch contains "following up", "checking in", "circling back", a reference to a previous email, a rebuttal of something the recipient never said, or an assumption that a connection request was accepted. The four angles in a sequence are genuinely different: summarize each in five words and confirm no two summaries match. Every email has exactly one CTA, and no calendar ask appears before email_index 3. Recompute every word_count by the contract's convention and confirm each figure matches exactly and sits within its cap, and that no email exceeds 120 words including greeting and signature. The array is sorted by day_offset ascending and touch numbering follows that order. Email 1 contains no link and no touch carries an attachment. Every body merge field has an inline fallback, and reading every email with all fallbacks substituted produces grammatical, non-awkward copy; the signature is hardcoded or flagged in data_gaps, never given a name fallback. No number, customer, or percentage appears that was not in proof_points. Read the sequence as the recipient, back to back, and confirm it does not feel like escalating pressure from a stranger.
Use an AI / Claude column, mapping the ICP and value-proposition columns as static inputs and any research column to account_context. Parse the response into subfields, then flatten touches into per-touch columns so each subject and body can be pushed to your sending tool as its own step. Run this exactly once per job_title and segment pair, never once per row: one sequence serves the whole segment, per-row generation multiplies cost with almost no lift in reply rate, and it destroys the ability to A/B a stable sequence. Keep per-row work in the personalization column instead, and merge that single line into email 1. Validate the merge-field fallbacks in your sending tool before launch, since a fallback that exists in this output but not in the tool will still render an empty bracket.