Running a Signal's Full Lifecycle with the Clay CLI
Running a Signal's Full Lifecycle with the Clay CLI
Resume a signal, confirm it actually ran, pause it again, and delete it for good, using Clay's real signals pause/resume/delete, each of which returns the signal's already-updated state with no second call needed to confirm.
What you will build
A complete state walk: resume → confirm via lastRunAt → pause → delete → confirm it's gone.
clay signals resume <id> → runStatus: "Active"
clay signals pause <id> → runStatus: "Paused", lastRunAt now set
clay signals delete <id> → { "ok": true }
clay signals list → the deleted signal no longer appears
AI Prompt
Using the Clay CLI, run a signal through its full pause/resume/delete lifecycle. Requirements: - `clay signals pause <triggerDefinitionId>` / `clay signals resume <triggerDefinitionId>` each return the signal with runStatus ALREADY updated -- no second `signals get` is needed to confirm the change took. - Only a signal whose CURRENT runStatus is Active or Paused can be paused or resumed -- any other status (Errored, Testing, Disabled, Preview) is refused rather than silently overwritten. An Errored signal is refused by `resume` specifically, because resuming would leave the recorded error in place while pretending the signal is healthy. - Setting a status a signal already has is accepted and changes nothing -- safe to retry pause/resume after a network failure without checking state first. - IMPORTANT, confirmed live: resuming a signal can populate "lastRunAt" within seconds, not only "on the next scheduled run" as the command's own help text states. Treat "resume" as capable of triggering a near- immediate check, not strictly a wait-for-next-schedule operation. - `clay signals delete <triggerDefinitionId>` is NOT idempotent -- a second delete of the same id returns not_found (exit 6), unlike some other Clay delete commands where not_found on retry means "already done." Deleting never removes the destination table or already-captured events, only the signal itself. - Prefer `signals pause` over `signals delete` when the real goal is just "stop this from running/spending" -- pausing is reversible, deleting is not. - 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 signal (see the JobChange or NewHire signal-creation examples)
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. Resume a Paused signal
clay signals resume td_0tl866woYQ5ScAFSHRP | jq '{runStatus, lastRunAt: .schedule.lastRunAt}'
Real output:
{"runStatus": "Active", "lastRunAt": null}
2. Pause it again, moments later
clay signals pause td_0tl866woYQ5ScAFSHRP | jq '{runStatus, lastRunAt: .schedule.lastRunAt}'
Real output, lastRunAt is now populated, roughly ten seconds after resuming, well before the signal's monthly schedule would naturally fire again:
{"runStatus": "Paused", "lastRunAt": "2026-09-12T00:18:43.188Z"}
This directly contradicts a plain reading of the CLI's own resume help text ("runs again on its existing schedule... expect the next event on the next scheduled run"), confirmed live, activation itself can trigger a near-immediate check.
3. Delete it, and confirm deletion is not idempotent
clay signals delete td_0tl866woYQ5ScAFSHRP clay signals delete td_0tl866woYQ5ScAFSHRP
Real output, the second call, not the first:
{"error": {"code": "not_found", "message": "..."}}
4. Confirm it's gone from list
clay signals list | jq '.data | length'
Verify the result
Confirm the behavior above holds by re-running the key command and checking the result:
clay signals resume td_0tl866woYQ5ScAFSHRP && clay signals pause td_0tl866woYQ5ScAFSHRP && clay signals delete td_0tl866woYQ5ScAFSHRP && clay signals delete td_0tl866woYQ5ScAFSHRP
Expected: resume/pause each return runStatus already updated in the same response; lastRunAt is populated within seconds of resuming; the first delete succeeds with {ok:true} and the second returns a real not_found.
How it works
Both pause and resume return the post-change object specifically so a script never needs a defensive follow-up signals get just to confirm state: the state IS the response. Delete's non-idempotence is a deliberate difference from Clay's usual "delete is idempotent" pattern elsewhere in Audiences (archiving an audience twice is harmless); here, a second not_found genuinely means "there was nothing to delete the second time," which matters if a script is retrying blindly after a timeout.
Common issues
Assuming resume only affects the next scheduled run
Cause: the CLI's own help text says exactly that.
Fix (confirmed live): resuming populated lastRunAt within seconds. Don't rely on a resumed signal being inert until its next scheduled interval; treat it as potentially live immediately.
Retrying a signals delete after a timeout, assuming not_found means "already deleted, fine"
Cause: that's the correct assumption for audiences archive, which IS idempotent.
Fix: for signals delete, not_found on a retry could equally mean the id never existed at all. Confirm via signals list before treating a delete as safely completed.
Next steps
- Read
error.userFriendlyMessageon any signal stuckErroredbefore deciding whether to fix it or delete and recreate it. - Prefer
signals pauseoversignals deletewhenever the goal is cost control rather than permanent removal.
verification:
status: verified
tested_at: "2026-09-12"
product_version: "clay CLI 0.19.0"
command: "clay signals resume td_0tl866woYQ5ScAFSHRP && clay signals pause td_0tl866woYQ5ScAFSHRP && clay signals delete td_0tl866woYQ5ScAFSHRP && clay signals delete td_0tl866woYQ5ScAFSHRP"
expected_result: "resume/pause each return runStatus already updated in the same response; lastRunAt is populated within seconds of resuming; the first delete succeeds with {ok:true} and the second returns a real not_found."