# CLI shell scripts and CI automation


Use the CLI in automation when you need account/workspace discovery or campaign reads from a shell. Use the [REST API](/rest-api) or [MCP tools](/mcp/tools) for broader product actions; the current CLI does not expose campaign launch, sending, or other write commands.

## Authenticate without a browser

Create an appropriately restricted personal key in Scheduler Zero and store it in your CI platform or secret manager as `SCHEDULER_ZERO_API_KEY`. The CLI consumes that environment variable directly; no `auth login` or local config is needed for each job.

Do not place the actual secret in a script, a repository file, a command-line flag, or an example log. Disable shell tracing around secret setup, avoid publishing a config directory as an artifact, and revoke/replace the key if it leaks. For laptop use, [browser login](/cli/authentication) avoids copying a secret manually.

Verify the effective identity without printing the secret:

```bash
npx --yes @scheduler-zero/sz@0.2.1 whoami --json
```

The identity output includes permission metadata. Treat any organization/workspace data you export according to your own data-handling rules even though it does not contain the key secret.

## Pin versions and check failures

Use a tested version in scheduled jobs. `latest` is convenient interactively, but a new release should not silently change a production script's output fields.

```bash
#!/usr/bin/env bash
set -euo pipefail
: "${SCHEDULER_ZERO_API_KEY:?Set this with your secret manager}"
: "${WORKSPACE_ID:?Set a workspace UUID}"

npx --yes @scheduler-zero/sz@0.2.1 campaigns list \
  --workspace "$WORKSPACE_ID" --limit 100 --offset 0 --json > campaigns.json

jq '.items | length' campaigns.json
```

The example needs Bash and `jq`, and assumes the two environment variables are supplied by your job. `set -euo pipefail` prevents a failed CLI request from being mistaken for an empty successful JSON result. See [exit codes](/cli/reference).

## Discover IDs

Read a compact view of workspace IDs and parent organizations:

```bash
bunx @scheduler-zero/sz@0.2.1 workspaces list --json \
  | jq '.[] | {id, name, organizationName}'
```

Use the unfiltered list when you need `organizationName`; a list filtered with `--organization` returns the underlying workspace rows without the CLI's added parent-name fields. IDs are UUIDs, not names or slugs. Keep `--workspace` explicit in automation instead of relying on someone else's local config default.

## Campaign filters and pagination

```bash
npx --yes @scheduler-zero/sz@0.2.1 campaigns list \
  --workspace "$WORKSPACE_ID" \
  --search "Welcome" \
  --status draft \
  --limit 25 \
  --offset 0 \
  --json | jq '.items'
```

Supported statuses are `all`, `draft`, `active`, `paused`, `archived`, and `completed`. The CLI returns one page per request; it does not auto-paginate. Advance `--offset` deliberately. The following bounded loop stops after an empty or short page and refuses to silently truncate at its safety limit:

```bash
#!/usr/bin/env bash
set -euo pipefail
: "${WORKSPACE_ID:?Set a workspace UUID}"
page_size=100
max_pages=100

for ((page=0; page<max_pages; page++)); do
  response=$(npx --yes @scheduler-zero/sz@0.2.1 campaigns list \
    --workspace "$WORKSPACE_ID" --limit "$page_size" \
    --offset "$((page * page_size))" --json)
  count=$(jq '.items | length' <<< "$response")
  jq -c '.items[]' <<< "$response"
  if ((count < page_size)); then exit 0; fi
done

printf 'Pagination safety limit reached; results may be incomplete.\n' >&2
exit 1
```

This produces newline-delimited campaign JSON. Concurrent campaign changes can affect offset pagination; it is not a transactional snapshot. For schema-sensitive integrations or larger workflows, use the [REST operation schemas](/rest-api/reference) directly.

## Environment separation

For a self-hosted or preview API, provide `SCHEDULER_ZERO_API_URL` and a key issued for that environment. If you use interactive login there, also set `SCHEDULER_ZERO_APP_URL` to the matching web origin. Use a separate `SCHEDULER_ZERO_CONFIG_DIR` for local interactive environments when you want to keep saved credentials apart.

Do not reuse customer production credentials for fixture tests. A profile directory override is not a substitute for selecting the correct API origin or checking the actual identity. See [configuration reference](/cli/reference) and [authentication](/cli/authentication).

## CLI, REST or MCP?

| Interface | Best fit |
| --- | --- |
| CLI | Shell commands, readable lists, account discovery and campaign inspection |
| REST API | Typed integrations, schema validation and broader product read/write operations |
| MCP | AI clients discovering and invoking permission-scoped capabilities |

All three use the same product authorization boundary. A key with `campaign:launch` permission does not mean the current CLI has a `campaigns launch` command. Discover actual commands with `sz help`, the [CLI catalog](/cli/catalog.json), or [command pages](/cli/commands). For agents, read [CLI Markdown](/cli/llms-full.txt), [MCP Markdown](/mcp/llms.txt) and [REST Markdown](/rest-api/llms.txt).

