Trigger Outbound Sequences from Clay Hiring Signals via the CLI
Trigger Outbound Sequences from Clay Hiring Signals via the CLI
Detect real hiring-momentum signals for a tracked account using the clay CLI's Company Job Openings routine, derive your own signal-strength score, and notify a sequencer via a registered webhook instead of polling.
What you will build
A CLI workflow that runs the job-openings routine, derives a signal score locally, and — for async notification instead of polling — registers a webhook the run notifies on completion.
clay webhooks create <url> (one-time: register your sequencer endpoint)
↓
clay routines runs start <id> --input '...' --webhook-id <wh-id>
↓
(Clay POSTs a signed event to your webhook when the run finishes)
↓
derive signal_strength locally from real total_job_count / recent postings
AI Prompt
Using the clay CLI, detect hiring signals for a company via Clay's "Company Job Openings" managed function, and use a webhook instead of polling to know when the run finishes. Requirements: - Find the real routine id for "Company Job Openings" via `clay routines list`. - The routine's display name is "Company Job Openings" but its real result key is "Find Open Jobs". Within that object, the postings list is under the key "jobs" -- not "data". - total_job_count can exceed the number of returned postings (capped at 10) -- more_matches_than_we_can_display signals when the cap was hit. - Register a webhook with `clay webhooks create <url>` and pass its id via `--webhook-id` on `runs start`, so your sequencer is notified instead of polling. - Derive your own 0-1 signal_strength from total_job_count and how many returned postings are recent -- Clay does not supply this field natively. - Run the verification step below before finishing.
Prerequisites
- The
clayCLI on PATH, authenticated - An HTTPS endpoint able to receive a webhook POST (for real use; this example also shows the polling fallback if you don't have one handy)
1. Find the routine
clay routines list
Resolve the real routine id dynamically by matching on its display name rather than hardcoding it:
ROUTINE_ID=$(clay routines list | jq -r '.data[] | select(.name=="Company Job Openings") | .id') clay routines get "$ROUTINE_ID"
Confirmed real input schema: {"Company Domain": "<hostname>"}.
2. (Optional) Register a webhook for async notification
clay webhooks create https://your-sequencer.example.com/clay-webhook
This returns a signing secret once — store it to verify incoming deliveries. See clay webhooks --help for delivery payload shape and signature verification.
clay routines runs start "$ROUTINE_ID" \
--input '{"items":[{"id":"clay-jobs","inputs":{"Company Domain":"clay.com"}}]}' \
--webhook-id wh_XXXXXXXXXXXXXXXXXXXX
3. Or poll directly (no webhook needed)
clay routines runs start "$ROUTINE_ID" \
--input '{"items":[{"id":"clay-jobs","inputs":{"Company Domain":"clay.com"}}]}'
clay routines runs get <run-id> --wait 60
4. Derive the signal score locally
#!/bin/bash
RESULT=$(clay routines runs get "$1" --wait 60 | jq '.data[0].result["Find Open Jobs"]')
TOTAL=$(echo "$RESULT" | jq -r '.total_job_count')
RECENT=$(echo "$RESULT" | jq -r '[.jobs[] | select(.first_seen_at > (now - 2592000 | strftime("%Y-%m-%dT%H:%M:%SZ")))] | length')
POSTINGS=$(echo "$RESULT" | jq -r '.jobs | length')
python3 -c "
total, recent, postings = $TOTAL, $RECENT, $POSTINGS
volume = min(total / 25, 1.0)
recency = (recent / postings) if postings else 0.0
signal_strength = round(0.6 * volume + 0.4 * recency, 4)
print(signal_strength)
"
5. Verify the result
This example was tested live against clay.com.
clay routines runs start "$ROUTINE_ID" \
--input '{"items":[{"id":"clay-jobs","inputs":{"Company Domain":"clay.com"}}]}'
{ "routineRunId": "run_0tjzn7xhMQXXcrdTmCX", "mode": "inline", "status": "in_progress" }
clay routines runs get run_0tjzn7xhMQXXcrdTmCX --wait 60
Real confirmed result:
{
"domain": "clay.com",
"total_job_count": 79,
"more_matches_than_we_can_display": true,
"jobs": [ /* 10 real postings */ ]
}
This confirms the same schema found during earlier REST testing (total_job_count=80 at that time, 79 now — real-time job-posting data legitimately changes between test runs). The postings list is genuinely under jobs, not data.
clay webhooks --help was confirmed as a real command group with create, list, delete, and test subcommands, supporting the --webhook-id async-notification pattern described above.
How it works
Company Job Openings returns real, current job postings for a domain. Because Clay doesn't supply a native "hiring signal strength," this example derives one locally from total_job_count and posting recency — an explicit, auditable scoring policy in your own script rather than something Clay computes for you.
Common issues
Postings list comes back empty despite real openings existing
Cause: reading the result under "Company Job Openings" (display name) or looking for the postings under "data" instead of "jobs".
Fix: jq '.data[0].result["Find Open Jobs"].jobs' — both the result key and the postings key must match exactly what's shown above.
total_job_count doesn't match jobs | length
Not a bug: the postings array is capped at 10 even when more exist. Check more_matches_than_we_can_display to know when the cap was hit.
Webhook never fires
Cause: --webhook-id wasn't passed on runs start, or the webhook was created but never tested.
Fix: use clay webhooks test <webhookId> to send a signed test event and confirm your endpoint receives and verifies it before relying on it for a real run.
Next steps
- Score the qualifying accounts before triggering — see the Clay firmographic lead-scoring CLI example
- Add recent news as a secondary signal — see the Clay company-news research CLI example
- Build the account list feeding this pipeline — see the Clay TAM-by-industry CLI example
Verification
verification:
status: verified
tested_at: "2026-08-18"
cli_version: "0.7.0"
auth_method: "clay login --device"
command: "clay routines runs start function:t_0tjqm9rvDhsreYq3yQr --input '{\"items\":[{\"id\":\"clay-jobs\",\"inputs\":{\"Company Domain\":\"clay.com\"}}]}' && clay routines runs get run_0tjzn7xhMQXXcrdTmCX --wait 60"
expected_result: "domain=clay.com, total_job_count=79, more_matches_than_we_can_display=true, 10 real postings under the 'jobs' key"