CLI
Agent-first command-line interface for the BeeL. API — run any endpoint from the terminal, sandbox by default.
The BeeL. CLI lets you operate the API from the terminal: create and issue invoices, manage customers and products, validate NIFs, export data — without writing code. It is agent-first by design: compact JSON output for data commands, semantic exit codes, and sandbox by default, so both humans and AI assistants can use it safely.
Commands are derived from the API spec bundled with the installed CLI version, so each release exposes the API surface as of that version — new endpoints become new commands when you update the CLI. For an endpoint the installed version doesn't know yet, use the generic escape hatch.
Installation
npm install -g @beel_es/cli
beel --helpnpx @beel_es/cli --helpHandy for CI pipelines and AI agents — always runs the latest published version.
Requires Node.js 20 or higher. The commands on this page are those of version 0.3.0; check yours with beel --version. Coming from 0.2.x? See Upgrading from 0.2.x.
Authentication
Two ways to sign in — OAuth (a browser sign-in, best for people) or an API key (best for CI and autonomous agents).
beel login # opens the browser; sign in on the BeeL. consent screen (sandbox)
beel login --live # same, for productionPick the account, scopes and environment on the consent screen — no keys to copy around. Tokens are stored (and auto-refreshed) in ~/.config/beel/config.json (chmod 600) and are never printed. Revoke anytime with beel logout (--live for production).
# Option A: env var (recommended for CI/agents)
export BEEL_API_KEY=beel_sk_test_...
# Option B: store a key in the config file (chmod 600)
beel login --api-key beel_sk_test_...
beel login --api-key beel_sk_live_... # both can coexistGet your keys at Settings > API Keys — each environment has its own, separate key, filed by prefix (beel_sk_test_* → sandbox, beel_sk_live_* → production).
When more than one credential is present, precedence is BEEL_API_KEY → OAuth session (refreshed if expired) → stored API key.
Sandbox is the default. Every command uses the test environment unless you pass --live. A live key in BEEL_API_KEY without --live is an error, not a silent upgrade — production access is always explicit.
beel config # shows config path, base URL and the active session / keys (masked)
beel logout # remove stored credentials for the environment (--live for production)Common recipes
Commands follow the beel <resource> <action> pattern, for example beel invoices list or beel customers create. A command acts on one company (NIF), which the CLI works out for you, so you don't pass its ID each time (see Which company a command acts on). These are everyday examples; the full, always-up-to-date list comes from beel commands (see below).
# Invoices
beel invoices list --status PAID --limit 5 # filter + paginate
beel invoices get <invoice_id>
beel invoices create --data @invoice.json # create a draft
beel invoices issue <invoice_id> --wait-for-pdf # emit + wait for the PDF
beel invoices set-status <invoice_id> --status PAID --payment-date 2026-09-01
beel invoices create-corrective <invoice_id> --data @rectificativa.json
beel invoices get-pdf <invoice_id> # returns the PDF download URL
beel invoices create-export --format SUMMARY --output invoices.xlsx
# Customers & products
beel customers list --search "ACME"
beel customers create --data @customer.json
beel customers patch <customer_id> --data '{"email":null}' # PATCH: fields you leave out don't change
beel products list --q "consultoría"
beel products delete <product_id>
# Series, VeriFactu & NIF
beel series list
beel series set-default <series_id>
beel verifactu-configuration get # VeriFactu status of the NIF
beel nif validate --nif B12345678 # checks the AEAT census
# Payment connections: events are addressed by the connection's ID
beel payment-connections list # each connection's id
beel payment-connections list-events <connection_id> --needs-action
beel payment-connections retry-event <connection_id> <event_id>
# Account level
beel webhooks list
beel me get # who the credential belongs to
# Another company of the same account
beel --company <company_id> invoices listWhich company a command acts on
Most commands work on a company (NIF): beel invoices list calls GET /v1/companies/{company_id}/invoices. The CLI fills in {company_id} in this order:
- The
--company <company_id>flag. - The
BEEL_COMPANY_IDenvironment variable. - Your account's only company. The CLI reads your account from
GET /v1/me/identity, then lists its companies.
If your account has more than one company, the CLI does not pick one. It exits with code 2 and lists the candidates (ID, NIF and name) so you can choose. beel companies list shows them too.
Step 3 costs one or two extra requests per command, and your API key or OAuth session needs the companies:list scope. Without that scope those requests answer 403 (exit code 3). In scripts, CI and agents, set BEEL_COMPANY_ID and skip the lookup:
export BEEL_COMPANY_ID=<company_id>
beel invoices listAccount-level commands (webhooks, members, invitations, request-logs, companies list…) work the same way with --account <account_id> and BEEL_ACCOUNT_ID. By default they use the account your credential belongs to, and finding it needs no scope.
Discovering commands
The CLI is agent-first: every command is derived from the API spec, so an assistant can discover the whole surface in one call instead of scraping help text.
beel commands # full command index as JSON — { command, signature, description, method, path }
beel commands --markdown # the same grouped by resource as markdown tablesbeel commands reflects the spec bundled with the installed CLI version — not the live API — so update the CLI to pick up newly added endpoints (or use the escape hatch meanwhile). There's no hand-maintained list to fall out of date. For details of a single command — flags, enums, defaults and body fields, all from the spec — use --help at any level:
beel --help
beel invoices --help
beel invoices create --helpFor commands with a request body, --help also lists the top-level body fields (name, type, whether they're required, and any enums), so you know what to send without leaving the terminal:
$ beel invoices create --help
...
Options:
--type <STANDARD|CORRECTIVE|SIMPLIFIED|PROFORMA> Invoice type to create. (body field, required)
--notes <string> (body field)
--series-id <string> Invoicing series ID (if not specified, uses default) (body field)
...
--data <json> JSON body (required): inline, @file.json, or - for stdin; field flags override its keys
Body fields, * = required. Fields with a --flag above can be passed that way; the rest go in --data:
* lines object[]
* recipient object Invoice recipient. …
* type string (STANDARD|CORRECTIVE|SIMPLIFIED|PROFORMA) Invoice type to create. …
notes string
…
Full schema (nested fields, formats): beel docs search invoicesArguments, flags and --data
- IDs in the path are positional arguments, in the order of the path:
beel invoices get <invoice_id>,beel members set-grant <member_id> <company_id>. The company and the account are not among them: they come from--companyand--account(see above). - Query parameters are flags named after the API parameter:
--status,--sort-by,--needs-action. - Top-level body fields that are a string, number or boolean have their own flag, named after the API field:
--status PAID,--default-price 12.5,--active false. Enum values are checked before the request is sent: a value outside the list exits with code2and names the allowed ones. --datatakes the whole body as inline JSON,@file.json, or-for stdin. Objects, arrays andnullonly go this way. When you pass both, the flags override the keys of--data:--data @base.json --status PAID.- A command whose body is required needs
--dataor at least one field flag. - Binary responses (PDF, ZIP, Excel) require
--output <path>. POSTrequests get an automaticIdempotency-Key.
Searching the docs
The CLI can search this documentation locally — useful for AI agents that want the relevant section without fetching whole pages. It downloads the machine-readable docs once (cached 15 minutes) and filters on your machine; search terms never leave your computer. No API key needed.
beel docs list # all documentation pages (JSON)
beel docs search idempotency key # prints only the matching sections
beel docs get glossary # prints a full page as markdownGeneric escape hatch
Any endpoint, even ones this CLI version doesn't know yet:
beel request GET /v1/companies/{company_id}/invoices --query status=PAID --query limit=5
beel request POST /v1/companies/{company_id}/customers --data @customer.json{company_id} and {account_id} in the path are filled in the same way as for regular commands. beel request also reaches the deprecated routes until they retire on 9 December 2026.
Output contract
Built for scripting and AI agents:
- stdout: for API/data commands, the response JSON, compact on one line — pipe straight into
jq. Add--prettyto indent it. Two helpers print human-readable markdown instead:docs(list/search/get) andcommands --markdown(plaincommandsis JSON). - stderr: errors as one JSON object on one line:
{"error": {"code", "message", "status", "details", "request_id"}}
| Exit code | Meaning |
|---|---|
0 | Success |
1 | Unexpected error |
2 | Usage or config error |
3 | Auth (401/403) |
4 | Not found |
5 | Validation (400/409/422) |
6 | Rate limit (429) |
7 | Server error (5xx) |
BEEL_COMPANY_ID and BEEL_ACCOUNT_ID choose the company and account (see above); BEEL_BASE_URL overrides the API host; BEEL_CONFIG_DIR overrides the config location.
Upgrading from 0.2.x
0.3.0 builds its commands only from the routes that name the company or the account. The flat routes that 0.2.x called are deprecated and retire on 9 December 2026. Scripts written for 0.2.x need new command names:
- Company commands move to their resource.
beel companies list-invoices <company_id>is nowbeel invoices list --company <company_id>, or justbeel invoices listif the CLI can work out the company. - Action names drop what the resource already says.
companies get-by-idiscompanies get,webhooks list-subscriptionsiswebhooks list,emails get-deliveryisemails get,me get-my-identityisme get, andinvoices convert-proforma-to-invoiceisinvoices convert-proforma. Actions that replace a value withPUTare calledset:owner set,members set-grant. - Status changes use one command.
invoices mark-paid,mark-sentandrevert-to-issuedbecomeinvoices set-status <invoice_id> --status PAID(orSENT,ISSUED).recurring-invoices pauseandresumebecomerecurring-invoices set-status. updatebecomespatchon invoices, customers, products, recurring invoices and series. Fields you leave out keep their value; to clear a field that can be cleared, send it asnull(each command's--helpsays which).- Payment events take a connection ID instead of the provider name:
beel payment-connections list-events <connection_id>. - Output is compact JSON. 0.2.x indented it; pass
--prettyfor the old layout. Body fields can now also be passed as flags (see Arguments, flags and--data).
If you type a 0.2.x name, the CLI exits with code 2 and its error message gives the new name:
unknown command 'list-invoices'. 'companies list-invoices' was renamed in 0.3.0: use 'beel invoices list --company <company_id>'. All renamed commands: https://docs.beel.es/cli#upgrading-from-02x