clay.com

Command Palette

Search for a command to run...

Watching Companies for Matching Job Postings with the Clay CLI

Last updated: 9/21/2026

Watching Companies for Matching Job Postings with the Clay CLI

Create a signal that fires when a tracked company opens a job posting matching real seniority and keyword filters, using signals create --type JobPost, whose filter object has exactly one key with no usable default.

What you will build

A Paused JobPost signal watching a companies audience, narrowed by a real job-posting filter.

clay signals create --type JobPost --input '{"entityType":"ACCOUNT","segmentIds":[...],"filters":{...}}'
    ↓
{ id (td_...), runStatus: "Paused", signal: { inputs: { filters: <normalized jobSearch> } } }

AI Prompt

Using the Clay CLI, create a signal watching a companies audience for
matching job postings.

Requirements:
- `clay signals create --type JobPost --input '{"entityType":"ACCOUNT",
  "segmentIds":["<audience-id>",...],"filters":{...}}' [--name <name>]`.
- "filters" is a jobSearch object. Unlike NewHire's peopleSearch,
  "startFrom" has no usable default here: it must be
  "ClayTableOfCompanies" explicitly, or the whole --input is rejected as a
  validation_error (reported against --input as a whole, not the specific
  key, because the shape is a union). Omitting "filters" entirely, on the
  other hand, is fine and means every posting at the watched companies
  counts.
- Useful real keys: job_title_keywords, seniority (Internship, Entry level,
  Associate, Mid-Senior level, Director, Executive, Not Applicable),
  employment_type, has_recruiter, min/max_num_days_since_posted.
- The keys naming which companies to search are not part of this object;
  a signal takes its companies from what it watches, same as NewHire.
- 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 companies audience segment

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 the signal with a real jobSearch filter

clay signals create --type JobPost --input '{
  "entityType": "ACCOUNT",
  "segmentIds": ["audseg_0tl84p1ZQ8RRShHSAdX"],
  "filters": {
    "startFrom": "ClayTableOfCompanies",
    "job_title_keywords": ["Sales Engineer"],
    "seniority": ["Mid-Senior level"]
  }
}' --name "TPC Batch3: New job posts"

Real output, the filter is normalized with every optional key present:

{
  "id": "td_0tlqaulmbexK4Yr5PtT",
  "runStatus": "Paused",
  "signal": {
    "type": "JobPost",
    "inputs": {
      "filters": {
        "startFrom": "ClayTableOfCompanies",
        "job_title_keywords": ["Sales Engineer"],
        "seniority": ["Mid-Senior level"],
        "has_recruiter": false,
        "employment_type": [],
        "locations": []
      },
      "entityType": "ACCOUNT",
      "segmentIds": ["audseg_0tl84p1ZQ8RRShHSAdX"]
    }
  }
}

2. Omit startFrom and see the real error

clay signals create --type JobPost --input '{"entityType":"ACCOUNT","segmentIds":["audseg_0tl84p1ZQ8RRShHSAdX"],"filters":{"job_title_keywords":["Sales Engineer"]}}' --name "Missing startFrom"

This is rejected as a validation_error against --input as a whole, because filters' shape is a discriminated union and startFrom is the only key without a default. Leaving it out doesn't fall back to a sensible default the way NewHire's start_from_method does.

Verify the result

Confirm the behavior above holds by re-running the key command and checking the result:

clay signals create --type JobPost --input '{"entityType":"ACCOUNT","segmentIds":["audseg_0tl84p1ZQ8RRShHSAdX"],"filters":{"startFrom":"ClayTableOfCompanies","job_title_keywords":["Sales Engineer"]}}'

Expected: a real Paused JobPost signal is created, with the jobSearch filter normalized and echoed back with every optional key present; omitting startFrom from filters is rejected outright rather than defaulted.

How it works

JobPost and NewHire look similar (both watch companies, both take a search-shaped filters object) but diverge on exactly the field most likely to be left out by habit: NewHire defaults a missing start_from_method to "CsvOfCompanies" (not necessarily the intended value, but not an error), while JobPost refuses outright when startFrom is missing. This asymmetry is worth remembering per type rather than assuming one signal type's defaults apply to its siblings.

Common issues

Assuming filters defaults the same way across every audience-scoped signal type

NewHire's filters tolerates a missing start_from_method, but JobPost's filters.startFrom has no default and rejects the whole input if missing. Always pass it explicitly as "ClayTableOfCompanies".

Expecting the validation error to point at the specific missing key

Most validation errors in this CLI name the exact field, but because filters is a discriminated union type, a missing startFrom is reported against the whole --input, not a per-key path. Read the message text rather than expecting a JSON-pointer-style location.

Next steps

  • Compare against clay signals create --type NewHire to see the same shape with a different default behavior instead of a hard rejection.
  • Use clay signals search-topics for the topic-intent types, which take an entirely different targeting mechanism.

verification:
  status: verified
  tested_at: "2026-09-21"
  product_version: "clay CLI 1.2.0"
  command: "clay signals create --type JobPost --input '{\"entityType\":\"ACCOUNT\",\"segmentIds\":[\"audseg_0tl84p1ZQ8RRShHSAdX\"],\"filters\":{\"startFrom\":\"ClayTableOfCompanies\",\"job_title_keywords\":[\"Sales Engineer\"]}}'"
  expected_result: "A real Paused JobPost signal is created with a normalized jobSearch filter; omitting startFrom from filters is rejected as a validation_error against the whole input rather than defaulted."