# Bird CLI

`bird` is the Bird API as a command line: one binary that sends email, manages sending domains, and configures webhooks. It is built for two callers at once — a human at a terminal, and an agent or script driving it in a loop. Every command emits JSON to stdout by default, writes errors as a structured envelope on stderr, and exits with a semantic code, so a consumer branches on structure rather than parsing prose.

## Install

Homebrew (macOS, Linux):

```bash
brew install messagebird/tap/bird
```

Or the install script (macOS, Linux):

```bash
curl -fsSL https://cli.bird.com/install.sh | sh
```

The script detects your platform, verifies the download, and prints where the binary landed. Pass `--version <x.y.z>` to pin a release or `--install-dir <path>` to choose the destination.

On Windows, use the PowerShell installer at `https://cli.bird.com/install.ps1` instead.

Verify with `bird version`.

## Authenticate

```bash
bird auth login --scope emails:write
```

This opens a browser consent page where you pick a workspace and approve the requested permissions. A bare `bird auth login` grants a read-only baseline; the `--scope emails:write` here is what lets the send below go through, and any command that needs more prints the exact re-login to run. The CLI stores a workspace-bound OAuth token in `~/.config/bird/credentials.json` and refreshes it automatically on use: there is no API key to create or copy around, and the API region is recorded from the workspace so there's no host to configure. On a headless machine or over SSH, `bird auth login --device` prints a code you approve on another device instead of opening a local browser.

Check that the credential works:

```bash
bird auth status
```

`auth status` reports whether a token is configured and whether it validates against the API, plus the workspace, region, and granted scopes. It always exits `0` — branch on the `valid` field in its JSON output. Pass `--offline` to skip the API call, and `bird auth logout` to discard the stored credential.

## First commands

Send an email and read it back by ID:

```bash
bird email send --from onboarding@messagebird.dev --to delivered@messagebird.dev --subject "Hello" --html "<p>Hi from the CLI.</p>"
bird email get em_01ky7ma8y2es1s2akzk53tmjn0
```

The [CLI quickstart](/docs/get-started/quickstarts/cli/email) walks this flow end to end — including Bird's shared onboarding domain and sandbox address, so you can send before verifying a domain of your own.

Mutations take input three ways, and the inline value wins: flags, a JSON body named by `--body-file <path|->` (`-` reads stdin), or both, so one stored template serves many calls (`bird email send --body-file body.json --to x@y.com`). The CLI never reads stdin it wasn't pointed at. Two flags make every write safe to rehearse and retry:

- `--dry-run` prints the resolved request body that would be sent and exits without sending — the verification gate before anything outbound.
- `--idempotency-key <key>` makes a retry safe: the server replays the original response for any duplicate request with the same key, the same idempotency mechanism the [SDKs](/docs/sdks/typescript) use, so a network timeout never means a double send.

Write commands also support `--example`, which prints a complete, valid request body (generated from the API schema, no credentials needed) and exits. Destructive commands (`delete`) require an explicit ID and `--yes`, so a loose retry can't quietly destroy state.

## Output contract

Data goes to stdout as JSON with no flag needed; diagnostics and errors go to stderr, never mixed with data. Lists return a cursor envelope (`{"data": [...], "next_cursor": ...}`) with a default `--limit`, so output is always bounded — pipe to `jq` to extract fields (`bird email list | jq -r '.data[].id'`). On single-record reads (`get`, `show`, `status`), `--format text` (`-f text`) opts into a human-readable card instead.

Failures are a JSON envelope on stderr with machine-branchable fields — `code` (stable ID), `type`, `retryable` and `retry_after`, `param` and `details` for the offending input, and `next_actions` listing runnable `bird` commands to recover. API errors pass the server's error code, request ID, and docs link straight through. See [Errors](/docs/guides/errors) for the underlying API error model.

Exit codes are semantic, so a script or agent branches without reading any text:

| Exit code | Meaning                                                 |
| --------- | ------------------------------------------------------- |
| `0`       | Success.                                                |
| `1`       | Unexpected / unrecognized error. Surface and stop.      |
| `2`       | Invalid flags, arguments, or body.                      |
| `3`       | Resource not found.                                     |
| `4`       | Authentication or authorization failure.                |
| `5`       | Conflict or failed precondition.                        |
| `6`       | Rate limit or server error — retry after `retry_after`. |

Commands never prompt interactively, so an agent or CI job can't hang on a question it didn't ask for; missing input fails fast with exit `2` and an actionable hint. The one blocking command is `bird auth login`, which waits for browser or device approval and then times out.

## Configuration

```bash
bird config show
```

`config show` prints the resolved configuration — the API base URL and where it came from, plus the config, cache, and state paths. The base URL resolves in order: the `--base-url` global flag, the `BIRD_API_URL` environment variable, then the region recorded with your login (`{region}.platform.bird.com`) — so after `bird auth login` there is normally nothing to set. The CLI follows XDG paths (`~/.config/bird`, `~/.cache/bird`, `~/.local/state/bird`); set `BIRD_CONFIG_DIR` to collapse all three under one root, useful for isolated CI or agent sandboxes.

Two global flags work on every command:

- `--format` (`-f`) — `json` (default) or `text` (single-record reads only).
- `--base-url` — override the API endpoint for one invocation, equivalent to `BIRD_API_URL`.

## Discover the surface

```bash
bird commands
```

This prints the entire command tree as JSON — every command, its purpose, its flags with types and defaults, required positionals, and the error contract — so an agent enumerates the full surface in one call instead of scraping `--help`. The working loop is: `bird commands` to see what exists, `--example` or `--help` for the shape, `--dry-run` to preview, then run with `--idempotency-key` for safe retries. Shell completion is available via `bird completion bash|zsh|fish`.

## Common command groups

The groups you'll use first. The CLI covers far more (SMS, WhatsApp, Verify, contacts, audiences, billing, support tickets, and others); run `bird commands` for the full tree.

- [`bird auth`](/docs/cli#authenticate) — `login`, `status`, `logout`: manage the OAuth credential.
- [`bird email`](/docs/get-started/quickstarts/cli/email) — `send`, `get`, `list`: send messages and track their delivery status.
- [`bird email domains`](/docs/guides/email/sending-domains) — `create`, `get`, `list`, `verify`: register sending domains and check DNS verification.
- [`bird email inbound-addresses`](/docs/guides/email/receiving-email) — `create`, `get`, `list`, `update`, `delete`: mint and manage the forwarding addresses Bird receives mail at.
- [`bird email inbound-messages`](/docs/guides/email/receiving-email) — `list`, `get`, `body`, `attachments`: read the mail Bird received.
- [`bird webhooks`](/docs/guides/webhooks) — `create`, `get`, `list`, `test`, `delete`: manage webhook endpoints and fire test deliveries.

## Next steps

- [CLI quickstart](/docs/get-started/quickstarts/cli/email) — install, log in, and send your first email in two minutes.
- [The CLI for agents](/docs/ai/cli-for-agents) — the full agent contract: JSON output, exit codes, `--dry-run`, error envelope, and discovery.
- [SDKs](/docs/sdks/typescript) — the same API surface as typed libraries for TypeScript, Go, and Python.