CLI

Manage bem functions, workflows, and calls from your terminal

Hand off to an LLM

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/bem

Requires Go 1.22 or later.

go install github.com/bem-team/bem-cli/cmd/bem@latest

The 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 --help

Verify the install:

bem --version

Authentication

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

Copy 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-staging

Function 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 3

Workflows

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

Invoking 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 2

Calls

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_abc123

Filter 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_abc123

Errors

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_abc123

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

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

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

FlagDescription
--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.
--debugVerbose logging including HTTP request and response details.
--help, -hShow command-specific help.
--version, -vPrint 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

SourcePurpose
BEM_API_KEYAuth. Required unless --api-key is passed on every call.
--base-urlOptional API host override (mostly for staging or self-hosted environments).
$(go env GOPATH)/binWhere 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.

Reference

On this page