Use an Idempotency-Key to replay the same decision while its stored answer is available.
decide.sh
# Set SYSTEM_ZERO_API_KEY to your API key before running this example.# Generate once per logical decision; reuse this key when retrying.idempotency_key="$(uuidgen 2>/dev/null \
|| cat /proc/sys/kernel/random/uuid 2>/dev/null \
|| openssl rand -hex 16 2>/dev/null \
|| python3 -c 'from uuid import uuid4; print(uuid4())' 2>/dev/null)"cat > request.json <<'EOF'
{
"model": "system-zero",
"state": "The blender I ordered arrived with a cracked jar.",
"questions": {
"damaged": {
"type": "boolean",
"instructions": "Did the item arrive damaged?"
}
}
}
EOF# Headerless error responses require Python 3 to read error.code.# If x-should-retry is present, resend only when x-should-retry is true. If the# header is absent, resend on 429, or on 409 and 5xx only with a retryable# error.code. No answer also retries. Wait Retry-After plus up to 10 %,# otherwise a random wait doubling from 1 s to 30 s. Stop if Retry-After is over# 60 s. Also stop when a 429 has no Retry-After, or a 5xx has x-should-retry but# no Retry-After.random() { od-An-N4-tu4/dev/urandom | tr-d' '; }
# Stop resending after 10 minutes.start=$(date+%s) attempt=0while:; do:"${SYSTEM_ZERO_API_KEY:?set it first}":"${idempotency_key:?could not make one}"rm-freply.jsonout=$(curlhttps://api.rightnowai.co/v1/system-zero/decide \
--max-time130-sS-oreply.json-D - -w'%{http_code}' \
-H"Authorization: Bearer $SYSTEM_ZERO_API_KEY" \
-H"Content-Type: application/json" \
-H"Idempotency-Key: $idempotency_key" \
-d@request.json)
code=$(printf'%s'"$out" | tail-c3)
retry_header=$(printf'%s\n'"$out" \
| grep-i'^x-should-retry:' | tail-n1)
retry=$(printf'%s\n'"$retry_header" \
| sed's/^[^:]*:[[:space:]]*//;s/[[:space:]]*$//')
after=$(printf'%s\n'"$out" | grep-i'^retry-after:' \
| tail-n1 | sed's/^[^:]*:[[:space:]]*//;s/[[:space:]]*$//')
case$afterin *[!0-9]*|'') after= ;; esacif[-z"$retry_header" ]; thencase$codein000|429) ;;
409|5??)
error_code=$(python3-c'import json, sys
try:
print(json.load(sys.stdin).get("error", {}).get("code", ""))
except (ValueError, AttributeError):
pass' < reply.json2>/dev/null) || breakcase$code:$error_codein409:idempotency_conflict) ;;
409:upload_in_use) ;;
409:session_busy) ;;
5??:service_unavailable) ;;
5??:system_zero_unavailable) ;;
5??:billing_settlement_error) ;;
5??:idempotency_unavailable) ;;
5??:limiter_unavailable) ;;
5??:internal_error) ;;
5??:maintenance) ;;
5??:revoke_notice_unavailable) ;;
*) break ;;
esac ;;
*) break ;;
esacelse["$retry"= true ] || breakcase$codein 4??|5??) ;; *) break ;; esacfiif[-n"$after" ]; thenafter=$(printf'%s'"$after" | sed's/^0*//')
after=${after:-0}["$after"-le60 ] || breakms=$(( after * 1000 + $(random) % (after * 100 + 1) ))
else["$code" != 429 ] || breakif[-n"$retry_header" ]; thencase$codein 5??) break ;; esacficap=$(( attempt < 5 ? 1000 << attempt :30000 ))
ms=$(( $(random) % (cap + 1) ))
fi[ $(( $(date+%s) - start + ms /1000 )) -lt600 ] || breaksleep"$(( ms / 1000 )).$(printf '%03d' $(( ms % 1000 )))"[ $(( $(date+%s) - start )) -lt600 ] || breakattempt=$(( attempt + 1 ))
doneecho"HTTP $code"catreply.json2>/dev/null
Keep the key and body
Generate a fresh key for a new decision. Keep that key and the same JSON body for retries, using the same workspace and API key.
The API compares the canonical JSON body with the method and route. Changing the body while keeping the key returns 400 idempotency_key_reuse.
The header is optional. Without it, repeating a call cannot retrieve an answer lost in transit.
Read a replay
The same key and body can return the completed answer with X-RightNow-Idempotent-Replay: true. The replay has a new request trace and current rate-limit headers.
Replay availability is not guaranteed. If the settled answer cannot be read, the response uses X-RightNow-Idempotent-Replay: unavailable and 409 idempotency_replay_unavailable.
Waiting does not recover an unavailable replay. A new key starts new work and may produce a different answer.
Handle a replay refusal
Code
What to do
idempotency_conflict
The same work is still running. Wait for Retry-After, then retry with the same Idempotency-Key and body.
idempotency_key_reuse
Use the original body with this Idempotency-Key, or a new key for changed work.
idempotency_replay_unavailable
Inspect error.details.original_status. A new key starts new work rather than recovering the old answer.
invalid_idempotency_key
Correct the blank or malformed key.
idempotency_unavailable
Wait for Retry-After, then retry with the same Idempotency-Key.
Use error.retryable and x-should-retry to decide whether to retry. Follow the wait in the response; see Errors.