Turn Buyer Intent Signals into Outbound Sequences
The problem
Intent data shows which accounts are researching your category, but it sits in a dashboard nobody checks daily, so the signal is stale before a rep acts on it.
Implementation steps
- 1
Pick the signals you will act on, for example G2 Buyer Intent category or competitor page views, or Bombora topic surges, and send each new signal to a Clay webhook through the provider's webhook, a Zapier relay, or a scheduled export
- 2
Enrich the account with domain, headcount, industry, and region, and drop accounts outside your ICP before spending credits on contacts
- 3
Check HubSpot or Salesforce and skip customers, open opportunities, and accounts in an active sequence, creating a task for the account owner instead
- 4
Find one to three people in the buying roles for your product with Apollo or a Clay waterfall, and verify their emails
- 5
Write a short opener with an AI step that refers to the problem area behind the signal, without saying you tracked their research
- 6
Push the contacts into an Instantly or Smartlead sequence and log the signal type, topic, and date on the CRM company so reps can see why the account was contacted
Template outline
The structure to recreate in your own workspace. Field names and thresholds are starting points.
Clay Workflow (webhook trigger, one run per signal):
1. Webhook payload: company_domain, signal_type, topic, signal_date
2. Enrich company: headcount, industry, country
3. ICP filter: headcount and industry in range, region served
4. CRM lookup by domain: skip customers, open deals, active sequences (task to owner instead)
5. Find people: buying-role titles, 1-3 per account, verified emails
6. AI opener prompt: "Write one sentence for {{first_name}}, {{title}}, related to {{topic}}. Do not mention intent data or research activity."
7. Instantly or Smartlead API: add to sequence with {{opener}}
8. CRM update: "Last intent signal" = {{signal_type}}: {{topic}} on {{signal_date}}
Output: accounts showing intent move into a sequence as soon as the signal arrives, with the reason logged in the CRMBuild it in Clay
Copy these commands into your own Clay CLI. They run in your Clay workspace under your login. Nothing runs on our side, and this site never sees your Clay data or login.
This builds the play as a Clay workflow with a webhook trigger. Each intent signal your provider or a relay POSTs becomes one run: enrich the account, find the best-fit contact, write an opener, and add the contact to a sequence. The Clay CLI cannot create tables, so this build uses a workflow. It does not check your CRM for customers or open deals, so filter those before signals reach the webhook.
Not tested by GTM Playbooks
This build was written from the Clay CLI v1.3.1 help output, not from a run. Anything marked “Verify in your workspace”, and every line tagged VERIFY in the script, uses a shape that help does not document. Workflow runs can spend your Clay credits.
Part 1: Before you start
1.Install the Clay CLI
Use version 1.3.1 or later, installed from Clay's own instructions. This page does not include an install command because the CLI reference it was written from does not list one. You also need jq, which the script uses to read the JSON the CLI prints.
2.Log in to your workspace
Browser login is the default. Use the device flow on a headless machine. A static key in the CLAY_API_KEY environment variable also works. Workflow commands need the terracotta:cli scope, so an auth error that names it means your login cannot manage workflows.
Log in with a browser
clay loginLog in on a headless machine
clay login --deviceConfirm who you are logged in as
clay whoami3.Pick the workspace to build in
The workflow is created in your active workspace. Switch first if you belong to more than one.
List workspaces
clay workspaces listShow the active workspace
clay workspaces currentSwitch workspace
clay workspaces switch <workspaceId>4.Find your action keys
Action package ids and keys are specific to your workspace. Save the catalog, search it for company enrichment, a find-people action, an AI text action, and your sequencer, then paste their ids into the top of the script. Skip "clay workflows actions test": it spends credits.
Save the action catalog
clay workflows actions list > catalog.jsonSearch the catalog
grep -Eio '"[^"]*(enrich|find people|apollo|use ai|generate|instantly|smartlead)[^"]*"' catalog.json | sort -uSee an action's inputs and outputs
clay workflows actions schema <packageId> <actionKey>5.Decide how signals reach the webhook
Some intent providers can POST to a webhook directly. Otherwise relay a scheduled export with Zapier or Make, sending one JSON object per signal with the fields in the sample payload.
6.Connect your sequencer and make a test campaign
Connect Instantly or Smartlead inside Clay and create an empty campaign for testing, so early runs do not land in a live sequence.
Part 2: Workflow spec
What the script builds: Intent signals to outbound.
Trigger
webhookYour intent provider, a Zapier or Make relay, or a small script POSTs one JSON object per signal to the trigger's webhook URL. The fields arrive as run inputs as-is.
Verify in your workspace: Whether your intent provider can POST to a webhook directly depends on the provider and your plan.
Inputs (webhook payload fields)
company_domain- Domain of the account showing intent
- example.com
signal_type- What happened, in your own labels
- category_research
topic- Category, topic, or competitor involved
- sales engagement software
signal_date- When the signal was recorded
- 2026-10-15
- 1
Enrich the account
actionPulls firmographics so the next step can skip accounts outside your ICP before spending credits on contacts.
- Reads
company_domain- Writes
headcountindustrycountry
- 2
Find the best-fit contact
actionFinds one person in the buying role for your product, with a work email.
- Reads
company_domainheadcount- Writes
first_namelast_nametitleemail
Verify in your workspace: One run handles one contact. The ICP filter before this step needs a run condition, which the CLI help does not document, so set it in the Clay UI.
- 3
Write the opener
actionAn AI text action writes one opening sentence tied to the problem area, not to the tracking.
- Reads
first_nametitletopic- Writes
opener
Prompt
Write one sentence, under 25 words, that opens a cold email to {{first_name}}, {{title}} at {{company_domain}}. Relate it to this problem area: {{topic}}. Do not mention intent data, research activity, or how you found them. No flattery and no em dashes.
- 4
Add the contact to a sequence
actionAdds the contact to your Instantly or Smartlead campaign with the opener as a custom variable.
- Reads
emailfirst_namelast_nameopenercampaign- Writes
Sequencer response
Verify in your workspace: Check that your sequencer appears in the catalog, and point it at a test campaign first.
Part 3: Build script
Creates the workflow, the webhook trigger, and four steps, then pauses so you can map step inputs and the ICP condition. It validates the graph, offers one test run, and asks before publishing.
Save it as build-intent-outbound.sh, fill in the 8 action ids at the top, then run bash build-intent-outbound.sh.
build-intent-outbound.sh
#!/usr/bin/env bash# Builds the "Intent signals to outbound" Clay workflow# in YOUR Clay workspace, using YOUR Clay CLI login.# Written against Clay CLI v1.3.1. Not run or tested by GTM Playbooks.# Lines tagged VERIFY use a shape the CLI help does not document.set -euo pipefail # Copy these from catalog.json (see "Find your action keys").ACCOUNT_PACKAGE_ID="" # Company enrichment (headcount, industry)ACCOUNT_ACTION_KEY=""CONTACT_PACKAGE_ID="" # Find people at a company by titleCONTACT_ACTION_KEY=""WRITER_PACKAGE_ID="" # AI text generationWRITER_ACTION_KEY=""SEQUENCER_PACKAGE_ID="" # Instantly or Smartlead: add leadSEQUENCER_ACTION_KEY="" # One input to test before real traffic. Use yourself or a teammate.TEST_ROW='{"company_domain":"","signal_type":"","topic":"","signal_date":""}' for v in ACCOUNT_PACKAGE_ID ACCOUNT_ACTION_KEY CONTACT_PACKAGE_ID CONTACT_ACTION_KEY WRITER_PACKAGE_ID WRITER_ACTION_KEY SEQUENCER_PACKAGE_ID SEQUENCER_ACTION_KEY; do [[ -n "${!v}" ]] || { echo "Set $v at the top of this script." >&2; exit 1; }donecommand -v clay > /dev/null || { echo "Clay CLI not found on PATH." >&2; exit 1; }command -v jq > /dev/null || { echo "jq is required to read CLI output." >&2; exit 1; }clay whoami > /dev/null || { echo "Not logged in. Run: clay login" >&2; exit 1; } # VERIFY: only "workflows create" documents an "id" field in its output.read_id() { local id id=$(jq -r '.id // empty' "$1") [[ -n "$id" ]] || { echo "No id found in $1. Open it and adjust read_id." >&2; exit 1; } echo "$id"} mkdir -p clay-intent-outboundcd clay-intent-outbound echo "1/8 Create the workflow"clay workflows create --name "Intent signals to outbound" > workflow.jsonWF_ID=$(read_id workflow.json)WF_URL=$(jq -r '.url // empty' workflow.json) echo "2/8 Add the webhook trigger"clay workflows triggers create "$WF_ID" --input '{"triggerType":"webhook","inputSchema":{"type":"object"}}' > trigger.jsonTRG_ID=$(read_id trigger.json) echo "3/8 Step 1: enrich the account"# VERIFY: help shows nodeType and name only; actionPackageId and actionKey are a guess.STEP1=$(jq -nc --arg p "$ACCOUNT_PACKAGE_ID" --arg k "$ACCOUNT_ACTION_KEY" '{nodeType: "tool", name: "Enrich the account", actionPackageId: $p, actionKey: $k}')clay workflows nodes create "$WF_ID" --input "$STEP1" > step1.jsonSTEP1_ID=$(read_id step1.json) echo "4/8 Step 2: find the best-fit contact"# VERIFY: action fields inside "step" are inferred from the documented utility example.STEP2=$(jq -nc --arg n "$STEP1_ID" --arg p "$CONTACT_PACKAGE_ID" --arg k "$CONTACT_ACTION_KEY" '{target: {type: "after-node", nodeId: $n}, step: {kind: "action", actionPackageId: $p, actionKey: $k}}')clay workflows nodes insert "$WF_ID" --input "$STEP2" > step2.jsonSTEP2_ID=$(read_id step2.json) echo "5/8 Step 3: write the opener"# VERIFY: same inferred insert shape as step 2.STEP3=$(jq -nc --arg n "$STEP2_ID" --arg p "$WRITER_PACKAGE_ID" --arg k "$WRITER_ACTION_KEY" '{target: {type: "after-node", nodeId: $n}, step: {kind: "action", actionPackageId: $p, actionKey: $k}}')clay workflows nodes insert "$WF_ID" --input "$STEP3" > step3.jsonSTEP3_ID=$(read_id step3.json) echo "6/8 Step 4: add the contact to a sequence"# VERIFY: same inferred insert shape as step 2.STEP4=$(jq -nc --arg n "$STEP3_ID" --arg p "$SEQUENCER_PACKAGE_ID" --arg k "$SEQUENCER_ACTION_KEY" '{target: {type: "after-node", nodeId: $n}, step: {kind: "action", actionPackageId: $p, actionKey: $k}}')clay workflows nodes insert "$WF_ID" --input "$STEP4" > step4.jsonclay workflows graph get "$WF_ID" --mode full > graph.json echo "7/8 Map step inputs"cat <<EOF Step inputs are specific to your workspace. Map them before validating: Step 1 account: company_domain from the webhook payload Step 2 contact: company_domain plus your buying-role titles; add a run condition that skips accounts outside your ICP Step 3 opener: first name and title from step 2, topic, and the writer prompt Step 4 sequence: email, first and last name, the opener, and a TEST campaignMap them in the Clay UI${WF_URL:+ at $WF_URL}, or per node with: clay workflows nodes get $WF_ID <nodeId> clay workflows nodes update $WF_ID <nodeId> --input node.jsongraph.json in this folder shows the current nodes and edges. EOFread -r -p "Press Enter when inputs are mapped. " echo "8/8 Validate, test once, publish"clay workflows graph validate "$WF_ID"clay workflows diagram "$WF_ID" if jq -e 'all(.[]; . != "")' <<< "$TEST_ROW" > /dev/null; then read -r -p "Run TEST_ROW once? It can spend Clay credits and add a lead to your sequencer. [y/N] " answer if [[ "$answer" == [yY] ]]; then clay workflows runs test "$WF_ID" --inputs "$TEST_ROW" > test-run.json echo "Saved test-run.json. Check each step with: clay workflows runs steps $WF_ID <runId>" fielse echo "TEST_ROW is empty, skipping the test run."fi read -r -p "Publish the workflow now? [y/N] " answerif [[ "$answer" == [yY] ]]; then clay workflows publish "$WF_ID" --name v1fi echo "Workflow $WF_ID, webhook trigger $TRG_ID. All responses are saved in $(pwd)."Part 4: Sample signal payload
Save this as clay-intent-outbound/sample-signal.json. Your relay should send the same fields for every signal.
sample-signal.json
{ "company_domain": "example.com", "signal_type": "category_research", "topic": "sales engagement software", "signal_date": "2026-10-15"}Part 5: Send it signals
After publishing, read the webhook URL and send the sample payload once. Each POST is one run.
Load the saved ids (run inside clay-intent-outbound/)
WF_ID=$(jq -r '.id' workflow.json); TRG_ID=$(jq -r '.id' trigger.json)Read the webhook URL (argument order inferred, see the verify notes)
WEBHOOK_URL=$(clay workflows triggers get "$WF_ID" "$TRG_ID" | jq -r '.webhookUrl')Send the sample payload (needs a published workflow, one run per request)
curl -sS -X POST "$WEBHOOK_URL" -H 'Content-Type: application/json' --data @sample-signal.jsonList runs
clay workflows runs list "$WF_ID"Inspect one run
clay workflows runs get "$WF_ID" <runId>See each step's output for that run
clay workflows runs steps "$WF_ID" <runId>Verify in your workspace
The Clay CLI help does not document these details. Check each one before you rely on the build.
- The webhook trigger shape comes from the CLI help, but the help does not say how "triggers get" orders its arguments. If the run commands fail, copy the webhook URL from the Clay UI instead.
- The help documents "nodes create" with nodeType and name only. If it rejects actionPackageId and actionKey, create the node with a name only and pick the action in the Clay UI.
- The reference says action steps take actionPackageId and actionKey but shows only a utility example for "nodes insert", so the placement of those fields inside "step" is inferred.
- Only "workflows create" documents an id field in its response. The script reads .id from trigger and node responses too and stops if it is missing.
- Package ids and action keys differ by workspace and depend on which integrations you have connected. Copy them from your catalog; this page does not guess them.
- Step input mapping is dynamic per workspace. The CLI path is "nodes get" then "nodes update", where incomingEdges is a full replacement. The argument order for "nodes get" is inferred from "nodes update".
- The help does not say whether step 1 connects to the trigger automatically. "clay workflows graph validate" reports a disconnected graph.
- This build does not check your CRM for customers or open opportunities. Filter those out before the signal reaches the webhook, or add a CRM lookup step in the Clay UI.
- "runs test" without --live is assumed to run the draft. The reference lists --live without describing it.
- Workflow runs can spend Clay credits and write to connected tools. Exit code 3 means re-run clay login, and exit code 4 means you are rate limited (wait for details.retryAfter).
Keep going
Related playbooks
- InboundBeginner
Monitor Website Intent Signals and Alert Sales
High-value accounts visit your pricing page repeatedly, but sales never finds out.
HubSpotZapierSlackEstimate: 5 hrs/week - OutboundAdvanced
Turn Anonymous Website Visitors into Outbound Sequences
Marketing and sales cannot tell which ICP companies hit pricing, docs, or blog pages, so high-intent traffic dies without a follow-up.
RB2B or VectorClayHubSpot or Salesforce+1Estimate: 6 hrs/week - OutboundIntermediate
Outbound When ICP Companies Hire AEs or SDRs
Competitors and category peers hiring sellers is a timing signal that budgets and territory plans are expanding, but most teams notice weeks late.
ClayApify or job-board sourceHubSpot or Salesforce+1Estimate: 5 hrs/week - RevOpsIntermediate
Build a Clay Waterfall to Find Verified Work Emails
A single data provider misses many contacts, so reps either work incomplete lists or pay for several tools and copy results between them by hand.
ClayApolloHunter or Prospeo+2Estimate: 4 hrs/week - OutboundAdvanced
Launch Outbound When Target Accounts Add or Drop a Competitor
When a target account drops a competitor or adds a tool that pairs with yours, there is a short window to start a conversation, but static tech stack lists only show what is installed today, not what changed.
ClayBuiltWith or HG InsightsHubSpot or Salesforce+2Estimate: 3 hrs/week - OutboundAdvanced
Find Lookalike Accounts from Your Closed-Won Customers
Outbound lists are usually built from broad filters, while the best predictor of who will buy, the customers you already closed, sits in the CRM unused, so reps spend sequences on accounts that look nothing like your wins.
HubSpot or SalesforceClayClaygent+1Estimate: 4 hrs/week
Need help implementing a playbook?
Our team builds custom Clay workflows and AI automations for revenue teams.
Get it built for you