> ## Documentation Index
> Fetch the complete documentation index at: https://qodex.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Run tests with a generic shell

> Use a portable curl and jq script to trigger Qodex, poll for completion, and fail any CI job on a failed run.

# Run tests with a generic shell

Use this recipe when your CI provider does not have a dedicated page, or when you want one script that works in any pipeline.

The script starts a Qodex run, polls until it finishes, and exits non-zero when the run fails or times out.

## Before you start

You need:

* A Qodex project with runnable scenarios.
* A project API key from **Settings > Platform > API keys**.
* `bash`, `curl`, and `jq` on the runner.

## Add the script

Save this file at `scripts/qodex-run.sh` and make it executable.

```bash theme={null}
#!/usr/bin/env bash
set -euo pipefail

: "${QODEX_PROJECT:?QODEX_PROJECT is required}"
: "${QODEX_API_KEY:?QODEX_API_KEY is required}"

HOST="${QODEX_HOST:-https://agents.qodex.ai}"
ENV_NAME="${QODEX_ENV:-staging}"
TAGS_RAW="${QODEX_TAGS:-}"
TIMEOUT="${QODEX_TIMEOUT:-1500}"
POLL="${QODEX_POLL:-10}"

if [ -n "$TAGS_RAW" ]; then
  TAGS_JSON=$(printf '%s' "$TAGS_RAW" | jq -Rcn '[inputs | split(",")[] | gsub("^\\s+|\\s+$"; "")] | map(select(length > 0))')
  BODY=$(jq -cn --arg env "$ENV_NAME" --argjson tags "$TAGS_JSON" '{environment:$env,tags:$tags}')
else
  BODY=$(jq -cn --arg env "$ENV_NAME" '{environment:$env}')
fi

RESPONSE=$(curl -fsS -X POST \
  "$HOST/api/projects/$QODEX_PROJECT/webhooks/trigger" \
  -H "Authorization: Bearer $QODEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$BODY")

RUN_ID=$(echo "$RESPONSE" | jq -r .testRunId)
if [ -z "$RUN_ID" ] || [ "$RUN_ID" = "null" ]; then
  echo "Failed to start run. Response: $RESPONSE" >&2
  exit 1
fi

VIEW_URL="$HOST/p/$QODEX_PROJECT/test-runs/$RUN_ID"
echo "Started Qodex run $RUN_ID"
echo "View at $VIEW_URL"

DEADLINE=$(( $(date +%s) + TIMEOUT ))
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
  STATUS=$(curl -fsS \
    "$HOST/api/projects/$QODEX_PROJECT/test-runs/$RUN_ID" \
    -H "Authorization: Bearer $QODEX_API_KEY" | jq -r .status)
  echo "status=$STATUS"
  case "$STATUS" in
    completed) echo "Run passed: $VIEW_URL"; exit 0 ;;
    failed) echo "Run failed: $VIEW_URL"; exit 1 ;;
    *) sleep "$POLL" ;;
  esac
done

echo "Timed out waiting for Qodex run $RUN_ID" >&2
exit 1
```

## Run it

```bash theme={null}
# staging smoke
QODEX_ENV=staging QODEX_TAGS=smoke ./scripts/qodex-run.sh

# production critical checks
QODEX_ENV=prod QODEX_TAGS=smoke,critical ./scripts/qodex-run.sh

# full active suite
QODEX_ENV=staging ./scripts/qodex-run.sh
```

## Add it to any CI provider

The CI provider only needs to expose `QODEX_PROJECT` and `QODEX_API_KEY` as environment variables, then run the script as a normal step.

```yaml theme={null}
# Example shape
steps:
  - run:
      name: Qodex smoke
      command: |
        QODEX_ENV=staging QODEX_TAGS=smoke ./scripts/qodex-run.sh
```

The job passes only when the Qodex run reaches `completed`. HTTP errors, failed runs, and timeouts fail the job.

## Related

<CardGroup cols={2}>
  <Card title="Run tests via webhook" icon="webhook" href="/run-tests-via-webhook" />

  <Card title="Run tests in CI" icon="git-branch" href="/run-tests-in-ci" />
</CardGroup>
