---
title: CLI Scripting
description: Automate the Capawesome Cloud CLI in scripts and CI — token authentication, non-interactive flags, parsing JSON output, polling builds, and handling failures.
---

# Scripting

The CLI is designed to run unattended. This guide covers what changes when there's no human at the keyboard: authenticating without a browser, running commands non-interactively, parsing JSON output, waiting on long-running jobs, and failing the pipeline when something goes wrong.

If you're looking for ready-made snippets, see [Examples](examples.md). This page explains the underlying behavior so you can adapt them safely.

## Authenticate with a token

Interactive login doesn't work on a build server. Instead, create an [API token](../accounts/tokens.md) and expose it as the `CAPAWESOME_TOKEN` environment variable — the CLI reads it automatically, so you don't need to pass it on every command:

```bash
export CAPAWESOME_TOKEN="$YOUR_CI_SECRET"
npx @capawesome/cli whoami
```

Store the token as a secret in your CI provider and never commit it to version control. You can also pass `--token` on an individual command if you prefer; see [Authentication](authentication.md) for details.

!!! note "Alternative variable"

    `CAPAWESOME_CLOUD_TOKEN` is also accepted and takes precedence over `CAPAWESOME_TOKEN` if both are set.

## Run commands non-interactively

Run a command without a TTY and two things will trip you up: confirmation prompts and missing required values. Pass every required value as a flag, and add `--yes` to skip confirmations:

```bash
npx @capawesome/cli apps:builds:create \
  --app-id "$APP_ID" \
  --platform android \
  --type release \
  --git-ref main \
  --yes
```

The CLI only prompts when both input and output are a terminal and the `CI` environment variable is unset. In CI it therefore won't hang waiting for input — if a required value like `--app-id` is missing, it exits with a non-zero code instead. Always pass the required flags explicitly.

## Pin the CLI version

`npx @capawesome/cli` resolves to the latest version, which can change behavior between pipeline runs. Pin a version so your automation stays reproducible:

```bash
npx @capawesome/cli@4.9.3 whoami
```

## Parse JSON output

Add `--json` to get machine-readable output instead of human-readable text. How you consume it depends on the command.

### Commands that print only JSON

Read-style commands — such as `apps:list`, `apps:builds:get`, or `apps:channels:list` — print nothing but the JSON object. You can pipe them straight into [jq](https://jqlang.github.io/jq/){:target="_blank"}:

```bash
npx @capawesome/cli apps:list --json | jq -r '.[].id'
```

### Commands that stream progress

Commands that do long-running work — `apps:builds:create` and `apps:liveupdates:upload` — print progress lines to standard output while they run and the JSON object only at the very end. Both share the same stream, so piping directly into `jq` fails on the progress lines. Capture the output and extract the trailing JSON block instead:

```bash
OUTPUT=$(mktemp)
npx @capawesome/cli apps:builds:create \
  --app-id "$APP_ID" \
  --platform android \
  --type release \
  --git-ref main \
  --json \
  --yes | tee "$OUTPUT"
BUILD_ID=$(sed -n '/^{/,$p' "$OUTPUT" | jq -r '.id')
rm "$OUTPUT"
```

The JSON is pretty-printed, so it begins with a `{` on its own line. `sed -n '/^{/,$p'` keeps everything from that line to the end and drops the progress above it, while `tee` still shows the progress in your CI logs.

## Wait for a build to finish

By default, `apps:builds:create` and `apps:deployments:create` wait for the job to complete and exit non-zero if it fails — so a failed build stops your pipeline with no extra work on your part.

Pass `--detached` when you'd rather not block, for example to run other steps while the build runs. There's a trade-off: a detached command exits `0` as soon as the build is *queued*, regardless of whether it later succeeds or fails. To get the real outcome, poll [`apps:builds:get`](commands.md#appsbuildsget) — which prints clean JSON — until the status is terminal:

```bash
while true; do
  STATUS=$(npx @capawesome/cli apps:builds:get \
    --app-id "$APP_ID" \
    --build-id "$BUILD_ID" \
    --json | jq -r '.job.status')
  case "$STATUS" in
    succeeded) echo "Build succeeded"; break ;;
    failed|canceled|rejected|timed_out) echo "Build $STATUS"; exit 1 ;;
    *) sleep 10 ;; # queued, pending, in_progress
  esac
done
```

The build's status lives on the nested `job` object (`.job.status`), not at the top level.

## Handle failures

The CLI exits `0` on success and non-zero on failure, so in most CI systems a failed command stops the job automatically. A build or deployment that fails counts as a failure and exits non-zero — unless you detached from it, in which case you own the polling (see above).

To understand *why* a build failed, request an AI-generated summary with `--failure-summary` (powered by [Capawesome Cloud Assist](../assist/index.md)), or fetch one afterward with [`apps:builds:failure-summary`](commands.md#appsbuildsfailure-summary).

!!! tip "Transient network errors"

    The CLI automatically retries failed requests caused by network errors or `5xx` responses, and honors the `HTTPS_PROXY` and `HTTP_PROXY` environment variables — useful behind a corporate proxy.

## Make repeated runs idempotent

A pipeline shouldn't fail just because a resource already exists. `apps:channels:create` accepts `--ignore-errors`, which exits `0` even when the channel is already there, so you can create-then-use without a separate existence check:

```bash
npx @capawesome/cli apps:channels:create --app-id "$APP_ID" --name production --ignore-errors
```

This flag is specific to channel creation.

## Full example: build and deploy in CI

Putting it together — authenticate with a token from a CI secret, pin the version, ensure the channel exists, then build and deploy non-interactively. Because the default behavior waits and fails on a bad build, no extra error handling is needed to fail the pipeline:

```bash
# CAPAWESOME_TOKEN is provided as a CI secret
CLI="npx @capawesome/cli@4.9.3"

# Ensure the channel exists (won't fail if it already does)
$CLI apps:channels:create --app-id "$APP_ID" --name production --ignore-errors

# Build the web app in the cloud and deploy it to the channel
$CLI apps:builds:create \
  --app-id "$APP_ID" \
  --platform web \
  --channel production \
  --git-ref "$GIT_SHA" \
  --yes
```

## Next steps

- [Examples](examples.md) — ready-made recipes for builds, Live Updates, and CI.
- [Usage](usage.md) — command anatomy, global flags, and the `doctor` command.
- [Command reference](commands.md) — every command and its options.
- [Integrations](../integrations/index.md) — wire these scripts into your CI/CD provider.
