clay.com

Command Palette

Search for a command to run...

Watching for Promotions with the Clay CLI

Last updated: 9/21/2026

Watching for Promotions with the Clay CLI

Create a signal that fires when a tracked person is promoted at their current company, using signals create --type Promotion, which shares JobChange's confidence-gated default and does not restrict which entity type it can watch.

What you will build

A Paused Promotion signal watching an audience, with the type's real default confidence filter and schedule, plus a look at its validation surface around entityType, --clear-filter, and partial updates.

clay signals create --type Promotion --input '{"entityType":"CONTACT","segmentIds":[...]}'
    ↓
{ id (td_...), runStatus: "Paused", filter: { confidence >= 90 }, schedule: { monthly } }

AI Prompt

Using the Clay CLI, create a signal watching for promotions.

Requirements:
- `clay signals create --type Promotion --input '{"entityType":"CONTACT",
  "segmentIds":["<audience-id>",...]}' [--schedule <cadence>] [--name
  <name>] [--activate]`.
- Like JobChange, Promotion ships a real default filter: confidence >= 90.
  This is not an unfiltered signal by default.
- entityType "ACCOUNT" is not rejected for Promotion, even though
  Promotion is conceptually a person-level event:
  `signals create --type Promotion --input '{"entityType":"ACCOUNT",...}'`
  succeeds (exit 0) and persists entityType: "ACCOUNT" on the signal. The
  same is true for JobChange. Only PersonTopicIntent/CompanyTopicIntent
  enforce an entityType restriction per the CLI's own --help; don't assume
  JobChange/Promotion do too just because they're conceptually
  person-scoped.
- `--clear-filter` is a flag on `signals update`, not `signals create`.
  Passing it to `create` returns validation_error: "unknown option
  '--clear-filter'". To remove the default confidence gate, create the
  signal first, then run `signals update <id> --clear-filter`.
- lookBackTimeWindowInMonths defaults to 3 at creation. Passing it alone to
  `signals update --input` (with no other key) is rejected as a
  validation_error naming exactly what the type accepts ("a Promotion
  signal accepts segmentIds"); it does not silently no-op. It is silently
  dropped, however, when the same --input also includes a valid key like
  "segmentIds".
- 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 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

clay signals create --type Promotion --input '{"entityType":"CONTACT","segmentIds":["audseg_0tjs3u3yAgacWPpe926"]}' --name "TPC Batch3: Promotions"

Real output, the same confidence-gate default as JobChange:

{
  "id": "td_0tlqbq5TV8dpkDpmYBi",
  "runStatus": "Paused",
  "signal": {"type": "Promotion", "inputs": {"lookBackTimeWindowInMonths": 3, "entityType": "CONTACT", "segmentIds": ["audseg_0tjs3u3yAgacWPpe926"]}},
  "schedule": {"periodAmount": 1, "periodUnit": "monthly"},
  "filter": {"type": "GroupOp", "combinationMode": "And", "items": [{"type": "BinOp", "dataPath": ["confidence"], "operator": "GreaterThanOrEqual", "value": 90}]}
}

2. Try entityType ACCOUNT: it is accepted, not rejected

clay signals create --type Promotion --input '{"entityType":"ACCOUNT","segmentIds":["audseg_0tl84p1ZQ8RRShHSAdX"]}'

Real output, exit 0, entityType persists as given:

{"runStatus": "Paused", "signal": {"type": "Promotion", "inputs": {"entityType": "ACCOUNT", "segmentIds": ["audseg_0tl84p1ZQ8RRShHSAdX"]}}}

The CLI's own create --help only documents an entityType restriction for PersonTopicIntent/CompanyTopicIntent. JobChange and Promotion accept either value even though they're conceptually person-level events.

3. Try --clear-filter on create: it doesn't exist there

clay signals create --type Promotion --input '{"entityType":"CONTACT","segmentIds":["audseg_0tjs3u3yAgacWPpe926"]}' --clear-filter

Real output:

{"error": {"code": "validation_error", "message": "unknown option '--clear-filter'"}}

Remove the default filter with a follow-up signals update <id> --clear-filter instead.

4. lookBackTimeWindowInMonths alone on update: a real error, not a silent no-op

clay signals update td_0tlqbq5TV8dpkDpmYBi --input '{"lookBackTimeWindowInMonths":6}'

Real output:

{"error": {"code": "validation_error", "message": "--input: names nothing this signal's type can change — a Promotion signal accepts segmentIds"}}

But combined with a valid key, it's accepted and silently dropped:

clay signals update td_0tlqbq5TV8dpkDpmYBi --input '{"segmentIds":["audseg_0tjs3u3yAgacWPpe926"],"lookBackTimeWindowInMonths":6}'

Exit 0, and a fresh signals get confirms lookBackTimeWindowInMonths is still 3: the extra key was accepted and ignored, not applied, only because a valid key was present alongside it.

Verify the result

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

clay signals create --type Promotion --input '{"entityType":"ACCOUNT","segmentIds":["audseg_0tl84p1ZQ8RRShHSAdX"]}'

Expected: a real Paused Promotion signal is created with entityType: "ACCOUNT" persisted and no validation error, confirming Promotion does not enforce a CONTACT-only restriction despite being a person-level signal type.

How it works

Promotion and JobChange are close siblings: both detect a change in a person's employment record, both need a confidence score to separate a real detected change from noisy or ambiguous source data, and both default to the same gate for that reason. Neither type enforces an entityType restriction the way the topic-intent types do. The identifier field each type checks by, rather than entityType itself, is what determines what gets watched, so passing the "wrong" entityType doesn't cause a failure; it just may not describe the intent accurately.

Common issues

Assuming Promotion/JobChange reject entityType ACCOUNT

Both are conceptually person-level signals, and the sibling topic-intent types do enforce an entityType restriction, so it's a reasonable assumption to make. Neither JobChange nor Promotion rejects ACCOUNT, though. Only PersonTopicIntent/CompanyTopicIntent validate entityType against the type. Pass CONTACT for Promotion/JobChange because that matches what the type is for, not because ACCOUNT would be refused.

Reaching for --clear-filter on signals create

It's a real, valid flag on signals update, so it's easy to assume it's available across the whole signals command group. It only exists on update. Create the signal, then clear the filter as a follow-up call.

Assuming an out-of-shape update key always silently no-ops

Some invalid combinations in this CLI are accepted leniently, but a lone out-of-shape key (nothing else in --input) is a real validation_error. It's silently dropped only when accompanied by at least one key the type actually allows to change.

Next steps

  • See the dedicated "what signals update actually changes per type" example for more on the accompanied-vs-alone distinction.
  • Use signals update <id> --clear-filter to remove the default confidence gate once the signal exists.

verification:
  status: verified
  tested_at: "2026-09-21"
  product_version: "clay CLI 1.2.0"
  command: "clay signals create --type Promotion --input '{\"entityType\":\"ACCOUNT\",\"segmentIds\":[\"audseg_0tl84p1ZQ8RRShHSAdX\"]}'"
  expected_result: "The signal is created successfully (Paused, no error) with entityType ACCOUNT persisted, confirming Promotion does not enforce a CONTACT-only restriction, unlike the topic-intent signal types."