# Commands

Every API v3 operation is a `cademi` command, generated from the API contract. This page explains how an operation becomes a command, how to find commands, and how to pass parameters and bodies, read the output, paginate, and handle writes and asynchronous operations.

## From operation to command

The command follows the operation's `operationId`: each dot-separated segment becomes a word, underscores become hyphens, and the last segment is the verb.

| operationId | Command |
|---|---|
| `products.list` | `cademi products list` |
| `products.publications.create` | `cademi products publications create <product_id>` |
| `modules.list` | `cademi modules list <product_id>` |
| `credentials.current_policies` | `cademi credentials current-policies` |
| `products.access_schedules.rules.update` | `cademi products access-schedules rules update <product_id> <access_schedule_id> <release_rule_id>` |

The command name comes from the `operationId`, not from the path: `modules.list` is `GET /products/{product_id}/modules`. In the [API reference](https://cademi.dev/api/reference.md), the last segment of each operation page's URL is its `operationId`.

`cademi --help` lists the resource groups, and `cademi <group> --help` lists their commands. The help of an operation command shows its description, the API route, the required permission, the path parameters, and the body fields:

```sh
cademi products get --help
```

```text
Retrieves a product by its public ID.

The `offer` object is included only when the credentials have the `products.read_offer` permission.

The response includes an `ETag` representing the current revision. Send this value in the `If-Match` header when updating the product to avoid overwriting a newer version.

API: GET /api/v3/products/{product_id}
Permission: products.read
  <product_id>: Public ID of the product, prefixed with `prd_`.

Usage:
  cademi products get <product_id> [flags]
```

The event stream connection (`cademi event-streams events get`) is the only operation that does not run as a regular command: use [`cademi listen`](https://cademi.dev/cli/listen.md).

## Finding commands

`cademi commands` lists every command in one table: the commands generated from the API, with their route, and the built-in commands (`auth`, `api`, `listen`, `config`, and so on), with `-` as the route. Pass a prefix to list only the commands that start with it:

```sh
cademi commands modules
```

```text
COMMAND                              ROUTE                                                                SUMMARY
cademi modules content list          GET /api/v3/products/{product_id}/modules/{module_id}/content        List module contents
cademi modules content order update  PUT /api/v3/products/{product_id}/modules/{module_id}/content/order  Reorder module contents
cademi modules copies create         POST /api/v3/products/{product_id}/modules/{module_id}/copies        Duplicate a module
cademi modules create                POST /api/v3/products/{product_id}/modules                           Create a module
cademi modules delete                DELETE /api/v3/products/{product_id}/modules/{module_id}             Delete a module
cademi modules get                   GET /api/v3/products/{product_id}/modules/{module_id}                Retrieve a module
cademi modules list                  GET /api/v3/products/{product_id}/modules                            List modules
cademi modules publications create   POST /api/v3/products/{product_id}/modules/{module_id}/publications  Publish a module
cademi modules update                PATCH /api/v3/products/{product_id}/modules/{module_id}              Update a module
```

`--generated` lists only the commands generated from the API, and `--builtin` only the built-in commands. You can use one or the other, not both. A prefix that matches no command fails with exit code `2`.

With `--json`, `-o yaml`, or `--jq`, the command describes the whole CLI in one document, so a script or an agent does not need to run `--help` on each command:

| Field | Content |
|---|---|
| `cli_version`, `api_release` | The CLI version and the API release it was built for. |
| `global_flags` | The flags every command accepts. |
| `flag_sets` | Flags shared by many commands, grouped in named sets (`output`, `body`, `idempotency`, `async`, `pagination`, `confirm`, and so on). Each set is described once here, and commands refer to it by name. |
| `commands[]` | One entry per command: `command`, `summary`, `args` (positional arguments, in order), `operation_id`, `method`, `path`, `permissions`, `deprecated`, `flag_sets` (the sets the command accepts), and `flags` (its other flags, with name, type, default, and description). |

Built-in commands have no `operation_id`, `method`, `path`, or `permissions`, and fields without a value are left out. The entry for `cademi products delete`:

```sh
cademi commands products delete --json
```

```json
{
  "command": "cademi products delete",
  "summary": "Delete a product",
  "args": [
    "product_id"
  ],
  "operation_id": "products.delete",
  "method": "DELETE",
  "path": "/api/v3/products/{product_id}",
  "permissions": [
    "products.delete"
  ],
  "flag_sets": [
    "output",
    "idempotency",
    "confirm"
  ]
}
```

`--brief` keeps only `command`, `summary`, `args`, `method`, and `path` for each command, without flags or permissions. It gives a small index of the whole CLI. Start with it, then ask for the full detail of one group with a prefix:

```sh
cademi commands --brief --json
cademi commands products --json
```

`--jq` filters the document. For example, to list every delete command of the `products` group:

```sh
cademi commands products --json --jq '.commands[] | select(.method == "DELETE") | .command'
```

```text
cademi products access-schedules delete
cademi products access-schedules rules delete
cademi products delete
```

## Path and query parameters

Path parameters are positional arguments, in the order they appear in the path. Query parameters are flags named after the parameter, with underscores replaced by hyphens:

```sh
cademi products get prd_12
cademi products list --status published --showcase-id shw_01J8Z1... --limit 50
cademi users list --tag-id tag_01,tag_02
```

Array parameters (`tag_id[]`) accept a comma-separated list or a repeated flag. Enum parameters list their allowed values in `--help` and complete them in the shell. See [Pagination and filtering](https://cademi.dev/api/pagination-and-filtering.md) for the filters and sort orders of each collection.

## Request bodies

Commands for operations that take a body accept three flags, which you can combine:

| Flag | Value |
|---|---|
| `-d`, `--data` | A JSON body: a literal, `@file.json`, or `@-` to read standard input. |
| `-f`, `--field key=value` | A string field. |
| `-F`, `--typed-field key=value` | A typed field: `true`, `false`, `null`, a number, a JSON value starting with `{`, `[`, or `"`, or `@file` to read a file's content as a string. |

`-f` and `-F` are applied on top of the object given with `--data`, and repeat for each field. Nested fields use dots (`a.b=v`) or brackets (`a[b]=v`). `key[]=value` appends to an array. Arrays of objects need `--data`.

```sh
cademi users create -f name="Ana Souza" -f email=ana@example.com -F send_credentials=false

cademi webhooks create -f url=https://example.com/hooks/cademi \
  -f 'event_types[]=user.created' -f 'event_types[]=enrollment.created'

cademi certificates template update prd_12 -F enabled=true \
  -f criteria.kind=progress_percent -F criteria.percent=80

cademi products create -d @product.json
```

Quote fields that contain `[]` so your shell does not expand them. When an operation requires a body and you pass no body flag, the command fails with exit code `2` and asks you to use `-f`, `-F`, or `--data`.

## Output

Commands print the `data` of the response. Status messages, warnings, and progress go to standard error, so standard output carries only data.

| Flag | Effect |
|---|---|
| (none) | Lists of objects print as a table in a terminal. Everything else, and any output sent to a pipe or a file, prints as JSON. |
| `--json` | JSON. Same as `-o json`. |
| `-o`, `--output` | `json`, `table`, or `yaml`. |
| `--jq <expression>` | Filters the JSON output with a jq expression. The filter is built in, so `jq` does not need to be installed. Strings print without quotes. |
| `--raw` | The full response envelope (`data`, `page`, and so on) instead of `data`. |
| `-i`, `--include` | The HTTP status and the response headers, such as `ETag`, `X-Request-Id`, and `X-Cademi-Release`, printed to standard output before the body. |

```sh
cademi users list --jq '.[].email'
cademi products get prd_12 -o yaml
cademi products list --raw --jq '.page.next_cursor'
```

Error messages from the API show the error `code`, the message, each item of `details`, a hint for common codes, and the `request_id`. The message language follows `CADEMI_LANG` or your system locale ([Environment and diagnostics](https://cademi.dev/cli/environment.md)). With `--json`, `-o json`, or `--jq`, errors print to standard error as JSON ([Exit codes](https://cademi.dev/cli/exit-codes.md)).

## Pagination

A list command returns one page. `--limit` sets the page size, and `--cursor` requests the page after the one that returned `page.next_cursor`.

`--all` follows `next_cursor` until the last page and prints every item as a single array. `--limit` still sets the size of each request:

```sh
cademi products list --status published --all
cademi products list --all --limit 200 --jq '.[].id'
```

## Writes, retries, and idempotency

Every `POST`, `PATCH`, `PUT`, and `DELETE` carries an `Idempotency-Key` header, a new UUIDv7 per command. The CLI retries a request, with the same key, after a network error, a `500`, `502`, `503`, or `504`, a `409 idempotency_in_progress` or `409 result_uncertain`, or a `429`. It makes up to 3 retries, waiting as long as the API indicates in `Retry-After`, or with exponential backoff when the API gives no wait time. Because the key stays the same, a retry never duplicates an effect.

A `429` is not retried when the API asks for a wait longer than 60 seconds, or when the error is `too_many_streams`: the command stops with exit code `7`.

The key changes with every run of a command. To repeat a write safely across runs, for example after the command failed without a clear result, choose the key yourself with `--idempotency-key` and send the same key again. A key has 1 to 128 characters: letters, digits, `.`, `_`, `:`, and `-`.

```sh
cademi users create --idempotency-key import-ana-2026-09-25 -f name="Ana Souza" -f email=ana@example.com
```

If the API already processed that key with the same body on the same route, it returns the stored result and the CLI warns `Idempotent replay`. Keys are scoped to the credential and remembered for 48 hours. See [Asynchronous operations, batches, and idempotency](https://cademi.dev/api/operations.md).

## Conditional updates

Operations that accept `If-Match` have an `--if-match` flag. Read the resource with `-i` to get its `ETag`, then send it with the update:

```sh
cademi products get prd_12 -i
cademi products update prd_12 -f name="New name" --if-match 'W/"product:prd_12:..."'
```

If the resource changed after you read it, the API returns `412 revision_mismatch` and the command exits with code `6`: read the resource again and retry with the new `ETag`.

## Asynchronous operations

Operations that answer `202` return an operation. By default the command prints it and tells you how to follow it:

```sh
cademi products publications create prd_12
cademi operations wait op_01J8Z3ZQ4H8K2M0T1S9P7YQF5C
```

With `--wait`, the command waits for the operation to finish, prints its progress to standard error whenever the status or the item counts change, and then prints the final operation. `--wait-timeout 10m` limits the wait.

```sh
cademi products publications create prd_12 --wait
```

`cademi operations wait` polls every 2 seconds for the first 30 seconds and every 10 seconds after that. `--timeout` limits the wait.

| Final status | Exit code |
|---|---|
| `succeeded` | `0` |
| `partially_succeeded` | `0`, with a warning that points to `cademi operations items <operation_id>` |
| `failed`, `canceled` | `1` |

A wait that reaches its time limit also exits with `1`, and the operation keeps running. `cademi operations items`, `cademi operations attempts create`, and `cademi operations update` inspect, resume, and cancel operations as described in [Asynchronous operations, batches, and idempotency](https://cademi.dev/api/operations.md).

## Deleting

Delete commands ask for confirmation. When the profile is a production profile, the question starts with `[production]`. Answer `y` or `yes` to proceed (`s` and `sim` are also accepted). Any other answer cancels the command with exit code `130`.

```sh
cademi products delete prd_12
cademi products delete prd_12 --yes
```

`--yes` (`-y`) skips the question. Without a terminal, a delete command fails with exit code `2` unless you pass `--yes`.

## Global flags

| Flag | Effect |
|---|---|
| `-p`, `--profile <name>` | Use this profile for the command. |
| `--base-url <url>` | Use this platform URL instead of the profile's. |
| `--debug` | Log each HTTP request to standard error: method, URL, status, duration, and `request_id`. Secrets are never logged. |
| `-h`, `--help` | Show the help of the command. |
