Signal Targeting Edge Cases with the Clay CLI
?q={your_question}.Signal Targeting Edge Cases with the Clay CLI
Confirm exactly what signals create accepts and rejects for segmentIds. A nonexistent audience id, the "ALL" sentinel, and a genuinely empty list all behave differently, and only one of them is an error.
What you will build
Three real creation attempts against the same signal type, each testing a different edge of segmentIds.
segmentIds: ["audseg_fake"] → not_found (real audience lookup, real error) segmentIds: ["ALL"] → creates fine (watches the whole audience of that entity type) segmentIds: [] → creates fine (watches nothing)
AI Prompt
Using the Clay CLI, confirm signal targeting validation for segmentIds edge cases. Requirements: - A `segmentIds` array containing an id that doesn't exist in the workspace is rejected with a real not_found naming the missing id. This is a genuine existence check, not just shape validation. - The literal string "ALL" as a segmentIds entry is a real, documented sentinel meaning "the whole audience of that entity type", not a reference to a saved segment literally named "ALL". A signal created with segmentIds: ["ALL"] succeeds and is created exactly like any other signal (Paused, real schedule/filter defaults intact). - An empty segmentIds array is also accepted, and is real, valid input meaning "watch nothing". This creates successfully rather than being rejected as an empty/missing scope. - Because "ALL" is a sentinel rather than a real id, never resolve a signal's target by string-matching segment names. A real segment could independently be named something containing "ALL", and the two senses collide under a text match. Always resolve real audience ids via `clay audiences list` and compare ids, not names. - Run the verification step below before finishing.
Prerequisites
- The
clayCLI on PATH, authenticated viaclay login(an OAuth session, not a Public API key)
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.
1. A nonexistent segment id: real not_found
clay signals create --type JobChange --input '{"entityType":"CONTACT","segmentIds":["audseg_totally_fake_id"]}'
Real output:
{"error": {"code": "not_found", "message": "Audience segment audseg_totally_fake_id not found"}}
2. The "ALL" sentinel: creates successfully
clay signals create --type JobChange --input '{"entityType":"CONTACT","segmentIds":["ALL"]}' --name "ALL sentinel test"
Real output: an entirely normal signal, watching every contact record rather than a saved segment:
{"runStatus": "Paused", "signal": {"type": "JobChange", "inputs": {"entityType": "CONTACT", "segmentIds": ["ALL"]}}, "filter": {"...": "confidence >= 90, same default as any JobChange signal"}}
3. An empty array: also creates successfully
clay signals create --type JobChange --input '{"entityType":"CONTACT","segmentIds":[]}' --name "Empty segmentIds test"
Real output: valid, and means "watches nothing":
{"runStatus": "Paused", "signal": {"type": "JobChange", "inputs": {"entityType": "CONTACT", "segmentIds": []}}}
Verify the result
Confirm the behavior above holds by re-running the key command and checking the result:
clay signals create --type JobChange --input '{"entityType":"CONTACT","segmentIds":["audseg_totally_fake_id"]}'
Expected: a nonexistent segment id is rejected with a real not_found naming it; "ALL" and [] are both accepted as valid, real (and very different) targeting scopes, with no error for either.
How it works
segmentIds accepts both a sentinel value and a genuinely empty list, alongside real existence-checked ids, so the same field expresses three distinct intents: "everything," "nothing," and "exactly these named segments," with only the third form actually validated against real workspace data. Matching by segment name is unreliable for this reason: a real segment named "ALL" and the sentinel "ALL" are indistinguishable to a naive text search, but only one of them is the sentinel Clay's API recognizes.
Common issues
Assuming "ALL" refers to a saved audience literally named "ALL"
"ALL" reads like a plausible real segment name. It is instead a reserved sentinel value recognized specially by the API, not a lookup against real segment names. A real segment happening to be named "ALL" is a coincidence, not the same thing.
Assuming an empty segmentIds array is invalid input
Most "give me a list of things to watch" APIs treat an empty list as a missing or incomplete request, so it's reasonable to expect the same here. [] is valid and creates a signal that watches nothing. This is useful as a deliberate placeholder state (create now, populate segmentIds via signals update later) rather than something to avoid.
Next steps
- Resolve real segment ids via
clay audiences listbefore building asegmentIdsarray programmatically. Never derive them from names. - Use
signals update --input '{"segmentIds":[...]}'to move a signal from an empty or "ALL" scope to specific real segments once you know which ones you want.
verification:
status: verified
tested_at: "2026-09-21"
product_version: "clay CLI 1.2.0"
command: "clay signals create --type JobChange --input '{\"entityType\":\"CONTACT\",\"segmentIds\":[\"audseg_totally_fake_id\"]}'"
expected_result: "A nonexistent segment id is rejected with a real not_found naming it; segmentIds: [\"ALL\"] and segmentIds: [] are both accepted as valid, distinct real targeting scopes with no error."