CLI
Manage bem functions, workflows, and calls from your terminal
The bem CLI gives you a bem command for every resource in the API — functions, workflows, calls, outputs, errors, and schema inference. It's the fastest way to script ad-hoc work, drop into CI, or test an API change without writing SDK code.
Installation
brew install bem-team/tools/bemRequires Go 1.22 or later.
go install github.com/bem-team/bem-cli/cmd/bem@latestThe binary lands in $(go env GOPATH)/bin (default $HOME/go/bin). If bem isn't on your $PATH after install, add the Go bin directory to your shell profile:
export PATH="$PATH:$(go env GOPATH)/bin"Clone the repo and run the bundled wrapper:
git clone https://github.com/bem-team/bem-cli.git
cd bem-cli
./scripts/run --helpVerify the install:
bem --versionAuthentication
The CLI reads BEM_API_KEY from the environment by default. Generate a key from Settings → API Keys in the bem dashboard, then export it:
export BEM_API_KEY='your-api-key-here'Persist the line in ~/.zshrc or ~/.bashrc to keep it across shell sessions.
You can also pass --api-key per command — useful for CI runners and scripts that target multiple environments:
bem functions list --api-key "$BEM_PROD_KEY"The flag overrides the environment variable when both are set.
Functions
Manage functions — extract, classify, split, join, enrich, payload_shaping, and send.
bem functions list
bem functions retrieve --function-name invoice-extractor
bem functions create \
--function-name invoice-extractor \
--type extract \
--display-name "Invoice Extractor" \
--output-schema-name Invoice \
--output-schema '{"type":"object","required":["invoiceNumber","totalAmount"],"properties":{"invoiceNumber":{"type":"string"},"totalAmount":{"type":"number"}}}'
bem functions update --function-name invoice-extractor --display-name "Invoice Extractor v2"
bem functions delete --function-name invoice-extractorCopy a function (creates a new function with a fresh name from an existing one):
bem functions:copy create --function-name invoice-extractor --new-function-name invoice-extractor-stagingFunction Versions
Every change to a function creates a new version. List or retrieve them with functions:versions:
bem functions:versions list --function-name invoice-extractor
bem functions:versions retrieve --function-name invoice-extractor --version-num 3Workflows
Workflows orchestrate functions into a DAG and are the entry point for calls.
bem workflows list
bem workflows retrieve --workflow-name invoice-intake
bem workflows create \
--workflow-name invoice-intake \
--display-name "Invoice Intake" \
--main-node-name extract \
--node '{"name":"extract","function":{"name":"invoice-extractor"}}'
bem workflows update --workflow-name invoice-intake --display-name "Invoice Intake v2"
bem workflows copy --workflow-name invoice-intake --new-workflow-name invoice-intake-staging
bem workflows delete --workflow-name invoice-intakeInvoking a Workflow
bem workflows call runs a workflow against a file or batch of files. Use @path/to/file inside JSON values to attach files inline:
bem workflows call \
--workflow-name invoice-intake \
--input.single-file '{"inputContent": "@invoice.pdf", "inputType": "pdf"}' \
--wait--wait blocks until the call completes (up to 30 seconds) and returns the finished call object. Omit it to fire-and-forget; the call ID comes back immediately and you can poll with bem calls retrieve.
Workflow Versions
bem workflows:versions list --workflow-name invoice-intake
bem workflows:versions retrieve --workflow-name invoice-intake --version-num 2Calls
A call is one execution of a workflow. Use calls to inspect history and check status:
bem calls list --workflow-name invoice-intake --limit 20
bem calls retrieve --call-id call_abc123Filter by status, reference ID, or date range — see bem calls list --help for the full set of flags.
Outputs
outputs returns the terminal non-error events emitted by completed function executions — the structured payloads your downstream systems consume.
bem outputs list --function-name invoice-extractor --limit 50
bem outputs retrieve --output-id evt_abc123Errors
errors mirrors outputs for terminal error events. Useful when a webhook subscription failed or you want to triage call failures:
bem errors list --workflow-name invoice-intake --limit 50
bem errors retrieve --error-id err_abc123Schema Inference
infer-schema analyzes a sample file and returns a JSON Schema you can paste straight into a function's output_schema. Handy for bootstrapping a new extractor:
bem infer-schema create --file @sample-invoice.pdfPassing files as arguments
Anywhere a command takes a file value, prefix the path with @ to read it inline:
bem workflows call \
--workflow-name invoice-intake \
--input.single-file '{"inputContent": "@invoice.pdf", "inputType": "pdf"}'The CLI sniffs the file type and sends plain text as a string or binary as base64 automatically. Override the encoding when you need to:
# Force base64 encoding
bem <command> --arg @data://payload.bin
# Force string encoding
bem <command> --arg @file://notes.txtTo pass a literal @ at the start of a value (e.g. an email handle), escape it with a backslash:
bem <command> --user '\@alice'Global options
These flags work on every command:
| Flag | Description |
|---|---|
--api-key <key> | API key, overrides $BEM_API_KEY. |
--base-url <url> | Point at a non-default API host. |
--format <auto|explore|json|jsonl|pretty|raw|yaml> | Output format. Defaults to auto. |
--format-error <auto|explore|json|jsonl|pretty|raw|yaml> | Error output format. |
--transform <gjson> | Reshape successful output with a GJSON expression. |
--transform-error <gjson> | Reshape error output with a GJSON expression. |
--debug | Verbose logging including HTTP request and response details. |
--help, -h | Show command-specific help. |
--version, -v | Print the CLI version. |
Output formatting
--format auto (the default) prints a human-friendly table when stdout is a TTY and switches to JSON when piped, so the same command works interactively and in scripts:
# Pretty-printed table
bem functions list
# Raw JSON for jq
bem functions list | jq '.data[] | .functionName'Pin a format explicitly when you need consistent output:
bem functions list --format json
bem functions list --format yaml
bem functions list --format jsonl # one JSON object per line, ideal for streaming--transform lets you reshape responses without piping through jq:
bem functions list --transform 'data.#.functionName'CI/CD
For pipelines, set BEM_API_KEY as a secret and call commands directly. The CLI exits non-zero on API errors, so the build will fail when something goes wrong:
# GitHub Actions example
- name: Promote workflow
env:
BEM_API_KEY: ${{ secrets.BEM_API_KEY }}
run: |
bem workflows retrieve --workflow-name invoice-intake --format json > current.json
bem workflows update --workflow-name invoice-intake --display-name "Invoice Intake $GITHUB_SHA"Use --format json (or jsonl) in CI to keep output stable across CLI versions.
Configuration
| Source | Purpose |
|---|---|
BEM_API_KEY | Auth. Required unless --api-key is passed on every call. |
--base-url | Optional API host override (mostly for staging or self-hosted environments). |
$(go env GOPATH)/bin | Where the binary lives when installed via go install. |
The CLI is stateless — it doesn't write a config file or cache credentials, so machine-to-machine use is just BEM_API_KEY=... plus the command.