Poll a Sume job from a CI step with curl and jq: exit codes
A 23-line bash script that polls /v1/jobs/{id}/status and exits 0, 1, 2 or 3, so a CI step can tell done, failed, still running and a bad request apart.

A CI step that waits on a Sume job needs four outcomes, not two: completed, failed or canceled, deadline reached with the job still running, and a request that can never succeed. The script below polls GET /v1/jobs/{id}/status with curl, reads sume_status with jq, honors next_poll_after_seconds, and exits 0, 1, 2 or 3 accordingly. On success it prints the artifact URLs, one per line.
Exit code 2 is the one most scripts get wrong. A deadline in your pipeline stops your watching, not the job: the docs say a client-side timeout does not cancel it, and it keeps running and billing. So the script says so, and you should store the id rather than resubmitting.
Exit codes
| Exit | Meaning | HTTP or status seen |
|---|---|---|
| 0 | Job completed; URLs printed | sume_status completed |
| 1 | Job failed or canceled; error JSON on stderr | sume_status failed or canceled |
| 2 | Deadline reached, job may still run | non-terminal until the end |
| 3 | Request cannot succeed by retrying | 401, 403, 404, other 4xx |
| (loops) | Transient; sleeps and retries | 429, 5xx, connection failure |
The script
Needs bash, curl and jq. Set SUME_API_KEY from your CI secret store, never inline. POLL_DEADLINE_SECONDS defaults to 1200, the 20 minutes the docs suggest for video.
#!/usr/bin/env bash
# Usage: poll.sh job_id Exit: 0 completed, 1 failed or canceled, 2 deadline, 3 request error
set -u
BASE="${SUME_BASE_URL:-https://api.sume.com}"
DEADLINE=$((SECONDS + ${POLL_DEADLINE_SECONDS:-1200}))
job="$1"; tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT
while (( SECONDS < DEADLINE )); do
code=$(curl -sS --max-time 20 -o "$tmp" -w '%{http_code}' \
-H "Authorization: Bearer ${SUME_API_KEY:?set SUME_API_KEY}" "$BASE/v1/jobs/$job/status")
if [[ "$code" == 429 || "$code" == 5?? || "$code" == 000 ]]; then
sleep "$(( ${RETRY_SLEEP:-5} ))"; continue # transient: try again
elif [[ "$code" != 200 ]]; then
echo "HTTP $code: $(cat "$tmp")" >&2; exit 3 # 401, 404...: retrying cannot help
fi
status=$(jq -r '.data.sume_status' "$tmp")
case "$status" in
completed) jq -r '.data.job.result.artifacts[].url' "$tmp"; exit 0 ;;
failed|canceled) jq -c '.data.job.error' "$tmp" >&2; exit 1 ;;
esac
sleep "$(jq -r '[(.data.next_poll_after_seconds // 2), 2] | max' "$tmp")"
done
echo "deadline reached; job $job may still be running" >&2
exit 2Using it in a pipeline
Keep secrets out of logs: the script prints response bodies only on a non-retryable error, and those bodies contain a request id and message, not your key.
- Submit with a stable
Idempotency-Keyso a re-run of the CI job does not bill a second render. - Capture the job id before polling and save it as a build artifact. On exit 2, the next run can resume by id.
- Treat 1 as a content failure and 3 as a configuration failure when you route alerts.
Limits
I ran all four exit paths against a local mock: completed, failed, a one-second deadline, and a 404. It is a mock of the documented shapes, so verify the artifact path (.data.job.result.artifacts[].url) against one real completed job before you depend on it. The script has no retry cap for transient errors other than the deadline, and it polls sequentially; to wait on many jobs, run one copy per job or move to a proper client. Webhooks are cheaper for long renders if a public receiver is available.
Sources
Related posts
More in Integrations
- Bun.serve webhook receiver for Sume: raw body, verify, dedupe
A 24-line Bun.serve receiver for Sume job webhooks: read the raw body first, verify sume-v1, dedupe on job_id, answer 204 before the work. Tested on Bun 1.4.
- Claude Code MCP whitespace warning: a pasted Sume key with a newline
Claude Code warns when an MCP header or url has leading or trailing whitespace, often a pasted token with a newline. It does not trim it. Fix a Sume entry.
- Claude Code .mcp.json: why ${ANTHROPIC_API_KEY} reads empty for Sume
Claude Code reads credential variables like ANTHROPIC_API_KEY and NPM_TOKEN as empty in a remote url or headers. Name your Sume key variable SUME_API_KEY.
- Claude Code: same MCP name in two scopes, one Sume entry, no merge
If a Sume server is defined in local and project scope, Claude Code loads one definition whole and warns. Order, no field merge, and which tools you get.
Written by Sume