#Scribo CLI
@causaprima/scribo-cli is the official Scribo terminal client. It wraps the public /api/v1 HTTP API in a commander-based CLI with sysexits-style exit codes, so it's safe to drop into CI pipelines and shell scripts.
#Install
# One-off run
npx @causaprima/scribo-cli <command>
# Global install
npm install -g @causaprima/scribo-cli
scribo <command>
Requires Node.js 20 or later.
#Quickstart
scribo create \
--jurisdiction DE --currency EUR \
--sender-name "Example GmbH" --sender-country DE \
--sender-address "Example Allee 1" --sender-postcode 10115 --sender-city Berlin \
--sender-tax-id DE123456788 --sender-email billing@example.com \
--recipient-name "Acme GmbH" --recipient-country DE \
--recipient-address "Hauptstrasse 1" --recipient-postcode 10117 --recipient-city Berlin \
--recipient-email ap@acme.example \
--line "Senior consulting,3,1200,19,DAY,S" \
--due-date 2026-06-01 \
-o invoice.pdf
Output:
Invoice created
invoice_id: f0b3c1e2-...
format: zugferd_comfort
download: https://scribo.causaprima.ai/i/f0b3c1e2-.../download
expires: 2099-12-31T23:59:59Z
validator: ok (invopop)
saved to: invoice.pdf
On the first invoice for a sender email the CLI prompts on stdin for the 6-digit verification code Scribo emails to --sender-email (suppress with --no-prompt, which prints the challenge and exits 75). An empty answer or a rejected code exits 65 (EX_DATAERR); re-run the command to request a fresh code.
#Commands
#scribo create
Generate an invoice and save the file locally. Two ways to build the payload:
- Flags — flat CLI flags map onto the
CreateInvoiceInputschema. Repeat--linefor each line item. - JSON file —
--from invoice.jsonreads a rawCreateInvoiceInputand ignores most flags.
Line item syntax
--line "<description>,<quantity>,<unit_price>,<tax_rate>[,<unit_code>[,<tax_category_code>[,<tax_exemption_code>]]]"
Escape commas in the description with a backslash: "Hours, Sep \\& Oct,40,100,19,HUR,S". unit_code defaults to EA; tax_category_code defaults to S (standard rated). The optional 7th field, tax_exemption_code, is required for tax_category_code=E (per BR-E-10) and otherwise overrides the auto-applied default for AE/K/G/O — pass a VATEX-EU-* code matching the legal basis (e.g. VATEX-EU-79-C for Kleinunternehmer § 19 UStG). See /docs/tax-codes for the full enum.
Flag reference
| Flag | Maps to | Notes |
|---|---|---|
--from <file> |
(whole payload) | Reads CreateInvoiceInput JSON; overrides other flags. |
--idempotency-key <key> |
Idempotency-Key header |
Auto-minted from payload hash if omitted. |
-o, --output <file> |
(local write) | Default invoice-<id>.pdf. |
--json |
(stdout) | Print the InvoiceRecord JSON to stdout after writing the file. |
--sender-name, --sender-country, --sender-address, --sender-address2, --sender-postcode, --sender-city, --sender-tax-id, --sender-email, --sender-contact-name, --sender-contact-phone |
sender.* |
|
--recipient-name, --recipient-country, --recipient-address, --recipient-address2, --recipient-postcode, --recipient-city, --recipient-tax-id, --recipient-email, --recipient-leitweg-id |
recipient.* |
--recipient-leitweg-id auto-selects XRechnung UBL. |
--currency, --jurisdiction, --format-override, --notes |
top-level | --format-override enum at /docs/api. |
--due-date, --payment-terms, --delivery-date, --delivery-start, --delivery-end |
top-level | Dates are YYYY-MM-DD. |
--payment-iban, --payment-bic, --payment-account-name |
payment_means.* (SEPA) |
--payment-iban is required when the resolved format is XRechnung (Leitweg-ID present or --format-override=xrechnung_*) — BR-DE-1 mandates payment instructions on German B2G invoices. Optional elsewhere. |
--payment-account-number, --payment-routing-number, --payment-bank |
payment_means.* (US) |
US account number (4–17 digits) + 9-digit ABA routing number, instead of --payment-iban — not both. --payment-bank is the free-text beneficiary bank name + address, rendered on the invoice for wires. |
--locale <bcp47> |
X-Scribo-Locale header |
Language of the verification email + confirmation page (e.g. de-DE). Defaults to your POSIX locale ($LC_ALL / $LC_MESSAGES / $LANG); unsupported values fall back to English. |
--no-prompt |
(behavior) | Skip the interactive stdin prompt for the email verification code — prints the magic-link hint to stderr and exits 75. For CI / unattended runs. |
--line (7th field: tax_exemption_code) |
line_items[].tax_exemption_code |
Required for tax_category_code=E (per BR-E-10) — pass a VATEX code matching the legal basis. AE/K/G/O get their VATEX codes auto-applied server-side; pass the 7th field only to override. |
--base-url <url> |
env override | Overrides SCRIBO_API_BASE_URL. |
--api-key <key> |
env override | Overrides SCRIBO_API_KEY. |
Common VATEX codes for the 7th --line field:
| Code | When |
|---|---|
VATEX-EU-79-C |
Kleinunternehmer § 19 UStG (small-business exemption) |
VATEX-EU-132 |
Art. 132 EU VAT Directive — health, education, social services |
VATEX-EU-143 |
Art. 143 — importation exemption |
VATEX-EU-AE |
Reverse charge (auto-applied for category AE) |
VATEX-EU-IC |
Intra-community supply (auto-applied for category K) |
VATEX-EU-G |
Free export (auto-applied for category G) |
VATEX-EU-O |
Outside-scope of VAT (auto-applied for category O) |
Example — Kleinunternehmer (§ 19 UStG)
scribo create \
--sender-name "Friedrich Beratung" --sender-country DE \
--sender-address "Musterstr. 1" --sender-postcode "10115" --sender-city Berlin \
--sender-email "f@example.de" \
--recipient-name "Acme GmbH" --recipient-country DE \
--recipient-address "Hauptstr. 1" --recipient-postcode "10117" --recipient-city Berlin \
--recipient-email "ap@acme.de" \
--currency EUR \
--line "Beratung,5,80.00,0,HUR,E,VATEX-EU-79-C"
Example — German B2G XRechnung (Leitweg-ID + payment-iban)
scribo create \
--sender-name "Acme GmbH" --sender-country DE \
--sender-address "Musterstr. 1" --sender-postcode "10115" --sender-city Berlin \
--sender-tax-id "DE123456788" --sender-email "billing@acme.de" \
--sender-contact-name "Erika Beispiel" --sender-contact-phone "+49 30 1234567" \
--recipient-name "Bundesamt für Beispiele" --recipient-country DE \
--recipient-address "Wilhelmstr. 1" --recipient-postcode "10117" --recipient-city Berlin \
--recipient-email "rechnung@bund.de" \
--recipient-leitweg-id "991-12345-67" \
--currency EUR \
--payment-iban "DE89370400440532013000" --payment-account-name "Acme GmbH" \
--line "Consulting,3,1200.00,19,DAY,S"
#scribo get <invoiceId>
Fetch invoice metadata as JSON on stdout. Useful in scripts that need a fresh signed download URL:
URL=$(scribo get f0b3c1e2-... | jq -r .download_url)
curl -fsSL "$URL" -o invoice.pdf
#scribo download <invoiceId> [-o file]
Stream the invoice bytes only. Default output file: invoice-<id>.pdf.
#scribo jurisdictions
Print the supported-jurisdictions matrix. Use --json for machine-readable output:
scribo jurisdictions --json | jq '.[] | .jurisdiction'
#Configuration
| Env var | Purpose | Default |
|---|---|---|
SCRIBO_API_BASE_URL |
Public API origin (SCRIBO_BASE_URL is accepted as a fallback — same var the skill uses). |
https://scribo.causaprima.ai |
SCRIBO_API_KEY |
Optional bearer token (partner quotas). | (unset) |
CLI flags --base-url and --api-key override env vars.
#Exit codes
The CLI follows BSD sysexits, so callers can branch on the exit code without parsing stderr:
| Code | Constant | Meaning |
|---|---|---|
0 |
— | Success. |
64 |
EX_USAGE |
Missing or malformed CLI flag. |
65 |
EX_DATAERR |
Server returned 4xx (other than 404). |
66 |
EX_NOINPUT |
Invoice not found (404). |
70 |
EX_SOFTWARE |
Server returned 5xx. |
75 |
EX_TEMPFAIL |
Rate-limited (429) or temporarily soft-blocked (tenant_soft_blocked) — retry after the period printed in the error. |
1 |
— | Network or unclassified error. |
Example CI usage:
scribo create --from invoice.json -o out.pdf || case $? in
75) echo "rate limited; backing off"; exit 75 ;;
70) echo "server error; retry later"; exit 1 ;;
*) exit 1 ;;
esac
#Contract
The CLI talks to the public /api/v1/* HTTP API. The authoritative contract is the OpenAPI document served at GET {SCRIBO_API_BASE_URL}/api/v1/openapi.json. Any field accepted by the API is accepted by --from <file>.
#See also
- API — the underlying HTTP contract.
- MCP server — hosted at
scribo.causaprima.ai/mcpfor Claude Desktop, Cursor, Cline, ChatGPT App, OpenAI Codex CLI, and other MCP clients. - Skill —
curlhelpers for Claude Code / Codex CLI. - Troubleshooting — error codes and recovery.