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.

operationIdCommand
products.listcademi products list
products.publications.createcademi products publications create <product_id>
modules.listcademi modules list <product_id>
credentials.current_policiescademi credentials current-policies
products.access_schedules.rules.updatecademi 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, 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:

cademi products get --help
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.

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:

cademi commands modules
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:

FieldContent
cli_version, api_releaseThe CLI version and the API release it was built for.
global_flagsThe flags every command accepts.
flag_setsFlags 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:

cademi commands products delete --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:

cademi commands --brief --json
cademi commands products --json

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

cademi commands products --json --jq '.commands[] | select(.method == "DELETE") | .command'
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:

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 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:

FlagValue
-d, --dataA JSON body: a literal, @file.json, or @- to read standard input.
-f, --field key=valueA string field.
-F, --typed-field key=valueA 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.

cademi users create -f name="Ana Souza" -f [email protected] -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.

FlagEffect
(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.
--jsonJSON. Same as -o json.
-o, --outputjson, 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.
--rawThe full response envelope (data, page, and so on) instead of data.
-i, --includeThe HTTP status and the response headers, such as ETag, X-Request-Id, and X-Cademi-Release, printed to standard output before the body.
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). With --json, -o json, or --jq, errors print to standard error as JSON (Exit codes).

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:

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 -.

cademi users create --idempotency-key import-ana-2026-09-25 -f name="Ana Souza" -f [email protected]

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.

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:

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:

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.

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 statusExit code
succeeded0
partially_succeeded0, with a warning that points to cademi operations items <operation_id>
failed, canceled1

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.

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.

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

FlagEffect
-p, --profile <name>Use this profile for the command.
--base-url <url>Use this platform URL instead of the profile's.
--debugLog each HTTP request to standard error: method, URL, status, duration, and request_id. Secrets are never logged.
-h, --helpShow the help of the command.

On this page