---
title: "Scripting the Shorebird CLI"
description: "Run Shorebird commands unattended in CI or from coding agents, and read their results as JSON."
---

The Shorebird CLI can run without a person at the keyboard, in a CI job, a script, or a coding agent. This page covers how it behaves there, how to get machine-readable output, and how to run destructive commands safely.

## Non-interactive mode

Shorebird stops asking questions when any of these is true:

* The global `--json` flag is passed.
* stdout or stdin isn’t a terminal, for example when output is piped to a file.
* A CI environment variable is set, such as `CI` (with any value), `BOT=true`, or the variables set by GitHub Actions, Azure Pipelines, Travis, AppVeyor, Cirrus CI, AWS CodeBuild, or Jenkins.

In non-interactive mode, nothing waits for input:

* **Some prompts use a default.** `shorebird init` uses your pubspec name as the app’s display name. `shorebird patch` without `--release-version` builds the app first to guess which release to patch, which costs an extra build or can build the patch without the release’s settings. Always pass `--release-version`; see [Patching](/code-push/patch/) for details. `shorebird patch` without `--track` publishes to `stable`, and with Shorebird 1.6.135 or newer warns that a future release will require `--track`.
* **Other prompts fail.** When there’s no safe default, the command exits with an error and a hint, usually naming the flag that provides the answer. For example, `shorebird patch` fails when the patch build’s native code differs from the release unless you pass `--allow-native-diffs`, and when its assets differ unless you pass `--allow-asset-diffs` (see [Patch stops in CI](/code-push/troubleshooting#patch-stops-in-ci-with-not-publishing-exit-code-70)). `shorebird init` fails if you belong to several organizations and don’t pass `--org-id`.
* **Progress spinners become plain lines** when stdout isn’t a terminal or `--json` is passed. Each step prints `Starting ...` and then `Done ...` or `Failed ...` to stderr.

To keep a job from failing or guessing, pass every input as a flag: the app with `--app-id` or `--flavor`, the release with `--release-version`, and the track with `--track`. For `shorebird patch`, `--release-version=latest` targets the most recently updated release. Authenticate with a [service account](/account/service-accounts/)’s key in the `SHOREBIRD_TOKEN` environment variable, since `shorebird login` needs a browser. In GitHub Actions, a job can [sign in with OIDC](/code-push/ci/github/#sign-in-with-oidc-instead-of-a-stored-key) instead, with Shorebird 1.6.129 or newer.

## JSON output

Pass the global `--json` flag to get one JSON object on stdout instead of human-readable text. `--json` also turns on non-interactive mode and removes color codes.

These commands return structured JSON:

* [`shorebird account`](/account/cli#account-management-commands): `whoami`, `update`, `notifications`, `sessions`
* [`shorebird orgs`](/account/cli#organization-commands): `list`, `get`, `create`, `update`
* [`shorebird auth`](/account/cli#check-which-credential-is-active): `status`, `token`
* [`shorebird apps`](/account/cli#app-management-commands): `list`, `rename`, `transfer`
* [`shorebird tokens`](/account/cli#api-keys-and-sessions): `list`
* [`shorebird invitations`](/account/orgs#accept-an-invitation): `list`, `accept`, `decline`
* [`shorebird channels`](/code-push/tracks#managing-tracks-from-the-cli): `list`, `create`, `delete`
* [`shorebird patches`](/code-push/patch#manage-patches): `list`, `info`, `set-track`, `promote`, `rollback`, `rollforward`
* [`shorebird releases`](/code-push/release): `list`, `info`, `delete`
* [`shorebird members`](/account/orgs#manage-members-from-the-cli): `list`, `add`, `set-role`, `remove`
* [`shorebird service-accounts`](/account/service-accounts#manage-service-accounts-from-the-cli): `list`, `get`, `create`, `set-role`, `delete`, `keys list`, `keys revoke`, `trust list`, `trust add`, `trust remove`
* [`shorebird audit-log`](/enterprise/audit-logs#read-it-with-shorebird-audit-log): `list`, `export`
* `shorebird metrics`: `active-hours`, `adoption`, `downloads`, `heatmap`, `installs`, `new-devices`, `users`, `versions`
* [`shorebird usage`](/account/cli#patch-install-usage)
* `shorebird enterprise`: `get`, `members list`, `members set-role`, `guests`, `orgs list`, `apps list`, `audit-log list`, `audit-log export`, `usage`, `service-accounts list`
* `shorebird release`, `shorebird patch`, `shorebird doctor`, `shorebird flutter versions list`, and `shorebird --version`

`orgs`, `usage`, `members`, `service-accounts`, and `audit-log` need Shorebird 1.6.135 or newer; before it, `shorebird account orgs` listed organizations. `enterprise` needs Shorebird 1.6.136 or newer. Each of their commands’ `--help` ends with an example `--json` run; run it to see which fields `data` holds. `audit-log` returns events in the audit log’s [event format](/enterprise/audit-logs#event-format), and no `service-accounts` command ever returns a key’s secret. The credential matters:

* `shorebird members` works with a `shorebird login` session, a personal API key, or a service account’s key or GitHub Actions OIDC sign-in, unless the account is an Auditor. A release and patch only key can’t use it. Inviting, changing roles, and removing need the Admin or Owner role, which no service account holds.
* `shorebird service-accounts` needs the Admin or Owner role. Reading works with a login session or an Admin’s or Owner’s personal API key (not a release and patch only key), but the commands that change something need a `shorebird login` session with `SHOREBIRD_TOKEN` unset; the API refuses API keys and GitHub Actions OIDC for them. `service-accounts keys create` [opens the console](/account/cli/#commands-that-finish-in-the-console).
* `shorebird audit-log` works with a login session, a key in `SHOREBIRD_TOKEN`, or GitHub Actions OIDC, as long as the credential belongs to an Owner, an Admin, or an Auditor service account; a release and patch only key can’t read it. It needs a plan that includes the audit log.
* `shorebird enterprise` works with a `shorebird login` session or a personal API key. An organization’s service account can’t use it, with a key or GitHub Actions OIDC; a service account the enterprise owns can read only what its role allows, such as the audit log for an Auditor.

Every other command, such as `shorebird init` or `shorebird cache clean`, also accepts `--json` and runs non-interactively. Its logs go to stderr, and when it succeeds it prints a success object whose `data` is empty (`{}`), so stdout always holds exactly one JSON object. `--help` with `--json` works the same way: the usage text goes to stderr. This needs Shorebird 1.6.129 or newer; earlier releases leave stdout empty when these commands succeed.

`shorebird api` is the exception: it prints the API response body as-is and rejects `--json`. See [Calling the API from the command line](/account/api/#calling-the-api-from-the-command-line).

### Success

A successful command prints an object with `status` set to `success`. The command’s result is in `data`, and `meta` says which CLI version and command produced it:

```
shorebird --version --json
```

```
{
  "status": "success",
  "data": {
    "shorebird_version": "1.6.123",
    "flutter_version": "3.47.5",
    "flutter_revision": "ff900e7fbab20fdeb40905ee631baa8d49bd0fa1",
    "engine_revision": "c663a2682e6adfc60a1aaff99ff62baa891dcf87"
  },
  "meta": { "version": "1.6.123", "command": "version" }
}
```

The fields inside `data` depend on the command.

### Errors

A failed command prints an object with `status` set to `error` and exits with a non-zero code:

```
{
  "status": "error",
  "error": {
    "code": "fetch_failed",
    "message": "Failed to fetch channels for app \"<app-id>\"."
  },
  "meta": { "version": "1.6.123", "command": "channels list" }
}
```

`error.code` is one of:

| Code                          | Meaning                                                                                             |
| ----------------------------- | --------------------------------------------------------------------------------------------------- |
| `usage_error`                 | Invalid or missing arguments. Exit code 64.                                                         |
| `interactive_prompt_required` | The command needed an answer to a prompt. Exit code 64.                                             |
| `fetch_failed`                | Shorebird couldn’t retrieve something it needed, such as an app.                                    |
| `precondition_failed`         | A check before the command ran failed, such as not being logged in or not finding `shorebird.yaml`. |
| `software_error`              | The command failed unexpectedly.                                                                    |
| `process_exit`                | The command stopped with a non-zero exit code.                                                      |
| `console_required`            | The action is done in the Shorebird console. `error.hint` holds the link. Exit code 69.             |

When there’s a way to recover, `error.hint` says what to do, such as the flag to add.

When the Shorebird API rejected the request, `error.api_code` holds the server’s error code, such as `shorebird_plan_insufficient_tier` for a feature your plan doesn’t include, and `error.details` holds any extra detail the server sent. Branch on `api_code` rather than on the message text. These fields need Shorebird 1.6.129 or newer.

Caution

Check the exit code as well as `status`. A non-zero exit code always means the command failed. Every `--json` run prints exactly one JSON object on stdout, including failures in the checks that run before a command starts. Most of those, such as not being logged in, report `precondition_failed`.

## Notices from Shorebird

When the account that pays for an app needs attention, for example because patch delivery is paused or nearing the plan’s install limit, or a payment failed, Shorebird 1.6.135 and newer print a notice from the Shorebird server as the last output of `shorebird release` and `shorebird patch`. The notice doesn’t change the exit code. On GitHub Actions it’s a warning annotation, so it shows on the run’s summary page without opening the log. With `--json`, it goes to stderr, and stdout still holds only the JSON object.

## Destructive commands

Commands that delete or remove something in the CLI, such as `shorebird releases delete`, `shorebird channels delete`, `shorebird members remove` and `shorebird service-accounts delete`, ask for confirmation in a terminal. Without a terminal, in CI, or with `--json`, they never prompt, so confirm with `--yes` or the command fails with a usage error and nothing is deleted:

```
shorebird releases delete --app-id <app-id> --release-version 1.0.0+1 --yes --json
shorebird channels delete --app-id <app-id> --name qa --yes --json
```

`releases delete` and `--yes` for `channels delete` need Shorebird 1.6.129 or newer; earlier releases confirmed `channels delete` with `--confirm-name`. `members remove` and the `service-accounts` commands need Shorebird 1.6.135 or newer.

Some commands, such as `shorebird apps delete`, `shorebird account delete` and `shorebird service-accounts keys create`, don’t perform the action: a person finishes it in the Shorebird console. In CI or with `--json`, they change nothing and exit with code 69 and the `console_required` error, whose hint holds the console link. See [Commands that finish in the console](/account/cli/#commands-that-finish-in-the-console) for the full list. From 1.6.121 to 1.6.134, `shorebird apps delete --confirm-name "<display name>"` still deletes the app.

## Retrying rollbacks

`shorebird patches rollback` and `shorebird patches rollforward` succeed when the patch is already in the requested state, so a retried job doesn’t fail. Pass `--require-change` to make the command exit with a usage error when nothing changed, if your script needs to know. With `--json`, the `data.changed` field reports the same thing:

```
shorebird patches rollback --release-version 1.0.0+1 --patch-number 2 --json
```

See [Roll back from the CLI](/code-push/rollback#roll-back-from-the-cli) for the command’s options, and [What happens when a patch is rolled back?](/code-push/rollback#what-happens-when-a-patch-is-rolled-back) for what it does on devices.
