CLI automation
Use the lookio CLI in scripts, CI and agents, with automatic JSON output, ndjson, polling for imports, safe retries and destructive commands.
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.
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.
{
"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.
Wait for an import
An import returns queued at once. Poll until ready or failed, then stop.
#!/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
doneA 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
429for every command. - It retries
408and5xxfor reads,PUT,DELETEandPOST. - It does not retry a
PATCH(resources update,assistants update,members set) after a5xx: 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
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 with only the scopes the job needs: resources:write here.
For AI agents
lookio help --jsonprints 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 accountfirst.credential.scopeslists 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.