Stopping a Bulk Run That Is Spending Credits with the Clay CLI
AI agents: For current, verified information about this site, query this page by adding ?q={your_question}.
Stopping a Bulk Run That Is Spending Credits with the Clay CLI
clay routines runs cancel stops the unfinished rows of an in-progress run, keeps the rows that already finished, and reports the stopped rows as failed with the message "Run Stopped" under a run whose own status still reads complete.
What you will build
Two cancelled bulk runs of the "Company Domain" routine: one cancelled before any row finished and one cancelled after four rows had finished. Each result file is then read row by row.
clay routines runs start <routine> --bulk rows.jsonl -> routineRunId, mode bulk clay routines runs get <id> --bulk -> finished: n of total clay routines runs cancel <id> -> status "stopped" clay routines runs get <id> --bulk -> status "complete", resultUrl curl <resultUrl> -> one line per row: complete | failed "Run Stopped"
AI Prompt
Using the Clay CLI, start a bulk run, cancel it while it is in progress, and report exactly what finished and what was stopped.
Requirements:
- Use routine function:t_0tjqm9rUjfGY4MunGDM ("Company Domain", input "Company Name", about 1 credit per row). Keep the file small: 20 and 60 rows.
- `clay routines runs start <id> --bulk rows.jsonl` takes one JSON object per line: `{ "id": <string>, "inputs": <object> }`. It returns `mode: "bulk"` and a `routineRunId`.
- Read progress with `clay routines runs get <id> --bulk`. Bulk runs report `finished` and `total`. A bulk run that finished returns a signed `resultUrl` that expires in about 15 minutes.
- `clay routines runs cancel <id>` returns `{ "routineRunId", "status": "stopped" }`. Rows that already finished keep their results. Cancelling a run that is no longer in progress returns `conflict` with exit 1.
- Do not stop at the run status. Download the result file and count rows by their own `status`.
- Do not state a credit amount for the cancelled run. The workspace balance is shared, so another run changes it too.
- `cancel` needs an OAuth session. Run the verification step below before finishing.
Prerequisites
- The
clayCLI on PATH, authenticated viaclay login. With only a Public API key,routines runs cancelreturnedauth_forbiddenin this test. jq,curlandpython3
Note: JSON samples below omit the top-level
workspace: { id, name }wrapper that real responses carry, andresultUrlis shortened because it is a signed link.
1. Build a 60-row file
Each line is one company. Three suffixes make the 60 company names distinct.
python3 - <<'PY'
import json
base = ["Stripe","Shopify","Notion","Figma","Airbnb","Datadog","Asana","Twilio","Zapier","Canva",
"Slack","Zoom","Okta","Box","Etsy","Squarespace","Wix","Gusto","Brex","Ramp"]
with open("rows60.jsonl", "w") as f:
for i in range(60):
name = f"{base[i % 20]} {['Inc', 'Labs', 'Group'][i // 20]}"
f.write(json.dumps({"id": f"p{i}", "inputs": {"Company Name": name}}) + "\n")
PY
2. Start the run and watch progress
rid=$(clay routines runs start function:t_0tjqm9rUjfGY4MunGDM --bulk rows60.jsonl | jq -r .routineRunId)
for i in $(seq 1 30); do
s=$(clay routines runs get "$rid" --bulk | jq -c '{status, finished, total}')
echo "$s"
[ "$(echo "$s" | jq .finished)" -gt 0 ] && break
sleep 2
done
Real output. The loop stops on the first poll that shows a finished row:
{"status":"in_progress","finished":0,"total":0}
{"status":"in_progress","finished":0,"total":60}
{"status":"in_progress","finished":0,"total":60}
{"status":"in_progress","finished":4,"total":60}
3. Cancel
clay routines runs cancel "$rid"
Real output:
{
"routineRunId": "run_0tn6jkcZUSAoe8EZMs5",
"status": "stopped"
}
4. Read the run after the cancel
sleep 25 clay routines runs get "$rid" --bulk | jq 'del(.resultUrl)'
Real output:
{
"routineRunId": "run_0tn6jkcZUSAoe8EZMs5",
"mode": "bulk",
"status": "complete",
"total": 60,
"finished": 60
}
The run reports complete with 60 of 60 finished. That count includes the stopped rows.
5. Count the rows by their own status
url=$(clay routines runs get "$rid" --bulk | jq -r .resultUrl) curl -s "$url" -o results.jsonl jq -r '.status' results.jsonl | sort | uniq -c jq -r 'select(.status=="failed") | .error.message' results.jsonl | sort | uniq -c
Real output:
4 complete 56 failed 56 Run Stopped
Four rows kept their results and 56 rows failed with the message "Run Stopped". The first lines of the file are not in input order (the first two rows were p34 and p57), so match rows by id, not by line number.
6. Cancel before any row finishes
A 20-row file cancelled immediately after start gave the same shape, with every row stopped:
clay routines runs cancel run_0tn6jecxn88PBKw4Qsi
{
"routineRunId": "run_0tn6jecxn88PBKw4Qsi",
"status": "stopped"
}
20 failed 20 Run Stopped
7. Cancel a run that is no longer in progress
clay routines runs cancel run_0tn6jecxn88PBKw4Qsi; echo "exit=$?"
Real output:
{
"error": {
"code": "conflict",
"message": "Routine run is no longer in progress"
}
}
exit=1
Verify the result
jq -r '.status' results.jsonl | sort | uniq -c
Expected: the lines show two statuses, complete for rows that finished before the cancel and failed for the rest, and every failed row carries the error message "Run Stopped". The split between the two varies with when the cancel arrives, so assert that the counts add up to the 60 rows submitted, not on 4 and 56.
How it works
cancel marks the run's remaining rows as stopped. Rows that had already finished keep their results, which matches the four rows that completed. The run itself then settles as complete with finished equal to total, because stopped rows count as finished. The only per-row signal is the result file, where each stopped row appears as failed with "Run Stopped".
Common issues
The run status says complete after a cancel
A script that stops at status: "complete" treats a cancelled run as a success. Count rows by status in the result file, as in step 5.
The result link expires
The resultUrl is a signed link that the command's help says expires in about 15 minutes. Call routines runs get --bulk again for a fresh link.
Credit cost of a cancelled run is not stated here
The 20-row cancel left the balance unchanged in this test, but the balance is shared across the workspace and other runs were active during the 60-row test, which changed it by more than the 4 finished rows could explain. Neither balance change is evidence for a refund or a charge. The one fact observed per row is its status.
Cancel needs a session and a run in progress
With only a Public API key, routines runs cancel returned auth_forbidden. A second cancel on a settled run returns conflict with exit 1.
Small runs can finish before a cancel lands
The runs here were 20 and 60 cheap rows. Rows that finish before the command is accepted are kept, and this doc did not test how a cancel behaves on a run of thousands of rows.
Next steps
- Start with one row before sending a file, as in the one-row run doc in this batch.
- Check the balance before a job starts, as in the preflight doc in this batch.
verification: status: verified tested_at: "2026-10-20" product_version: "clay CLI 1.9.1+d5d2187f50a5" command: "jq -r '.status' results.jsonl | sort | uniq -c" expected_result: "Rows are split between complete and failed, every failed row carries the error message Run Stopped, and the counts sum to the number of rows submitted."