Skip to content
Interactions API
Integrate the video model with the Interactions API
Video sample
This walkthrough uses the Interactions API for inferway/minimax-h3-768p: background create, poll, download.
Prerequisites: bash, curl, jq. The Python sample needs only the standard library; the TypeScript sample needs Node with fetch. Set the INFERWAY_API_KEY environment variable; optionally set INFERWAY_IDEMPOTENCY_KEY to resume a previous create.
- Create: POST a top-level JSON body to /interactions (op=create, mode=background, duration_seconds and prompt) with a persistent Idempotency-Key.
- Poll: query with op=get and the returned interaction_id at a bounded interval; failed/cancelled/expired/recovery_required are terminal errors.
- Download: once succeeded with a nonempty result.download_url, download it with a separate unauthenticated HTTPS request and no redirects.
If the wait limit is reached the job may still be running: keep the interaction ID and Idempotency-Key to resume later; refresh an expired download URL by calling get again.
Privacy tier: retained_7d. Video output is retained under the selected model's policy — not chat's zero-retention.
#!/usr/bin/env bash
# Inferway video generation via the Interactions API.
# Prerequisites: bash, curl, jq.
# Required env: INFERWAY_API_KEY.
# Optional env: INFERWAY_IDEMPOTENCY_KEY — resume a previous create with this
# key; otherwise a fresh key is generated.
# On success the video is saved to ./inferway-video.mp4.
set -u
API_BASE="https://api.inferway.ai/v1"
MODEL="inferway/minimax-h3-768p"
PROMPT="A drone shot along a coastline at dawn."
DURATION_SECONDS=5
SAVE_PATH=inferway-video.mp4
workdir=$(mktemp -d) || exit 1
cleanup() { rm -rf "$workdir"; }
trap cleanup EXIT HUP INT TERM
if [ -z "${INFERWAY_API_KEY:-}" ]; then
echo "error: INFERWAY_API_KEY is required" >&2
exit 1
fi
idempotency_key="${INFERWAY_IDEMPOTENCY_KEY:-}"
if [ -z "$idempotency_key" ]; then
if command -v uuidgen >/dev/null 2>&1; then
idempotency_key=$(uuidgen | tr 'A-Z' 'a-z') || exit 1
elif command -v uuid >/dev/null 2>&1; then
idempotency_key=$(uuid | tr -d '-') || exit 1
else
idempotency_key="iv-$(date +%s%N)-$$"
fi
fi
echo "Idempotency-Key: $idempotency_key"
interactions_url="$API_BASE/interactions"
response_headers=$workdir/headers
response_body=$workdir/body
create_body_file=$workdir/create-body.json
jq -cn \
--arg model "$MODEL" \
--argjson durationSeconds "$DURATION_SECONDS" \
--arg prompt "$PROMPT" \
'{op: "create", model: $model, mode: "background", duration_seconds: $durationSeconds, prompt: $prompt}' \
> "$create_body_file" || exit 1
attempt=0
while :; do
attempt=$((attempt + 1))
rc=0
curl -sS --connect-timeout 30 --max-time 30 \
-D "$response_headers" \
--data-binary @"$create_body_file" \
-H "Authorization: Bearer $INFERWAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $idempotency_key" \
"$interactions_url" > "$response_body" 2> "$workdir/curl-err" || rc=$?
if [ "$rc" -ne 0 ]; then
if [ "$attempt" -ge 5 ]; then
echo "error: create request failed on the 5th attempt: $(cat "$workdir/curl-err")" >&2
exit 1
fi
sleep $((1 << (attempt - 1)))
continue
fi
status=$(head -n 1 "$response_headers" | tr -d '\r' | awk '{print $2}')
case "$status" in
200|202)
id=$(jq -er '.id | select(type == "string" and length > 0)' "$response_body" 2>/dev/null) || {
echo "error: accepted response has no usable id" >&2
exit 1
}
echo "task id: $id"
break
;;
429|5[0-9][0-9])
if jq -e '((.error? // {}) | (type == "object") and .retryable == false) or (.retryable? == false)' "$response_body" >/dev/null 2>&1; then
echo "error: server reported a non-retryable failure (status $status)" >&2
jq -c . "$response_body" >&2 || true
exit 1
fi
if [ "$attempt" -ge 5 ]; then
echo "error: create request failed with status $status on the 5th attempt" >&2
exit 1
fi
retry_after=$(sed -n '1,/^$/s/^Retry-After:[[:space:]]*//Ip' "$response_headers" | head -n 1 | tr -d '\r' | tr -d '[:space:]')
if printf '%s' "${retry_after:-}" | grep -Eq '^[0-9]+$'; then
if [ "$retry_after" -gt 30 ]; then
echo "error: server asked to wait $retry_after s (ceiling 30 s); resume later with:" >&2
echo " INFERWAY_IDEMPOTENCY_KEY=$idempotency_key" >&2
exit 1
else
sleep "$retry_after"
fi
else
sleep $((1 << (attempt - 1)))
fi
continue
;;
*)
if jq -e '((.error? // {}) | (type == "object") and .retryable == false) or (.retryable? == false)' "$response_body" >/dev/null 2>&1; then
echo "error: server reported a non-retryable failure (status $status)" >&2
else
echo "error: create request failed with status $status" >&2
fi
jq -c . "$response_body" >&2 || true
exit 1
;;
esac
done
# --- Poll at most 30 times, 5 s apart, 30 s per request ---------------------
download_url=""
poll=0
while [ "$poll" -lt 30 ]; do
poll=$((poll + 1))
: > "$response_headers"
rc=0
curl -sS --connect-timeout 30 --max-time 30 \
-D "$response_headers" \
--data-binary "$(jq -cn --arg id "$id" '{op: "get", interaction_id: $id}')" \
-H "Authorization: Bearer $INFERWAY_API_KEY" \
-H "Content-Type: application/json" \
"$interactions_url" > "$response_body" 2> "$workdir/curl-err" || rc=$?
status=$(head -n 1 "$response_headers" | tr -d '\r' | awk '{print $2}')
if [ "$rc" -ne 0 ] || [[ ! "$status" =~ ^2[0-9][0-9]$ ]] || ! jq -e . "$response_body" >/dev/null 2>&1; then
echo "polling stopped (status=${status:-n/a}); the job may still be running." >&2
echo "resume later with:" >&2
echo " task id: $id" >&2
echo " INFERWAY_IDEMPOTENCY_KEY=$idempotency_key" >&2
exit 1
fi
state=$(jq -r '.state // empty' "$response_body")
case "$state" in
failed|cancelled|expired|recovery_required)
echo "error: task $id ended in state '$state'" >&2
exit 1
;;
succeeded)
candidate=$(jq -er '.result? | select(type == "object") | .download_url? | select(type == "string" and length > 0)' "$response_body" 2>/dev/null) || candidate=""
if [ -n "$candidate" ]; then
download_url="$candidate"
break
fi
;;
esac
if [ "$poll" -lt 30 ]; then
sleep 5
fi
done
if [ -z "$download_url" ]; then
echo "wait limit reached (30 polls); the job may still be running." >&2
echo "resume later with:" >&2
echo " task id: $id" >&2
echo " INFERWAY_IDEMPOTENCY_KEY=$idempotency_key" >&2
exit 1
fi
case "$download_url" in
https://*) ;;
*)
echo "error: refusing to download a non-HTTPS URL" >&2
exit 1
;;
esac
# --- Download: separate request, no API headers, no redirects ---------------
rc=0
curl -sS --connect-timeout 30 --max-time 30 \
-D "$response_headers" -o "$workdir/video" \
"$download_url" || rc=$?
if [ "$rc" -ne 0 ]; then
echo "error: download failed (network/timeout)" >&2
exit 1
fi
status=$(head -n 1 "$response_headers" | tr -d '\r' | awk '{print $2}')
case "$status" in
2??) ;;
3??)
echo "error: download redirected (status $status); refusing to follow redirects" >&2
exit 1
;;
*)
echo "error: download failed with status $status" >&2
exit 1
;;
esac
mv "$workdir/video" "$SAVE_PATH" || exit 1
echo "SAVED $SAVE_PATH"