# Scripts and CI

> Use bitsrun in scripts and CI pipelines with JSON output, predictable exit codes and a session token in the BITSRUN_TOKEN environment variable.

Source: https://bitsrun.com/docs/cli/automation/
Updated: 2026-11-09

Every bitsrun command works without a person at the keyboard. Three things make that reliable: `--json` output, exit codes you can check, and a token you pass in an environment variable instead of signing in through a browser.

## JSON output

Add `--json` to any command to get the API's JSON instead of text:

```bash
bitsrun ls --json
```

```json
{
  "endpoints": [
    {
      "id": "01k8y4000d3m00000000000011",
      "name": "Support chat",
      "state": "sleeping",
      "gpu": "l4",
      "url": "https://01k8y4000d3m00000000000011.endpoint.bitsrun.com/v1"
    }
  ]
}
```

The example is shortened; real output includes every field the API returns. Pipe it into a tool such as `jq`:

```bash
# The URL of one endpoint
bitsrun status "Support chat" --json | jq -r '.endpoint.url'

# Total spend this month, in dollars (the API counts in millionths of a dollar)
bitsrun usage --json | jq '.totalMicros / 1000000'
```

bitsrun writes its normal output to stdout and its questions and progress lines (like `Copying 2.1 GB of 9.4 GB, 22%`) to stderr, so `--json` output stays clean when you pipe it.

## Exit codes

| Code | Meaning |
|---|---|
| `0` | It worked. |
| `1` | It failed: for example you are not signed in, an endpoint was not found, or the API refused the request. |
| `2` | The command was used wrongly: an unknown option, a missing argument or an invalid value. |

Errors go to stderr as one friendly sentence, plus a **Request id for support** when the API returned one. Include that id if you write to support@bitsrun.com.

## Commands that would ask a question

In a script, nobody can type an answer, so commands that delete something refuse instead of waiting:

```text
Deleting cannot be undone. Add --yes to delete Code helper without a prompt.
```

Add `--yes` (or `-y`) to `rm`, `keys revoke` and `uploads rm` when you mean it.

## Sign in for CI with BITSRUN_TOKEN

When `BITSRUN_TOKEN` is set, bitsrun uses it as its CLI session token instead of the one saved on the computer. A CLI session token is what `bitsrun login` saves; it lasts 90 days.

Give CI its own session, separate from your laptop, so you can sign it out on its own:

1. On a computer you trust, sign in into a separate config folder, named so you recognize it later:

   ```bash
   BITSRUN_CONFIG_DIR="$HOME/.bitsrun-ci" bitsrun login --no-browser
   ```

2. Read the token it saved:

   ```bash
   jq -r '.[].token' "$HOME/.bitsrun-ci/credentials.json"
   ```

3. Store that value as a secret in your CI system (for example a masked variable named `BITSRUN_TOKEN`), then delete the folder: `rm -rf "$HOME/.bitsrun-ci"`.

> [!WARNING]
> The token manages your account: it can deploy and delete endpoints and create and revoke API keys. Keep it in your CI system's secret store only, never in the repository. To cut it off, open the dashboard, go to **API keys**, find it under **Signed-in CLIs** and click **Sign out**.

The session appears in **Signed-in CLIs** with its expiry date. Repeat these steps before it expires.

> [!NOTE]
> Your app does not need this token to call a model. Apps send an API key, which can be limited to one endpoint. The token is only for managing your account from CI. See [API keys](https://bitsrun.com/docs/api/api-keys/).

## Example: deploy from a pipeline

This job makes sure an endpoint exists and prints its URL for the next step. It works in any CI system that runs a shell.

```bash
#!/usr/bin/env bash
set -euo pipefail

# Install bitsrun into the job's home folder
curl -fsSL https://get.bitsrun.com | sh
export PATH="$HOME/.local/bin:$PATH"

# BITSRUN_TOKEN comes from the CI secret store
NAME="Support chat"
if ! bitsrun status "$NAME" --json > endpoint.json 2>/dev/null; then
  bitsrun deploy qwen3:8b --name "$NAME" --idle 5m --json > deployed.json
  jq '{endpoint: .endpoint}' deployed.json > endpoint.json
fi

echo "ENDPOINT_URL=$(jq -r '.endpoint.url' endpoint.json)"
```

A deploy of a gated or private Hugging Face repo waits while the model is copied into your storage, so give that step a generous timeout the first time.

## Run the coding agent in CI

The coding agent has its own headless mode for scripts, with text or JSON output: `bitsrun -p "<task>"`. It needs a model set up first. See [Headless runs](https://bitsrun.com/docs/agent/headless/).
