Responses

Create a response, stream it or poll it, cancel it, ask follow-up questions and retry safely.

POST /v1/responses asks Azta one question. The answer cites the provisions it rests on, so every answer links to the provisions it cites. This guide covers how to wait for an answer, how to ask a follow-up and how to retry without paying twice.

Stream or background

Set exactly one of stream and background to true.

  • stream: true answers 200 as server-sent events, in OpenAI's Responses event vocabulary. response.output_text.delta events carry the text as it is written. response.output_text.done carries the whole text and is the authoritative one, so replace what you built from deltas with it. Annotations and sources arrive only in the closing frames.
  • background: true answers 202 with the response in queued. Poll GET /v1/responses/{id} until status is completed, failed or cancelled. The quickstart does this every 5 seconds.

There is no partial text while a response runs. GET /v1/responses/{id} returns an empty output while the status is queued or in_progress; sources is [] until it is completed.

The stream has a deadline of 900 seconds. A stream that reaches it ends with response.failed, but that ends the stream, not the run. The run keeps executing, and it is charged if it completes. Poll GET /v1/responses/{id} for its real outcome, or cancel it. Dropping the connection does not cancel the run either, and reconnecting does not replay the stream.

Cancel a response

POST /v1/responses/{id}/cancel stops a run. A cancel after answer text has started streaming is charged; a cancel before any answer text was delivered is free (see Pricing). Cancelling a response that has already ended answers 409 response_finished. A cancel creates and charges nothing by itself, and a repeat answers the run's current state.

Depth

depth is standard by default. Send deep only when you want the run to research further before it answers; it is priced higher. The response echoes depth. Any other value is 400 invalid_request with param: "depth". See Pricing for the amounts.

Jurisdiction

X-Jurisdiction names the countries whose law the answer draws on, as a comma-separated list of ISO country codes, in any case and spacing. Without it the default is hu. us, oecd, ifrs and unknown codes are refused with 400 unsupported_jurisdiction.

Follow-up questions

The API is stateless: it keeps no conversation for you. To ask a follow-up, send the whole conversation as input, an item array whose last item is the new question with role: "user".

Terminal
#!/usr/bin/env bash
# Ask a follow-up question. The API is stateless, so send the whole
# conversation as `input`; the last item is the new user question.
# Needs curl, jq and uuidgen.
set -euo pipefail

: "${AZTA_API_KEY:?Set AZTA_API_KEY to your Azta API key}"
BASE_URL="${AZTA_BASE_URL:-https://api.azta.ai/v1}"

headers="$(mktemp)"
body="$(mktemp)"
trap 'rm -f "$headers" "$body"' EXIT

# One key per question, reused on every retry of it.
IDEMPOTENCY_KEY="$(uuidgen | tr 'A-Z' 'a-z')"

payload='{
  "model": "azta-agent",
  "background": true,
  "input": [
    {"role": "user", "content": "Mennyi az áfa mértéke?"},
    {"role": "assistant", "content": "Az általános kulcs 27%."},
    {"role": "user", "content": "És építőipari szolgáltatásnál?"}
  ]
}'

# retry_after DEFAULT prints the Retry-After seconds, else DEFAULT.
retry_after() {
  local value
  value="$(grep -i '^retry-after:' "$headers" | tail -n 1 | cut -d: -f2 | tr -d ' \r')"
  if [[ "$value" =~ ^[0-9]+$ ]]; then
    echo "$value"
    return
  fi
  echo "${1}"
}

for attempt in 1 2 3; do
  status="$(curl -sS -D "$headers" -o "$body" -w '%{http_code}' "$BASE_URL/responses" \
    -H "Authorization: Bearer $AZTA_API_KEY" \
    -H 'Content-Type: application/json' \
    -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
    -d "$payload")"
  case "$status" in
    200 | 202) break ;;
    429 | 500 | 503) [ "$attempt" -lt 3 ] && sleep "$(retry_after 1)" ;;
    *) echo "Error: http_$status" >&2; exit 1 ;;
  esac
done
[ "$status" = 200 ] || [ "$status" = 202 ] || exit 1
id="$(jq -r '.id' "$body")"

while true; do
  status="$(curl -sS -D "$headers" -o "$body" -w '%{http_code}' "$BASE_URL/responses/$id" \
    -H "Authorization: Bearer $AZTA_API_KEY")"
  case "$status" in
    429 | 503) sleep "$(retry_after 5)"; continue ;;
    200) ;;
    *) echo "Error: http_$status" >&2; exit 1 ;;
  esac
  state="$(jq -r '.status' "$body")"
  case "$state" in
    completed) break ;;
    failed | cancelled) echo "Error: $(jq -r '.error.code // .status' "$body")" >&2; exit 1 ;;
  esac
  sleep 5
done
jq -r '[.output[].content[] | select(.type == "output_text") | .text] | join("")' "$body"

A previous response's output can go back as an item, unchanged, so you do not need to rebuild it. Only the most recent turns are used: at most 40 messages and 200,000 characters, oldest dropped first, each turn kept whole or not at all. A system or developer item is accepted and not replayed into the model, so put instructions in the user turn.

previous_response_id and conversation are refused with 400 unsupported_parameter, because there is no server-side conversation to point at.

Idempotency-Key

Send an Idempotency-Key header on every POST under /v1: 1 to 255 printable ASCII characters, scoped to your API key and kept for 24 hours. A retry that reuses the key is the same request and is charged once. A retry without Idempotency-Key is a new request and is charged again. The OpenAI SDKs add no such header on their own retries, so set it yourself.

RepeatAnswer
Same key, same request, the response existsThe stored response as JSON, with Idempotent-Replayed: true
Same key, still being processed409 idempotency_key_in_use
Same key, a different request400 idempotency_key_reused

A request that is refused frees its key, so you can fix it and send it again. See Errors for which failures to retry.

On this page