curl --fail-with-body: keep Sume's error envelope in shell scripts
curl --fail drops the response body on a 4xx. --fail-with-body (curl 7.76.0+) keeps it, so a script can read Sume's error code and retryable flag.

Use curl --fail-with-body. The curl manual says it returns exit code 22 on HTTP 400 or above like --fail, but still outputs and saves the response body; --fail alone suppresses the body. It was added in curl 7.76.0. Sume puts the reason in that body, in an error object with a code, retryable and next_action.
The difference
Without the body, a script sees only the exit code 22 and cannot tell a retry from a fix.
| Option | Exit code on HTTP 400 or more | Body kept |
|---|---|---|
--fail | 22 | No |
--fail-with-body | 22 | Yes |
| neither | 0 | Yes |
Script
Sume's error envelope names a next_action such as fix_input, authenticate, add_funds, retry_later, poll_status, inspect_events or contact_support. The script maps it to an exit code your pipeline can branch on. It uses python3 to read JSON, so no extra tools are needed.
set -u
: "${SUME_API_KEY:?SUME_API_KEY is not set}"
curl -sS --fail-with-body -o resp.json \
-X POST https://api.sume.com/v1/video-router/generate \
-H "x-api-key: $SUME_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: ${1:?usage: submit.sh <stable-key>}" \
-d '{"model":"seedance-2.5","prompt":"A product clip on a desk","resolution":"720p","duration":4,"aspect_ratio":"9:16","mode":"async"}'
rc=$?
if [ "$rc" -eq 0 ]; then
python3 -c 'import json; print(json.load(open("resp.json"))["data"]["request_id"])'
exit 0
fi
if [ "$rc" -eq 22 ]; then
python3 - <<'PY'
import json, sys
e = json.load(open("resp.json"))["error"]
print(f'{e["code"]} retryable={e.get("retryable")} next={e.get("next_action")} req={e.get("request_id")}', file=sys.stderr)
sys.exit(10 if e.get("retryable") else 11)
PY
exit $?
fi
exit "$rc"Exit codes in your pipeline
Exit 10 means retry later with the same key, 11 means fix the request or the account, and 22 never escapes. Anything else is a transport problem such as DNS or a timeout, and the same Idempotency-Key makes a retry safe. The full decision table is in the retry or fix function.
Version check
The manual says the flag arrived in 7.76.0, so check curl --version on the runner before relying on it.
Sources
Related posts
More in Developers
- Cursor Security Review bot on a Sume webhook handler: what to find
Cursor added a Security Review bot on Sep 23. A webhook handler for Sume should pass seven checks: raw body, timestamp window, rotation, empty secret and more.
- Cursor self-hosted machines can't take Sume webhooks on a private URL
Sume rejects localhost, private-network and non-HTTPS webhook URLs. An agent on a self-hosted machine should poll status_url or use a public HTTPS receiver.
- D-ID API uses Basic auth; Sume uses a bearer API key
D-ID's API docs say requests use the Authorization header with Basic credentials. Sume sends Authorization: Bearer with an API key. A short migration checklist.
- Dagster asset retries and Sume: stop a retry from billing twice
A Dagster RetryPolicy re-runs the whole asset function. Give the Sume submit a stable Idempotency-Key and fail terminal jobs with allow_retries=False.
Written by Sume