---
title: "CLI automation"
description: "Use the lookio CLI in scripts, CI and agents, with automatic JSON output, ndjson, polling for imports, safe retries and destructive commands."
canonical: "https://lookio.app/docs/developers/cli/automation"
updated: "2026-11-08"
---

# CLI automation

Scripts and AI agents can use the CLI as easily as a person.

## JSON outside a terminal

When stdout is not a terminal, every command prints JSON. A pipe, a CI job or an agent never needs `--json`.

| Mode     | When                       | Output                                             |
| -------- | -------------------------- | -------------------------------------------------- |
| `json`   | Default outside a terminal | The whole result, indented.                        |
| `table`  | Default in a terminal      | A table or a list of fields, with colour.          |
| `ndjson` | `--output ndjson`          | One JSON object per line. For a list, one per row. |

`LOOKIO_NO_TUI` forces JSON in a terminal. `NO_COLOR` removes colour and nothing else.

In JSON, list commands print the whole API response: `data`, `has_more`, `next_cursor` and `limit`. For the next page, pass `next_cursor` as `--cursor`.

```bash
lookio resources list --limit 100 | jq -r '.data[] | select(.status == "failed") | .id'
```

Progress notes go to stderr, in a terminal only. Warnings always go to stderr.

## Errors

A failure prints one JSON error on **stderr** and exits with a code. It is the API's error format. A failure before the server has the same format, with `request_id` set to `null`.

```json
{
  "error": {
    "type": "not_found_error",
    "code": "resource_not_found",
    "message": "Resource not found.",
    "docs_url": "https://lookio.app/docs/developers/errors#resource_not_found"
  },
  "request_id": "req_0123456789abcdef0123456789abcdef"
}
```

Branch on the exit code or on `error.code`, never on the message. See [Exit codes](/docs/developers/cli/exit-codes).

## Wait for an import

An import returns `queued` at once. Poll until `ready` or `failed`, then stop.

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

id=$(lookio resources add-url https://example.com/pricing --tags pricing | jq -r '.id')

for _ in $(seq 1 60); do
  status=$(lookio resources get "$id" | jq -r '.status')
  case "$status" in
    ready)  echo "ready"; break ;;
    failed) lookio resources get "$id" | jq -r '.status_detail' >&2; exit 1 ;;
  esac
  sleep 5
done
```

A site takes minutes, so give it a longer loop.

## Retries

The CLI retries by itself, up to 3 times, with jittered backoff and `Retry-After`:

- It retries `429` for every command.
- It retries `408` and `5xx` for reads, `PUT`, `DELETE` and `POST`.
- It does not retry a `PATCH` (`resources update`, `assistants update`, `members set`) after a `5xx`: the change may have arrived. Read the resource again before you retry.

Every `POST` sends an `Idempotency-Key`: one UUID per command, the same on each automatic retry. So a retried `lookio ask` is not charged twice, and a retried import does not make two resources. If you run the command again, it is a new request with a new key.

Do not put your own retry loop around a command. To retry yourself, use `--no-retry`.

The CLI never retries an out-of-credits failure. It exits with code `7`, so a job can stop and tell a person.

One request stops after 60 seconds. Change it with `--timeout`.

## Destructive commands

Every delete command asks for confirmation in a terminal: resources, assistants, widgets, accesses and tags. Without a terminal, it does not wait: it exits with code `2` and an error that names `--force`.

## CI example

```yaml title=".github/workflows/refresh-docs.yml"
name: Refresh Lookio resources
on:
  schedule:
    - cron: "0 6 * * 1"

jobs:
  refresh:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - name: Re-index the docs site
        env:
          LOOKIO_API_KEY: ${{ secrets.LOOKIO_API_KEY }}
        run: npx @lookio/cli resources reindex "$RESOURCE_ID"
```

Use an [API key](/docs/guides/api-keys) with only the scopes the job needs: `resources:write` here.

## For AI agents

```bash
lookio help --json
```

prints all commands, flags, exit codes, the retry policy and the error format as one JSON document. An agent that reads it first does not guess flags.

- Call `lookio account` first. `credential.scopes` lists what the credential may do.
- Do not pass `--json`. Outside a terminal, the output is already JSON.
- For an endpoint without a command, use `lookio api`.
