cURL & jq
curl and jq are all you need to explore the Elastio API, script one-off exports or debug what another client is sending.
Set up your shell
Section titled “Set up your shell”Export your tenant and a personal access token once per session. Keep the token out of your shell history by reading it from a prompt or a secrets manager.
export ELASTIO_TENANT="acme"read -rs ELASTIO_TOKEN && export ELASTIO_TOKEN # paste the token, then Enterexport ELASTIO_BASE="https://${ELASTIO_TENANT}.app.elastio.com"A small helper keeps commands short. --fail-with-body (curl 7.76+) makes curl exit non-zero on HTTP errors while still printing the error envelope.
elastio() { local path="$1"; shift curl -sS --fail-with-body "${ELASTIO_BASE}${path}" \ -H "Authorization: Bearer ${ELASTIO_TOKEN}" \ -H "Accept: application/json" "$@"}Check that it works by listing open incidents:
elastio "/gateway/api/v2/incidents?status=open&limit=5" | jq .jq recipes
Section titled “jq recipes”Select a few fields
Section titled “Select a few fields”Incidents come back as { "total": …, "data": [ … ] }. Pull out what you need:
elastio "/gateway/api/v2/incidents?status=open&limit=50" \ | jq -r '.data[] | [.handle, .finding.severity, .finding.asset_name, .opened_at] | @tsv'.finding can be null when the finding is no longer available, so a // "-" fallback keeps the output tidy:
elastio "/gateway/api/v2/incidents?status=open" \ | jq -r '.data[] | "\(.handle)\t\(.finding.severity // "-")\t\(.finding.title // "-")"'Count and group
Section titled “Count and group”elastio "/gateway/api/v2/threats?page_size=100" \ | jq '.data | group_by(.threat_kind) | map({threat_kind: .[0].threat_kind, rows: length})'Build query strings safely
Section titled “Build query strings safely”Let curl encode parameters instead of pasting them into the URL. -G turns --data-urlencode pairs into a query string:
elastio "/gateway/api/v2/incidents" -G \ --data-urlencode "status=open" \ --data-urlencode "severity=critical,high" \ --data-urlencode "search=prod-db"Send a JSON body
Section titled “Send a JSON body”Build bodies with jq -n so quoting and escaping are always right. This posts a test message through a Slack integration:
SLACK_ID="3f8d2a6c-1b4e-4c7a-9e2d-7a5b1c9f0e43"body=$(jq -n --arg channel "C0ADDKEPP8V" '{channel_id: $channel}')
elastio "/gateway/integration/v1/slack/${SLACK_ID}/test" \ -X POST -H "Content-Type: application/json" -d "$body"Follow pagination
Section titled “Follow pagination”Most list operations return one page at a time. Operations that use page_size and next_page_token return the next token in pagination.next_page_token; keep requesting until it is empty. This loop exports every finding for one hunt result as JSON Lines:
SCAN_JOB_ID="9b2f4c1e-6d7a-4e8b-a1c3-5f0e2d4b6a71"token=""while :; do args=(-G --data-urlencode "page_size=100") [[ -n "$token" ]] && args+=(--data-urlencode "next_page_token=${token}")
page=$(elastio "/gateway/api/v1/scanjobs/${SCAN_JOB_ID}/threats" "${args[@]}") || exit 1 jq -c '.data[]?' <<<"$page"
token=$(jq -r '.pagination.next_page_token // empty' <<<"$page") [[ -z "$token" ]] && breakdone > findings.jsonlNot every operation paginates the same way. Some use pageSize / pageToken, a few use page_token, and others use limit / offset with a total count. Check the parameters of the operation in the API reference and see Pagination.
Common pitfalls
Section titled “Common pitfalls”- Single quotes stop expansion.
-H 'Authorization: Bearer $ELASTIO_TOKEN'sends the literal text$ELASTIO_TOKEN. Use double quotes. - Unquoted URLs.
&in an unquoted URL backgrounds the command and drops the rest of the query. Always quote URLs, or use-G --data-urlencode. - Raw page tokens. Tokens are opaque and can contain
=,+or/. Pass them with--data-urlencode, as in the loop above, rather than pasting them into the URL. - Silent failures. Without
--failor--fail-with-body, curl exits0on a401or500and your script carries on with an error body. The gateway’s error envelope looks like{ "status": 404, "error": "Not Found", "message": "…" }. See Errors. - Wrong host. The API lives on your tenant host,
https://<tenant>.app.elastio.com, not on a shared domain. - Tokens in logs.
curl -vprints theAuthorizationheader. Redact it before you share output.