# CLI commands

> Learn what each Bannerify CLI command, option, and exit code does.

Canonical: https://bannerify.co/docs/cli/commands

The Bannerify CLI has three commands: `config`, `create`, and `whoami`.

```text
bannerify <command> [options]
```

Run `bannerify --help`, or `bannerify <command> --help`, to print the options of a command. Run `bannerify` with no arguments to start the guided setup when no key is saved and the terminal is interactive. In any other case it prints the help.

## Global flags

| Flag | Description |
| --- | --- |
| `-v, --version` | Print the CLI version |
| `-h, --help` | Print help. It works on every subcommand |

## config

Manage the settings that the CLI keeps between runs. The commands work like `git config`.

| Command | Description |
| --- | --- |
| `bannerify config set <key> [value]` | Save a value. Without `value`, the CLI asks for it in an interactive terminal and fails in a script |
| `bannerify config get <key>` | Print one value |
| `bannerify config list` | Print all saved values |
| `bannerify config delete <key>` | Remove a value. It exits with 1 when the value is not set |

### Keys

| Key | Description |
| --- | --- |
| `apiKey` | The API key that the CLI sends |
| `baseUrl` | The API base URL. The default is `https://api.bannerify.co/v1` |
| `defaultFormat` | The image format when `--format` is absent. The default is `png` |
| `outputDir` | The folder for the output file when `--out` is absent |

The CLI does not check a value when it saves it. A `defaultFormat` other than `png`, `jpeg`, or `webp` fails later, when you run `create image`.

`get` and `list` hide the middle of the `apiKey`, for example `abcd****mnop`. Add `--show` to print it in full:

```bash
bannerify config get apiKey --show
```

`config set apiKey` with no value asks for the key and hides what you type. Add `--show` to see it as you paste a long key:

```bash
bannerify config set apiKey
bannerify config set baseUrl https://api.bannerify.co/v1
bannerify config set defaultFormat webp
bannerify config set outputDir ./renders
bannerify config delete defaultFormat
```

`get` and `list` print the values to stdout, and `list` prints the path of the config file to stderr. The file is `$XDG_CONFIG_HOME/bannerify/config.json`, or `~/.config/bannerify/config.json`, with `0600` permissions.

## create

Generate an image or a PDF from a template.

```bash
bannerify create image <templateId> [options]
bannerify create pdf <templateId> [options]
```

### Options

| Option | Description |
| --- | --- |
| `-o, --out <path>` | The output file. Use `--out=-` to write the file to stdout. The CLI creates missing folders in the path |
| `--format <format>` | The image format: `png`, `jpeg`, or `webp`. Images only. The default is `png`, or `defaultFormat` from the config |
| `--modifications '<json>'` | A JSON array of modifications. Use `-` to read it from stdin |
| `--modifications-file <path>` | Read the array from a JSON file. Use `-` for stdin |
| `--api-key <key>` | Use this API key for the command |
| `--base-url <url>` | Use this API base URL for the command |
| `--json` | Print the result as JSON on stdout |
| `-q, --quiet` | Hide every message except errors |

Without `--out`, the CLI writes `<templateId>.<format>` to the current folder, or to `outputDir` when you set it. A PDF gets the extension `pdf`.

### Modifications

`--modifications` takes an array of objects. Each object names a layer with `name` and sets its fields. The object format that the [MCP tools](https://bannerify.co/docs/mcp/tools#pass-layer-values) accept does not work here.

```bash
bannerify create image tpl_xxx -o out.png \
  --modifications '[{"name":"title","text":"Hello world"}]'
```

If you pass both `--modifications` and `--modifications-file`, the CLI joins the two arrays, with the file first.

An unknown layer name is ignored without an error, so check the spelling if a value does not change. [Modifications](https://bannerify.co/docs/essentials/modifications) lists the fields of every layer type.

### Examples

```bash
# WebP output with one text override
bannerify create image tpl_xxx --format webp -o out.webp \
  --modifications '[{"name":"title","text":"Summer sale"}]'

# A PDF with the same layers
bannerify create pdf tpl_invoice -o invoice.pdf \
  --modifications '[{"name":"total","text":"$29.00"}]'

# Image bytes on stdout, for the next command in a pipeline
bannerify create image tpl_xxx --out=- --quiet > out.png

# Modifications from a file
bannerify create image tpl_xxx -o out.png --modifications-file ./data.json
```

## whoami

Check the API key and print the workspace that it belongs to.

```bash
bannerify whoami
```

| Option | Description |
| --- | --- |
| `--api-key <key>` | Check this key instead of the saved one |
| `--base-url <url>` | Use this API base URL |
| `--json` | Print the result as JSON on stdout |

```text
+ Authenticated as Acme
  project id: proj:xxxxxxxxxx
  created:    2026-01-14T09:12:31.000Z
  key source: config
```

`key source` is `flag`, `env`, or `config`. It tells you which key a script uses. Without `--json`, `whoami` prints this text to stderr, so read the result in a script with `--json`.

Run `whoami` first in a new environment. It reports a key problem before a long batch fails.

## Use it from a script or an agent

The CLI never prompts when stdin or stdout is not a terminal, or when `CI` is set. A missing key fails at once and prints the commands that fix it.

| Behavior | Detail |
| --- | --- |
| Prompting | Only in an interactive terminal |
| stdout | Data only: the file bytes with `--out=-`, the JSON result with `--json`, and the output of `config get` and `config list` |
| stderr | Messages, progress, and errors |
| Key source | The `--api-key` flag, then `BANNERIFY_API_KEY`, then the config file |

### JSON output

Add `--json` to read the result from a script. On success, `create image` prints one object:

```json
{
  "ok": true,
  "command": "create image",
  "templateId": "tpl_xxx",
  "format": "png",
  "path": "/home/dev/out.png",
  "bytes": 80734
}
```

`create pdf` leaves out `format`. When you write the file to stdout, `path` is `stdout`. `whoami` prints the workspace and the key source:

```json
{
  "ok": true,
  "project": {
    "id": "proj:xxxxxxxxxx",
    "name": "Acme",
    "createdAt": "2026-01-14T09:12:31.000Z"
  },
  "keySource": "config"
}
```

When `create` fails, it prints the error object from the API and exits with a non-zero code:

```json
{
  "ok": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "template tpl_xxx not found",
    "docs": "https://bannerify.co/docs/api-reference/errors/code/NOT_FOUND",
    "requestId": "req:1a2b3c4d5e6f7a8b"
  }
}
```

When `whoami --json` fails, `error` holds only a `message`.

Do not combine `--out=-` with `--json`. Both write to stdout, so the JSON line would follow the bytes of the file. Write the file to a path instead.

### Exit codes

| Code | Meaning |
| --- | --- |
| `0` | The command succeeded |
| `1` | Any other error: a missing key, an unsupported format, invalid modifications, an unknown config key, or an error from the API |
| `2` | An authentication error. `create` exits with 2 when the error code is `401`, `403`, `UNAUTHORIZED`, or `FORBIDDEN`. `whoami` exits with 2 when its key check fails |

The API reports an unknown key as [`BAD_REQUEST`](https://bannerify.co/docs/api-reference/errors/code/BAD_REQUEST), so `create` exits with 1 for a wrong key. Run `whoami` to check a key: it exits with 2, or with 1 when you add `--json`.

```bash
bannerify create image tpl_xxx -o out.png --quiet || echo "generation failed with code $?"
```

## Troubleshoot

| Symptom | Cause | Fix |
| --- | --- | --- |
| `No API key found` | The session is not interactive and no key is set | Set `BANNERIFY_API_KEY`, pass `--api-key`, or run `bannerify config set apiKey` once |
| `whoami` prints `Project not found` and exits with 2 | The key does not exist, or someone deleted it | Create a key on the **API Keys** page and save it with `bannerify config set apiKey` |
| `Unsupported format` | `--format` or `defaultFormat` is not `png`, `jpeg`, or `webp`. SVG output is not available | Use one of the three formats |
| `Invalid modifications` | The value is not valid JSON, is not an array, or the file path is wrong | Pass an array of objects, such as `[{"name":"title","text":"Hi"}]` |
| Exit code 1 with `NOT_FOUND` | The template id does not exist | Copy the id with **Copy Template ID** in the menu of the template card |
| Exit code 1 with `BAD_REQUEST` | The API key is wrong, or a modification has a value of the wrong type | The message names the cause. To test a key, run `bannerify whoami` |
| Exit code 1 with `USAGE_EXCEEDED` | The workspace reached its generation limit | See [Quota and limits](https://bannerify.co/docs/account/quota) |
| A stack trace that starts with `TypeError: fetch failed` | `create` could not reach the API, for example because the network is down or `baseUrl` is wrong. The command exits with 1 | Check your connection, and run `bannerify config get baseUrl` |
| A value does not change | The layer name does not match any layer, and Bannerify ignores it | Check the name against [Modifications](https://bannerify.co/docs/essentials/modifications#name-your-layers) |
| Binary data fills the terminal | `--out=-` writes the file to stdout | Redirect it, for example `--out=- > out.png` |

## Learn more

- [Generate from the terminal](https://bannerify.co/docs/cli/overview)
- [Handle errors](https://bannerify.co/docs/api-reference/errors/introduction)
- [Quota and limits](https://bannerify.co/docs/account/quota)

## Related

- [Previous: Generate from the terminal](https://bannerify.co/docs/cli/overview)
- [Next: Manage projects and members](https://bannerify.co/docs/account/project)
- [Documentation index](https://bannerify.co/docs/llms.txt)
