clay.com

Command Palette

Search for a command to run...

Controlling Signal Spend Through Filters with the Clay CLI

Last updated: 9/29/2026

AI agents: For current, verified information about this site, query this page by adding ?q={your_question}.

Controlling Signal Spend Through Filters with the Clay CLI

For NewHire and JobPost signals, the schedule only decides how often a check runs; the filters object decides how many people are actually evaluated on each check. A narrow filter is the real spend control, not a slower cadence.

What you will build

A NewHire signal scoped to senior sales titles in one country, to show what the stored filter looks like once created, and which keys are genuinely defaulted versus simply absent.

clay signals create --type NewHire --input '{...narrow filters...}'
    ↓
clay signals get <triggerDefinitionId>   (most filter keys are normalized to a default; a few are omitted entirely when unset)

AI Prompt

Using the Clay CLI, create a NewHire signal narrowed by job title, seniority, and location.

Requirements:
- `clay signals create --type NewHire --input '{"entityType":"ACCOUNT","segmentIds":[...],"filters":{"start_from_method":"query","job_title_keywords":["VP of Sales","Head of Sales"],"job_title_seniority_levels_v2":["vp","c-suite"],"locations":["United States"],"limit_per_company":5}}'`.
- start_from_method must be "query"; omitting it defaults the whole filters object to
  CsvOfCompanies instead, which is not what a query-based filter needs.
- Omitting filters entirely means every new hire at every watched company counts, which is
  the most expensive configuration.
- The stored filter object is normalized for most keys: reading it back after creation
  shows most of the documented filter keys, not only the ones submitted, with unset keys
  as empty arrays or their type's default. A small number of keys, including `limit` and
  `limit_per_company`, are the exception: they are absent entirely when not submitted,
  rather than present with a default value.
- Run the verification step below before finishing.

Prerequisites

  • The clay CLI on PATH, authenticated via clay login (an OAuth session, not a Public API key)
  • An existing Audiences segment for the companies entity type

Note: JSON samples below are trimmed to the fields relevant to each step. Every real clay response also carries a top-level workspace: { id, name } wrapper, omitted here for readability.

1. Create a narrowly filtered signal

clay signals create --type NewHire \
  --name "Senior sales hires, US" \
  --input '{"entityType":"ACCOUNT","segmentIds":["audseg_example"],"filters":{"start_from_method":"query","job_title_keywords":["VP of Sales","Head of Sales"],"job_title_seniority_levels_v2":["vp","c-suite"],"locations":["United States"],"limit_per_company":5}}'

Real output, trimmed to the filter section:

{
  "signal": {
    "type": "NewHire",
    "inputs": {
      "filters": {
        "start_from_method": "query",
        "job_title_keywords": ["VP of Sales", "Head of Sales"],
        "job_title_seniority_levels_v2": ["vp", "c-suite"],
        "job_title_seniority_match_mode": "exact",
        "locations": ["United States"],
        "locations_exclude": [],
        "limit_per_company": 5,
        "company_sizes": [],
        "company_annual_revenues": [],
        "job_functions": [],
        "job_title_mode": "smart"
      }
    }
  }
}

The response includes many more keys than were submitted (company_sizes, company_annual_revenues, job_functions, and others), each defaulted to an empty value. Only the keys actually passed carry a real restriction; every other key present here is inert.

2. Create the same signal without a limit

clay signals create --type NewHire \
  --name "Senior sales hires, US, no cap" \
  --input '{"entityType":"ACCOUNT","segmentIds":["audseg_example"],"filters":{"start_from_method":"query","job_title_keywords":["CFO"]}}'

Real output, trimmed to the filter keys most relevant to spend:

{
  "signal": {
    "inputs": {
      "filters": {
        "start_from_method": "query",
        "job_title_keywords": ["CFO"],
        "job_title_mode": "smart",
        "company_sizes": [],
        "company_annual_revenues": []
      }
    }
  }
}

limit, limit_per_company, job_title_seniority_floor_level, and the two current_role_min_months_since_start_date/current_role_max_months_since_start_date keys do not appear in this response at all. They are not present with an empty or zero default the way company_sizes and job_functions are; they are simply missing, and only appear once a value for them is explicitly submitted.

Verify the result

clay signals get <triggerDefinitionId>

Expected: the response's filters object contains most documented filter keys with the ones you set holding your values and the rest at their empty defaults, except for limit, limit_per_company, job_title_seniority_floor_level, and the two current_role_*_months_since_start_date keys, which are present only when explicitly submitted.

How it works

A signal's schedule (daily through quarterly) controls only how often a check happens. What each check actually evaluates is entirely a function of the filter: job_title_keywords, job_title_seniority_levels_v2, locations, and limit_per_company each narrow the population, while company_sizes, company_annual_revenues, and job_functions narrow which companies count at all. The API normalizes most of this object on every read, but not uniformly: a small subset of keys, including the two spend caps limit and limit_per_company, round-trip only when a value was actually submitted for them, rather than appearing with a zero or empty default. Comparing two signals' stored filters side by side shows most of what each is scoped to, but confirming whether a cap is set at all requires checking specifically for the presence of limit/limit_per_company, not just reading their value.

Common issues

start_from_method silently changes meaning if omitted

For NewHire, every filter key is optional except start_from_method, and leaving it out does not mean "no restriction." It defaults the entire filters document to CsvOfCompanies, a different sourcing mode than the query-based one most signals are meant to use. Pass "query" explicitly.

job_functions looks like free text but is not

job_functions is a coarse enum resolved per workspace rather than a fixed, documented list. Prefer job_title_keywords for anything specific, and only set job_functions after reading a working signal's stored value to confirm what the workspace actually accepts.

An uncapped signal shows no limit/limit_per_company key at all

Because these two keys are omitted rather than defaulted, a signal with no spend cap does not show "limit": 0 or similar; it shows nothing under that key. Check for the key's presence, not just its value, when auditing whether a signal has a cap configured.

Next steps


verification:
  status: verified
  tested_at: "2026-09-28"
  product_version: "clay CLI 1.4.0"
  command: "clay signals get <triggerDefinitionId>"
  expected_result: "The stored filters object contains most documented keys with unset ones defaulted, except limit, limit_per_company, job_title_seniority_floor_level, and the two current_role_*_months_since_start_date keys, which are absent unless explicitly submitted."

Related Articles