Skip to content

Repository files navigation

fic

Unofficial CLI for the Fatture in Cloud API v2 — JSON-first, jq-pipeable, agent-friendly.

Disclaimer: Unofficial. Not affiliated with or endorsed by TeamSystem S.p.A. "Fatture in Cloud" is a trademark of its owner, used here descriptively.


Install

Requires Python 3.11+.

From PyPI (not yet published — use the clone method below for now):

uv tool install fic
# or
pipx install fic

From a local clone:

git clone https://github.com/yourusername/fic-cli.git
cd fic-cli
uv tool install .
# or
pipx install .

With the MCP server (for AI agents — see Use with an AI agent):

uv tool install 'fic[mcp]'   # or:  pipx install 'fic[mcp]'  /  uv tool install '.[mcp]'

Authentication

Manual token (recommended)

  1. Open the FIC web app → SettingsConnected Applications.
  2. Create a new application and copy the access token.
  3. Find your company id with fic companies list (look for the id field).

Set credentials via environment variables:

export FIC_ACCESS_TOKEN="your-token-here"
export FIC_COMPANY_ID=12345

Or write them to ~/.config/fic/config.toml:

access_token = "your-token-here"
company_id = 12345

Resolution order: environment variables take priority over the config file.

OAuth Device Code Flow

fic auth device --client-id YOUR_CLIENT_ID

This opens the FIC authorization page, polls for approval, and persists tokens to ~/.config/fic/config.toml.

Caveat: FIC enables the Device Code Flow on demand per application. If it is not enabled for your client_id, the authorization step fails with a clear error message. Contact FIC support to enable it for your OAuth application.

Check resolved credentials

# Show resolved token source and company_id (no network call, token redacted)
fic auth status

# Confirm the token works (network call)
fic auth whoami

Quickstart

# List companies linked to your token (use to find your company id)
fic companies list

# Show current user info
fic auth whoami

# List all clients (auto-paginate)
fic clients list --all

# List invoices for 2026
fic docs list --type invoice --year 2026

# Get a specific document as JSON and pipe to jq
fic docs get 123 | jq '.data.number'

# Create a document from a JSON file
fic docs create --type invoice -f invoice.json

# Create a document from stdin
echo '{"data": {"type": "invoice", ...}}' | fic docs create --type invoice -f -

# Download a document PDF
fic docs pdf 123 -o invoice.pdf

# List VAT types (useful when building document payloads)
fic info vat-types | jq '.data[]'

# List payment accounts (needed for fic docs pay)
fic info payment-accounts | jq '.data[] | {id, name}'

Command reference

Global options

fic [--output json|table] [-v] [--company-id N] COMMAND
Option Description
--output TEXT Output format: json (default) or table
-v / --verbose Verbose diagnostics on stderr
--company-id INTEGER Override the company id for this invocation

The access token is never a CLI flag (shell-history risk). Use env vars or the config file.


fic version

Print the fic version.


fic auth

fic auth status

Show resolved credentials (token redacted). No network call.

fic auth whoami

Show the current user's info (network call).

fic auth device --client-id ID [--scope S]

Log in via the OAuth 2.0 Device Code Flow and persist tokens.

Option Description
--client-id TEXT OAuth app client id (required)
--scope TEXT Space-separated scopes (default: situation:r situation:a)

fic companies

fic companies list

List all companies linked to the current token. Use this to discover your company_id.

fic companies info

Show the current company's info.


fic clients

fic clients list [OPTIONS]

Option Description
--all Auto-paginate all pages
--query TEXT Filter (maps to the API q param)
--page INTEGER Page number
--per-page INTEGER Results per page (max 100)
--sort TEXT Sort expression
--fieldset TEXT basic, detailed, or fic_view

fic clients get ID

Get a single client by id.

fic clients create [-f FILE]

Create a client from JSON. FILE is a path or - for stdin (default: -).

fic clients update ID [-f FILE]

Update a client by id from JSON.

fic clients delete ID [--yes]

Delete a client. Requires --yes to confirm.


fic suppliers

Same commands as fic clients: list, get, create, update, delete with identical options.


fic products

Same commands as fic clients: list, get, create, update, delete with identical options.


fic docs

Issued documents. The --type flag is required for list and create because the FIC API is type-scoped.

Issued document types: invoice, quote, proforma, receipt, delivery_note, credit_note, order, work_report, supplier_order, self_own_invoice, self_supplier_invoice.

fic docs list --type TYPE [OPTIONS]

Option Description
--type TEXT Document type (required)
--all Auto-paginate all pages
--year INTEGER Sugar: filter by year via q date filter
--query TEXT Filter (maps to the API q param)
--page INTEGER Page number
--per-page INTEGER Results per page
--sort TEXT Sort expression
--fieldset TEXT Fieldset variant

fic docs get ID

Get a single issued document by id.

fic docs create --type TYPE [-f FILE]

Create an issued document. --type is required. FILE defaults to stdin.

fic docs update ID [-f FILE]

Update an issued document by id from JSON.

fic docs delete ID [--yes]

Delete an issued document. Requires --yes to confirm.

fic docs convert ID --to NEW [OPTIONS]

Convert (transform) a document to a different type.

Option Description
--to TEXT New document type (required)
--from TEXT Current document type
--e-invoice Mark the output as an e-invoice
--keep-copy Keep the original as a copy

fic docs pdf ID [-o FILE]

Download the document PDF. Writes to FILE or stdout if -o is omitted.

Note: there is no dedicated PDF API endpoint. The CLI fetches the document, reads the temporary url field, and downloads the file from that URL.

fic docs pay ID --payment-account-id N [OPTIONS]

Mark a document as paid. This is a read-modify-write operation — see Gotchas.

Option Description
--payment-account-id INTEGER Payment account id (required; see fic info payment-accounts)
--date TEXT Paid date in YYYY-MM-DD format
--amount FLOAT Amount paid
--yes Confirm the write

fic received

Received documents. --type is required for list and create.

Received document types: expense, passive_credit_note, passive_delivery_note, self_invoice.

fic received list --type TYPE [OPTIONS]

Same list options as fic docs list (--all, --query, --page, --per-page, --sort, --fieldset).

fic received get ID

Get a single received document by id.

fic received create --type TYPE [-f FILE]

Create a received document.

fic received update ID [-f FILE]

Update a received document by id.

fic received delete ID [--yes]

Delete a received document. Requires --yes to confirm.


fic einvoice

E-invoicing / SDI operations.

fic einvoice send ID [--dry-run]

Send an issued document to the SDI (Sistema di Interscambio). --dry-run validates without sending.

fic einvoice status ID

Show the e-invoice status of an issued document. Returns {"id", "ei_status"} (the SDI status field only).

Note: there is no dedicated e-invoice status API endpoint. The CLI reads the ei_status field from the document object.

fic einvoice rejection ID

Show the SDI rejection reason for a rejected e-invoice. Returns the rejection reason object from the SDI.

E-invoice status values: attempt, missing, not_sent, sent, pending, processing, error, discarded, not_delivered, accepted, rejected, no_response, manual_accepted, manual_rejected.


fic info

Reference data lookups (not paginated).

Command Description
fic info vat-types List VAT types for the current company
fic info payment-accounts List payment accounts for the current company
fic info payment-methods List payment methods for the current company
fic info units List units of measure (global, no company required)
fic info currencies List currencies (global)

fic agent-docs

Print the machine-readable manual (every command, flag, exit code, and the error-envelope shape) as JSON, or as markdown with --output table. See Use with an AI agent.

fic mcp serve [--stdio]

Run fic as an MCP server over stdio for AI agents (needs the mcp extra). On a terminal it prints client setup instructions instead of starting; --stdio forces the server. See Use with an AI agent.


Output contract

  • Default output is JSON on stdout — a single value, jq-pipeable, no decorative chrome.
  • All diagnostics, progress, and errors go to stderr. Stdout is clean for piping.
  • --output table renders a human-readable Rich table; never use this in scripts.
  • -v / --verbose prints a diagnostic line to stderr with the resolved company_id and token source before each request.
# Safe to pipe
fic clients list --all | jq '[.data[] | {id, name}]'
fic docs get 123 | jq '.data.number'

Exit codes

Code Meaning Cause
0 Success
1 Generic / unexpected error Uncaught ApiError, network failure
2 Usage error Bad arguments (Typer parsing)
3 Auth / config error Missing token, 401 response
4 Validation error 422 response
5 Rate-limit exhausted 429 after all retries
6 Not found 404 response
7 Forbidden 403 non-throttle (permission / scope error)

On failure, JSON mode (the default) prints exactly one error-envelope line on stderr; stdout stays empty:

{"error":{"code":"not_found","message":"Resource not found (404).","hint":"Check the id and that --company-id is correct."}}

code is one of error, usage, config, auth, validation, rate_limited, not_found, forbidden. A validation error adds fields (per-field messages) and, for a totals mismatch, totals — enough for an agent to fix the input and retry. --output table keeps the human error: <msg> form instead.


Use with an AI agent

fic is built to be driven by coding agents — two surfaces, same data, same write-safety.

Self-describing manual

fic agent-docs                 # JSON: every command, flag, exit code, and the error envelope
fic --output table agent-docs  # the same as a markdown manual

Point an agent at fic agent-docs and it can discover the whole surface in one call.

MCP server

Run fic as a Model Context Protocol server so an agent calls fic operations as tools (needs the mcp extra — see Install):

# Claude Code
claude mcp add fic -- fic mcp serve
// Cursor — ~/.cursor/mcp.json
{ "mcpServers": { "fic": { "command": "fic", "args": ["mcp", "serve"] } } }

Run fic mcp serve in a terminal to print setup instructions for any stdio client (it does not start the server on a TTY; pass --stdio to force it). It exposes ~13 tools:

  • Reads (free): fic_list, fic_get, fic_info, fic_account, fic_einvoice_status, fic_einvoice_rejection, fic_docs_pdf_url.
  • Writes (require confirm=true): fic_create, fic_update, fic_delete, fic_docs_convert, fic_docs_pay, fic_einvoice_send. Called without confirm=true they refuse and do nothing — the MCP mirror of the CLI's --yes.

The server reads credentials exactly like the CLI (env / config / device login) and honors --company-id on the fic command.

Bundled skill

skills/fic/SKILL.md teaches an agent the search→inspect→act loop, the document type enums, the destructive-op gate, and the known traps. Drop it into a Claude/Hermes skills directory (or your agent's skill loader) and it is auto-discovered.


Gotchas

1. Totals-mismatch 422 from rounding

If any line item amount has more than 2 decimal places, FIC may reject the document with a 422 where validation_result is null and the message is "Il totale dei pagamenti non corrisponde al totale da pagare.". The computed totals are surfaced in the error response's extra.totals field and are printed to stderr by fic.

Fix: round all monetary values to 2 decimal places before submitting.

2. Partial entity details on document creation

Creating a document for a client that is not in the FIC saved-clients list results in only partial entity details being stored. For full data fidelity, either:

  • Create the client first (fic clients create) and reference its id in the document, or
  • Inline the full entity object in the document payload.

3. Device Code Flow availability

fic auth device uses the OAuth 2.0 Device Code Flow, which FIC enables on demand per application. If it is not enabled for your client_id, the command fails with a clear error. Contact FIC support to request it.

4. 403 is overloaded; rate limits are company-wide

FIC uses 403 for two distinct situations: a real permission/scope denial, and a long-term quota exhaustion (1 000 req/hr, 40 000 req/mo). fic retries on 403 only when a Retry-After header is present (quota); a bare 403 is treated as a hard PermissionDenied and is not retried.

The short-term limit (300 req / 5 min, exits as 429) is shared across all applications and users for the same company — heavy automation elsewhere can exhaust your budget.

5. No dedicated PDF or e-invoice-status endpoint

fic docs pdf downloads the PDF by fetching the document, reading the temporary url field, and downloading that URL. The URL is short-lived; do not cache it.

fic einvoice status reads the ei_status field from the document object. There is no polling or push endpoint.

6. Mark-as-paid is read-modify-write

fic docs pay has no dedicated "pay" endpoint. It fetches the document, patches the payments_list entry, and calls modify_issued_document. This is a read-modify-write and requires --yes to confirm. Concurrent modifications to the same document may conflict.


Design: the core/ adapter

All business logic lives in fic.core — a pure Python package with no Typer, no print(), no sys.exit(). It accepts plain arguments and returns plain data (dicts / SDK models), raising typed exceptions (AuthError, NotFound, ValidationError, etc.).

Both surfaces are thin adapters over it: fic.cli (Typer — parses args, formats json/table, maps exceptions to exit codes, writes diagnostics to stderr) and fic.mcp (the MCP server — wraps the same core functions through per-resource dispatch adapters, with the confirm=true write gate). Neither duplicates business logic, so the two surfaces can never disagree. The mcp SDK dependency is confined to fic/mcp/ and imported lazily, so the base CLI never requires it.

# fic.mcp and fic.cli both call core like this:
from fic.core import documents
from fic.core.client import FicClient

with FicClient(access_token="...", company_id=12345) as client:
    invoices = documents.list_issued(client, type="invoice", all=True)

No Typer, no subprocess, no shell. The core functions are the stable, reusable API surface. See AGENTS.md for the development contract.

About

Unofficial command-line tool for the Fatture in Cloud API v2 — JSON-first, agent-friendly.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages