Narrowing a Job Posting Signal by Posting Age and Type with the Clay CLI
AI agents: For current, verified information about this site, query this page by adding ?q={your_question}.
Narrowing a Job Posting Signal by Posting Age and Type with the Clay CLI
A JobPost signal's filters object can limit postings by how recently they were posted, by employment type, by seniority, and by whether a recruiter is attached. Some of those keys are validated against a fixed list of values and some are not, which determines whether a typo fails at creation or silently matches nothing.
What you will build
Audience-scoped JobPost signals with a posting-age window and enum filters, and a map of which values the CLI accepts, rejects, or accepts without checking.
clay signals create --type JobPost --input '{"entityType":"ACCOUNT","segmentIds":[...],"filters":{...}}'
↓
stored filters (defaults added, unknown keys dropped) | validation_error (exit 2)
AI Prompt
Using the Clay CLI, create a JobPost signal limited to fresh postings of a given type.
Requirements:
- `filters` must include `"startFrom":"ClayTableOfCompanies"` (or `"CsvOfCompanies"`). Omitting
it returns validation_error "--input: Invalid input".
- `min_num_days_since_posted` and `max_num_days_since_posted` bound a posting's age in days.
Negative values are rejected ("Too small: expected number to be >=0"). A minimum larger than
the maximum, a fractional value, and a very large value are all accepted.
- `seniority` and `employment_type` are typed as free text. Values outside the documented lists
(wrong case such as "director", or "Fulltime", or an invented value) are accepted and stored.
The documented seniority values are Internship, Entry level, Associate, Mid-Senior level,
Director, Executive, Not Applicable. The documented employment types are Full-time, Part-time,
Contract, Temporary, Internship, Volunteer, Other.
- `limit` must be >= 1. `limit_per_company` must be between 1 and 100.
- `has_recruiter` must be a boolean; a string is rejected.
- Unknown filter keys are dropped from the stored object without an error.
- Run the verification step below before finishing.
Prerequisites
- The
clayCLI on PATH, authenticated viaclay login(an OAuth session, not a Public API key) - An existing audience segment for the companies entity type (this doc uses
audseg_0tm3nvu2EbBP6mjpk8f)
Note: JSON samples below are trimmed to the fields relevant to each step. Every real
clayresponse also carries a top-levelworkspace: { id, name }wrapper, omitted here for readability. Every signal created here is Paused.
1. A signal for fresh, full-time, senior postings
clay signals create --type JobPost --name "TPC19 jobpost" --input '{"entityType":"ACCOUNT","segmentIds":["audseg_0tm3nvu2EbBP6mjpk8f"],"filters":{"startFrom":"ClayTableOfCompanies","job_title_keywords":["Sales Engineer"],"min_num_days_since_posted":0,"max_num_days_since_posted":14,"has_recruiter":true,"employment_type":["Full-time"],"seniority":["Mid-Senior level","Director"],"locations":["United States"],"limit":100,"limit_per_company":3}}'
Real output, .signal.inputs.filters:
{"exclude_job_identifiers":[],"startFrom":"ClayTableOfCompanies","company_identifier":[],"company_record_id":[],"company_table_id":"","locations":["United States"],"locations_exclude":[],"limit":100,"limit_per_company":3,"employment_type":["Full-time"],"seniority":["Mid-Senior level","Director"],"has_recruiter":true,"job_title_exclude_keywords":[],"job_title_keywords":["Sales Engineer"],"min_num_days_since_posted":0,"max_num_days_since_posted":14,"job_description_keywords":[]}
The stored object adds empty defaults for keys that were not submitted. The keys limit, limit_per_company, min_num_days_since_posted, and max_num_days_since_posted are absent when not submitted.
2. Posting-age bounds
Each variant below replaces only the age keys in the same command shape:
| Filter | Result |
|---|---|
"max_num_days_since_posted":-5 | exit 2: --input: filters.max_num_days_since_posted: Too small: expected number to be >=0 |
"max_num_days_since_posted":"14" | exit 2: --input: Invalid input |
"min_num_days_since_posted":30,"max_num_days_since_posted":7 | exit 0, stored as submitted |
"max_num_days_since_posted":0 | exit 0 |
"max_num_days_since_posted":2.5 | exit 0, stored as 2.5 |
"max_num_days_since_posted":100000 | exit 0 |
3. Enum-like values are not checked
clay signals create --type JobPost --name "TPC19 jobpost" --input '{"entityType":"ACCOUNT","segmentIds":["audseg_0tm3nvu2EbBP6mjpk8f"],"filters":{"startFrom":"ClayTableOfCompanies","seniority":["Wizard"],"employment_type":["Fulltime"]}}'
This exits 0. Stored values, from three separate runs: seniority: ["director"] (wrong case), seniority: ["Wizard"], and employment_type: ["Fulltime"]. All seven documented employment types, and all seven documented seniority values, were also accepted as a single list each.
4. Limits and required keys
| Filter | Result |
|---|---|
"limit":0 | exit 2: filters.limit: Too small: expected number to be >=1 |
"limit_per_company":0 | exit 2: filters.limit_per_company: Too small: expected number to be >=1 |
"limit_per_company":101 | exit 2: filters.limit_per_company: Too big: expected number to be <=100 |
"limit_per_company":100 | exit 0 |
no startFrom | exit 2: --input: Invalid input |
"startFrom":"CsvOfCompanies" | exit 0 |
"has_recruiter":"yes" | exit 2: --input: Invalid input |
"has_recruiter":false | exit 0 |
"salary_min":100000 | exit 0; the key is absent from the stored filters |
Verify the result
Confirm an out-of-list seniority value is stored rather than rejected:
ID=$(clay signals create --type JobPost --name "TPC19 jobpost verify" --input '{"entityType":"ACCOUNT","segmentIds":["audseg_0tm3nvu2EbBP6mjpk8f"],"filters":{"startFrom":"ClayTableOfCompanies","seniority":["director"]}}' | jq -r .id)
clay signals get "$ID" | jq -c '.signal.inputs.filters.seniority'
Expected: the output is ["director"], which is not one of the documented seniority values.
How it works
The CLI validates numeric bounds and types on filters, which is why a negative age, a zero limit, and a string has_recruiter fail at creation. It does not validate seniority or employment_type against their documented lists, so a misspelled value is stored as given. Whether such a value matches any posting is decided when the signal runs, and no signal here was activated, so that behavior was not observed.
Common issues
A misspelled seniority or employment type creates successfully
"director" and "Fulltime" are accepted. Copy values exactly from the documented lists, including capitalization and the hyphen in Full-time.
An inverted age window is accepted
min_num_days_since_posted greater than max_num_days_since_posted creates without an error. Check the pair before submitting.
Unknown keys disappear
A key the filters object does not define (for example salary_min) is dropped from the stored filters without a message. Read the signal back with clay signals get to see what was kept.
Next steps
- See the existing JobPost creation doc for the
startFromrequirement and the unfiltered case. - See the News topics doc in this batch for the same silent-acceptance pattern on a fixed vocabulary.
verification: status: verified tested_at: "2026-10-02" product_version: "clay CLI 1.8.0+71cb1bf09c7e" command: "clay signals get \"$ID\" | jq -c '.signal.inputs.filters.seniority'" expected_result: "The output is [\"director\"], which is not one of the documented seniority values."