ANAF e-Factura

com.foreverworksPublished by foreverworks.comv0.1.1Updated Nov 3, 2026

Read, check and file a Romanian company's e-invoices and e-Transport declarations with ANAF.

The publisher's own server: factura.bo

Overview

AI-generated overview

Reads, validates, builds and files a Romanian company's e-Factura invoices and e-Transport declarations with ANAF, including signed archives, PDFs and VAT…

What you can do
  • List, page through and download e-Factura messages, saving signed archives and ANAF-rendered PDFs to a local outbox
  • Read, validate and build UBL invoices and credit notes, and verify ANAF's seal on a stored file
  • File invoices to companies or to persons, and follow each upload's processing status
  • List, file and check the status of e-Transport declarations
  • Look up companies in ANAF's public VAT registry by CIF
  • Check, refresh and re-establish the stored ANAF authorization
When to use it
For a Romanian company that already uses ANAF's e-Factura and wants an assistant to review incoming and outgoing invoices, check ANAF's seal, prepare and validate invoice XML, or file invoices and e-Transport declarations. Useful when the work should stay on the user's machine with a local seal check and test-first filing.
Requirements
Runs locally over stdio via npx on Node 22.19 or newer. Needs an OAuth application registered with ANAF (client id, secret, callback URL) and the company's CIF in an app.json file whose path is given in ANAF_CONFIG; optional ANAF_STATE_DIR and ANAF_DOWNLOADS_DIR set where tokens and files go. Authorizing requires a person with a qualified digital certificate and SPV rights to sign in once in a browser. Network access to ANAF is needed; the e-Transport tools need the ETRANSPORT role.
Before you install
Three tools (upload_invoice, upload_invoice_b2c, etransport_file) file documents with ANAF that reach the counterparty and cannot be withdrawn; they go to the test environment unless test is false, and clients should ask for approval before each call. app.json holds the ANAF client secret and must be kept private. Tokens and a ledger of production filings are stored in the state dir, and a refresh rotates the refresh token, so a dead grant needs the certificate holder again. Downloads are…

Installation

In SourceWeft

  1. Open ANAF e-Factura in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

[ANAF e-Factura icon] ANAF e-Factura MCP server

Read the invoices your company sent and received through Romania's e-Factura, check ANAF's seal on them, and file invoices and e-Transport declarations with ANAF, from your AI assistant. It also validates, renders and writes invoices, and looks companies up in ANAF's VAT registry. Its three money tools (upload_invoice, upload_invoice_b2c, etransport_file) file a document that cannot be withdrawn: your client asks you before each call, and each goes to ANAF's test environment unless you say otherwise.

You need an OAuth application registered with ANAF (its client id, secret and callback URL), the company's CIF, and a person with a qualified digital certificate and SPV rights for the company, who signs in once in a browser. The server then renews the grant itself, and its --health check warns two weeks before it would need that person again. Validating, rendering, building and reading an invoice, checking a seal and looking up a company need no account.

What you can ask

  • "Which invoices did suppliers send us in the last two weeks?"
  • "Download the newest invoice we received, check ANAF's seal on it, and list its lines."
  • "Is RO87654329 a VAT payer, and does it apply VAT on collection?"
  • "Invoice Client SRL for 10 hours of consulting at 500 RON an hour plus 21% VAT, have ANAF validate it, and file it in the test environment."
  • "How many days are left on our ANAF authorization?"

Install

[Install in VS Code] [Install in Cursor] [Download for Claude Desktop]

With Claude Code:

bash
claude mcp add anaf-efactura -e ANAF_CONFIG="<path to ANAF app.json>" -- npx -y @foreverworks/mcp-anaf-efactura

Claude Desktop (Settings, Developer, Edit Config) and most other clients read the same command as JSON:

json
{  "mcpServers": {    "anaf-efactura": {      "command": "npx",      "args": ["-y", "@foreverworks/mcp-anaf-efactura"],      "env": {        "ANAF_CONFIG": "<path to ANAF app.json>"      }    }  }}

Each runs the published package on Node 22.19 or newer. VS Code and the Claude Desktop bundle ask for ANAF_CONFIG as they add the server. Cursor reads it from your environment instead (${env:ANAF_CONFIG}), so set it before you start Cursor. Configuration says what it holds.

From a clone

bash
git clone https://github.com/foreverworks/mcps.git && cd mcpsnpm ci && npm run buildclaude mcp add anaf-efactura -e ANAF_CONFIG="<path to ANAF app.json>" -- node "$PWD/servers/anaf-efactura/dist/index.js"

In a JSON configuration, the command is node and its one argument the full path of servers/anaf-efactura/dist/index.js. To share one running copy between several clients over HTTP, see docs/deploying.md.

Configuration

VariableRequiredMeaning
ANAF_CONFIGyesPath of app.json (below), which the server only reads.
ANAF_STATE_DIRnoWhere the grant, a begun authorization and the ledger of production filings go; the default is in docs/configuration.md.
ANAF_DOWNLOADS_DIRnoThe outbox for downloaded archives, PDFs and built invoices; default <state dir>/invoices.

app.json holds the application you register with ANAF (below):

json
{  "app_name": "<the name you gave it at ANAF, shown by health>",  "client_id": "<Client ID>",  "client_secret": "<Client Secret>",  "redirect_uris": ["<the Callback URL you registered>"],  "company_cif": "<the company's CIF, with or without RO>"}

client_id and client_secret are required, and authorizing needs redirect_uris. company_cif (or cif) is the company a call acts on when it names none; one grant can cover several companies, so a cif argument overrides it. endpoints overrides ANAF's URLs by name (authorize, token, hello, fctelProd, fctelTest, etransportProd, etransportTest), and api_base / api_test_base the e-Factura bases. The server never writes app.json, and its errors name a bad field, never a value. Without ANAF_CONFIG the server still starts, and the tools that need no grant work.

Getting the client id and secret

ANAF calls this registering an application in an OAuth profile; its procedure, Oauth_procedura_inregistrare_aplicatii_portal_ANAF.pdf (in Romanian), shows each screen.

  1. Check that the person who will sign in is registered in SPV with their qualified certificate and one of the company's rights: legal representative, designated representative or empowered person (reprezentant legal, reprezentant desemnat, împuternicit).
  2. Create a developer account at anaf.ro/InregOauth (Servicii Online, Înregistrare utilizatori, Dezvoltatori aplicații, "Înregistrare pentru API-uri"), new or from a portal account you have, and confirm your e-mail with the "Cod de verificare" ANAF sends.
  3. Sign in ("Autentificare utilizator"), open "Editare profil Oauth", then "Gestionare aplicații".
  4. Name the application, choose its services (E-Factura, and E-Transport for the e-Transport tools), and give a Callback URL you control, such as https://example.com/anaf/callback; nothing has to answer there. Press "Generare Client ID".
  5. Click the application in the list, and copy its Client ID, Client Secret and Callback URL into app.json.

The grant may do only what both the application's services and the certificate holder's SPV rights allow; health shows its roles (EFACTURA, ETRANSPORT).

Files in and out

A document goes in byte for byte, never as text a model retyped, because ANAF's seal covers the exact bytes: by path from the server's file roots (MCP_FILE_ROOTS; over stdio, the client's folders or the working directory), or as { "content_base64": "...", "sha256": "..." } from a script. A link, a file outside the roots, a wrong checksum or a document that is not UTF-8 is refused before anything is sent. A path in the outbox is taken too, so build_invoice's file goes straight to validate_invoice and upload_invoice.

Files come out into the outbox at mode 0640, whole or not at all, and never over a file holding other bytes (that one gets the next numbered name, such as 5001-2.zip); a save_dir must be a folder inside it. The state dir holds tokens.json, authorization.json and uploads.json at 0600. Neither the state dir nor the outbox may sit inside app.json's directory. The variables every server shares are in docs/configuration.md.

First run

The tools that read or file at ANAF need a grant, which only the certificate holder can give. Ask your assistant to authorize the server with ANAF:

  1. begin_authorization answers ANAF's sign-in page for your application (authorizeUrl), open for 30 minutes; redirect_uri picks one of several callback URLs. A client that shows links itself (MCP URL elicitation: over stdio, or over HTTP with a client of protocol 2026-07-28, as Claude Code is) offers you the page; if you do not hold the certificate, decline and pass the address on.
  2. The certificate holder opens it in the browser where the certificate is installed, picks the certificate, and signs in.
  3. The browser lands on the callback URL, with ?code=...&state=... in the address. The page may fail to load; the address is what counts.
  4. Pass that whole address to complete_authorization (redirect_url) at once: ANAF's code dies 60 seconds after it is issued. The server checks the state, exchanges the code, writes tokens.json, and answers only the two expiry dates; no token reaches the conversation.
  5. Ask for health: the grant's roles and the days left on each token.

Keeping the grant alive

ANAF's procedure (pp. 28 and 33) gives the access token 90 days (or the exp it states) and the refresh token 365. A call within 3 days of the access token's end refreshes it first (refresh_token with force refreshes now), and each refresh brings a refresh token good for another 365 days. If the refresh token runs out or ANAF refuses it (invalid_grant), only the certificate holder can bring the grant back, by repeating the steps above. For a server used rarely, refresh daily from a timer. From a clone, in servers/anaf-efactura:

bash
node dist/index.js --health            # greet ANAF with the stored token; the days leftnode dist/index.js --refresh           # refresh when the access token has under 14 days leftnode dist/index.js --refresh --force   # refresh now

Each prints one ANAF: OK|WARN|FAIL ... line, never a token, and exits 0 for OK, 1 for FAIL (a person has to act: a dead grant, a broken configuration) or 2 for WARN (ANAF unwell, or the refresh token ends within 14 days).

Tools

read changes nothing at ANAF (some save files to the outbox); write changes the stored grant; money files a document with ANAF, and your client asks you before each call.

ToolAccessWhat it does
healthreadGreets ANAF with the stored token: the application, the default CIF, the grant's roles, the days left on each token. Only the greeting's first line comes back, because ANAF echoes the bearer token in it.
token_statusreadThe stored grant's expiries and roles, without calling ANAF.
refresh_tokenwriteA new access token when the current one is within 3 days of its end, or now with force.
begin_authorization / complete_authorizationwriteA new grant: ANAF's page for the certificate holder, then the address their browser landed on.
list_invoicesreade-Factura messages from the last zile days, at most 60: sent, received, errors and buyer messages, with the id download_invoice takes.
list_invoices_windowreadThe same over a date range of at most 60 days, a page at a time, filtered by filtru (E errors, T sent, P received, R awaiting a response).
download_invoicereadSaves one signed ZIP to the outbox once it opens, every entry matches its checksum, and ANAF's seal holds. ANAF allows 10 downloads a day per message.
upload_invoicemoneyFiles an invoice XML (UBL, CII, CN or RASP) to a company. Test environment unless test is false.
upload_invoice_b2cmoneyFiles an invoice to a person through ANAF's /uploadb2c, otherwise as upload_invoice.
upload_statusreadHow an upload was processed: ok (with its download id), processing, or failed with ANAF's reasons.
etransport_listreade-Transport declarations from the last zile days, at most 60: UIT, declarant, partner, carrier, vehicle, and ANAF's reasons for one it refused.
etransport_filemoneyFiles a declaration XML (schema v2): a new transport, a correction, a deletion, a confirmation or a vehicle change. Test environment unless test is false.
etransport_statusreadHow a declaration was processed: ok (the UIT is valid from now), processing, or failed.
build_invoicereadWrites a CIUS-RO invoice (UBL) from fields into the outbox, totals computed exactly and the draft checked first. Files nothing.
parse_invoicereadA UBL invoice or credit note (a file, or an archive in the outbox) as JSON, every value the document's own text. Calls nothing.
validate_invoicereadANAF's own validation of a UBL invoice or credit note (schema, EN 16931 and CIUS-RO rules), without filing it.
invoice_pdfreadANAF's PDF of a UBL document, or of a filed invoice by message_id, saved to the outbox.
verify_signaturereadWhether an invoice is the file ANAF stored, under ANAF's seal: checked locally against a pinned CA, ANAF asked only when that cannot decide.
lookup_companyreadUp to 100 CIFs in ANAF's VAT registry, as of a date: identity, registered office, VAT payer and period, VAT on collection, split VAT, inactive, RO e-Factura registry.

The last six need no grant, except invoice_pdf and verify_signature given a message_id; the e-Transport tools need the ETRANSPORT role. The tools that call ANAF take cif (by default app.json's) and test: listings and downloads read production unless test is true, while the three filing tools and the two status tools use the test environment unless test is false, so a status is asked where its filing went.

Money

upload_invoice, upload_invoice_b2c and etransport_file file a legal or fiscal document with ANAF, which reaches the counterparty and cannot be withdrawn (undoing an invoice takes a credit note). They are marked money in tools/list, and your client asks you to approve each call (Claude Code does, in every permission mode). Whoever confirms, three rules hold:

  • Test unless you say otherwise. Each goes to ANAF's test environment, where a filing has no legal effect, unless test is false.
  • The same bytes are not filed twice in production unless refile is true. The SHA-256 of a production filing's exact bytes goes into a ledger (uploads.json) before it is sent; a filing ANAF refused, or one that never left, does not count.
  • A lost answer is never resent. A dropped connection, a 5xx or an undocumented answer fails as outcome_unknown, because the filing may have happened (Troubleshooting).

The file is read once, before you are asked, so the line you confirm and the filing come from the same bytes. With MCP_CONFIRM=elicit the server asks you itself, in one line read from the XML and ending with the file's SHA-256:

anaf-efactura: FILE with ANAF e-Factura PRODUCTION (legal filing, cannot be withdrawn): invoice EX-0042 of 2026-09-28, buyer Client SRL (87654329), payable 1234.50 RON, seller Exemplu SRL (RO92345672); UBL, file sha256 3f1c0a9e27b4anaf-efactura: File e-Transport declaration for a new UIT, Cluj-Napoca (county 12) to Iasi (county 22), vehicle CJ12ABC + trailer CJ34DEF, 1800.50 RON, environment prod; declarant 92345672, file sha256 5d0e7b2a91c4

A test upload's line starts TEST upload to ANAF's test environment (no legal effect), an invoice to a person reads B2C invoice, and a refiling adds AGAIN (refile). Amounts are the document's own decimal strings; the e-Transport value is the exact sum of the goods' valoareLeiFaraTva. A document the server cannot read says UNREADABLE XML: answer no.

Before you are asked, the check digits must hold on every IBAN an invoice names for payment (BT-84, BT-91), the seller's CIF, an e-Transport declarant's CIF or CNP, and every UIT (whose check digits, by ANAF's rule BR-019, catch a wrong character but not two swapped ones). A new UIT counts only once etransport_status says ok.

Troubleshooting

config: "tokens at ... are missing or unparseable". No grant is stored yet, or the file is damaged: authorize as in First run.

config naming ANAF_CONFIG, a field of app.json, company_cif, or "... is inside app.json's directory". Fix the variable or field, pass cif, or move ANAF_STATE_DIR / ANAF_DOWNLOADS_DIR away from app.json.

auth with invalid_grant, refresh_expired, invalid_client or unauthorized_client. The grant is dead: authorize again. Any other auth code is ANAF failing for now: retry later.

input: "the state in that address is not the one begin_authorization issued", or "begun more than 30 minutes ago". The address is from an older tab, or the window closed: call begin_authorization again, and pass the new address at once (a code older than 60 seconds fails as auth).

api with status 401 or 403. ANAF refused the token for this CIF or role (token_status shows the roles). The e-Transport tools need ETRANSPORT: add the service to the application at ANAF, then authorize again.

api with reason: "outcome_unknown", or input: "this exact file ... was already sent to ANAF production". The filing may have reached ANAF, or these bytes already did. Look first (upload_status with test: false, list_invoices, etransport_status or etransport_list), and pass refile: true only to file the same bytes again on purpose.

input: "the invoice was not sent", "the declaration was not sent" or "the invoice was not built". A check digit or a rule failed; the message names each. Fix the document, or build_invoice's fields.

validate_invoice finds only ERRIdentif "nu a fost identificat cui cumparator". The buyer is foreign, which ANAF's validator cannot be told, so this finding is expected. Upload with extern: true, which build_invoice answers for such a buyer.

checksum_mismatch. The bytes that arrived are not the file sha256 names, as when a model retypes base64. Nothing was sent: send the file by path, or let a script encode it.

api with status 0: ANAF was unreachable, or its answer lacked a field the call needs; retry later. "... answered in a shape ANAF does not document": ANAF's answer changed and the server refuses to guess; please report it.

How it works

What comes back. ANAF's wire shape stays inside the server: a listed message has a kind (sent, received, error, buyerMessage, unknown) and details with the upload id and both CIFs, lifted out of ANAF's prose. createdAtLocal is zoneless on purpose, because ANAF stamps Bucharest time without saying so; a date window is UTC, so it reads the same anywhere. notice is set when ANAF refused with HTTP 200 and an eroare key ("no messages in this period" included): reading that as an empty list would be the most expensive kind of wrong. cui, on a real answer the certificate holder's CNP, is never echoed.

ANAF's seal. download_invoice keeps an archive only once every entry matches its CRC and ANAF's seal (semnatura_<id>.xml) holds: a detached XML-DSig signature whose digest is the SHA-256 of the invoice file's raw bytes (so a re-indented copy is not the sealed file), by a certificate issued to Ministerul Finanțelor by "Trans Sped Electronic Seal QCA G3". verify_signature checks that locally: the certificate must come from the pinned CA (src/infra/sealTrust.ts) and not have expired before the invoice's date, the RSA signature must verify, and then the digest decides. So a swapped certificate fails the CA check, and a digest rewritten for a changed invoice fails the signature. Anything short of a verified signature goes to ANAF's public /api/validate/signature, and checkedBy says who answered. npx tsx scripts/check-seals.ts <folder> runs the check offline.

Building an invoice. build_invoice totals by EN 16931's rule, which ANAF checks (each line rounded to 2 decimals, VAT once per category and rate on the sum of its lines, half away from zero), with the toolkit's invoiceTotals.ts, as the oblio server does. Before writing, it names every problem ANAF would refuse:

  • check digits: the CIF in a Romanian VAT id or CUI, a person's CNP, the IBAN;
  • the address: a Romanian one needs its county (CJ or RO-CJ), and in Bucharest the city is the sector (Sector 3 is written SECTOR3);
  • VAT: S needs a rate and the others have none; E needs a reason (AE, G and O default to their VATEX code); reverse charge needs the buyer's id; O names no VAT id and mixes with no other category;
  • money: an amount due needs a due date or payment terms, a credit transfer the IBAN, and an invoice in another currency vat_exchange_rate (RON per unit, from the bnr server), because CIUS-RO states VAT in RON too;
  • CIUS-RO's lengths and counts, and a number with at least one digit.

The tests hold every built file to ANAF's own verdict on its exact bytes, recorded by scripts/record-validation.ts --send from seven invented invoices (test/fixtures/validation).

Reading an invoice. parse_invoice spends none of ANAF's downloads, and 400.0000 stays 400.0000: a faithful view, not a verdict, which is validate_invoice's job.

Validation, PDF and the VAT registry. validate_invoice and invoice_pdf call ANAF's public host, webservicesp.anaf.ro, which takes no token (the same operations on api.anaf.ro answer 401 without one), so they work after the grant runs out. Anything over 5 MB is refused before the call. A filed invoice's PDF is made without validating it again, so today's rules cannot refuse a document filed under older ones; any other is validated first unless validate is false. lookup_company checks each CIF's control digit, then asks PlatitorTvaRest v9 one request a second, as ANAF requires; a status ANAF did not send is absent, not false.

Rotation. A refresh retires the old refresh token, so the new pair is written to tokens.json, under a lock every process on the state dir shares, before the access token is used. --refresh keeps no backup, since a copy from before a rotation would hold a dead refresh token.

ANAF's endpoints live in src/infra/anafApi.ts. npm run gen:types regenerates their types from the OpenAPI documents in ANAF's Swagger UI pages (-- --offline from the snapshots in openapi/; mfinante.gov.ro resets connections from outside Romania, so ANAF_SPEC_MIRROR=http://web.archive.org/web/2025id_/ reads an archived copy). What ANAF documents wrongly or not at all is modelled by hand in src/domain/schema.ts, and every answer is parsed at the boundary, so drift fails loudly.

Smoke test. npx tsx scripts/smoke.ts drives the built server with your configuration, read-only (--public-only for the tools that need no grant) unless given --allow-writes, which adds refresh_token without force and begin_authorization. It never files anything, and spends two of the newest message's daily downloads.

Limitations

  • build_invoice writes invoices, not credit notes (a storno is an invoice with negative quantities), with no document-level allowances or charges, no attachments, and no intra-EU supply of goods (VAT category K, which needs a delivery country).
  • e-Transport files a declaration you wrote. It does not build one from fields, nor read /info (what others declared with you as the transport organiser).
  • Reading, validating and rendering take UBL only, as ANAF's validator does; upload_invoice also files CII.
  • Seal revocation (Trans Sped's CRL and OCSP) is not checked locally.
  • The ledger lives in one state dir: deployments with separate state dirs do not see each other's production filings.
  • No SPV mailbox and no tax declarations.

Other servers for ANAF e-Factura

Searched 2026-10-15. ANAF publishes none: its one public GitHub repository is a 2019 Java sample client for SPV, and the MCP Registry lists nothing for e-Factura. Three community servers talk to ANAF directly:

  • anafpy on PyPI, the broadest: Python, a stdio server of 48 tools (its mcp extra) for e-Factura, e-Transport filing, the SPV mailbox through a qualified certificate, tax declarations and the public registries, every filing through a prepare and confirm step bound to the document's bytes.
  • @florinszilagyi/anaf-mcp on npm (stdio): lists, downloads, validates and uploads, B2C included; it takes the XML as a text argument, with no confirmation step of its own.
  • anaf-efactura-mcp on npm (stdio or HTTP): builds and uploads invoices, on the test environment by default.

Others found only upload and poll through a hosted service, reach e-Factura through another invoicing account, or validate files offline.

None of them combines a local seal check against a pinned CA, documents taken only as files or checksummed bytes, test-first filing after a line read from the XML, check digits, no second filing of the same bytes or blind resend, and no token in the client. It does not reach the SPV mailbox or file tax declarations, as anafpy does.

Source: servers/anaf-efactura/README.md at commit cbd1121

Tools

20

begin_authorization

Begin ANAF authorization

Start (re)authorizing this server with ANAF when the grant is dead or about to end. Returns authorizeUrl for the CERTIFICATE HOLDER to open in a browser with the qualified digital certificate; you cannot do that step. Then call complete_authorization with the address the browser lands on, within 60 seconds of it landing. Starting again replaces an earlier begun authorization.

Parametersredirect_uri

build_invoice

Build a CIUS-RO invoice

Write a Romanian e-Factura invoice (UBL 2.1, CIUS-RO) from fields: seller and buyer, lines with their net price, quantity and VAT category, the currency, the payment terms. Totals are computed exactly (each line rounded to 2 decimals, VAT once per category and rate), and the draft is checked first: CIF, CNP and IBAN check digits, the county and Bucharest sector, the VAT rules per category, CIUS-RO's lengths, every problem named at once. The file goes to the outbox; the answer gives its path, sha256 and totals. Nothing is sent to ANAF: next, validate_invoice with xml: { path }, then upload_invoice with the same path to file it. An invoice in another currency needs vat_exchange_rate (RON per unit), so its VAT is stated in RON too.

Parametersbuyerrequiredlinesrequirednotesnumberrequiredsellerrequiredpaymentcurrencydue_datesave_dirfile_nametype_codeissue_daterequired+8 more

complete_authorization

Complete ANAF authorization

Finish what begin_authorization started: exchanges ANAF's code for tokens and stores them where this server keeps them. Pass the whole address the browser landed on as redirect_url (or code and state). Answers only the two expiry dates; no token ever appears in the result. ANAF's code dies 60 seconds after it is issued, so call this at once.

Parameterscodestateredirect_url

download_invoice

Download a signed invoice archive

Download the signed ZIP for a message id from list_invoices and save it to the downloads outbox (a name holding another file gets a number, never overwritten). The archive is checked first: it must open with every entry matching its checksum and hold the document with ANAF's seal (semnatura_*.xml), and the seal is checked against the document. Returns the path, bytes, sha256 and md5 of the archive, each entry's name, bytes and sha256, the seal's verdict, and what the invoice says.

Parameterstestsave_dirmessage_idrequired

etransport_file

File an e-Transport declaration

File an e-Transport declaration XML (ANAF's schema v2) with ANAF: a new transport (notificare), its correction (notificare with corectie), a deletion (stergere), a receiver's confirmation (confirmare) or a vehicle change (modifVehicul), whichever the file holds. The XML as xml: { path } for a file, or { content_base64, sha256 } from a script. Goes to ANAF's TEST environment unless test is explicitly false. A money tool (a fiscal declaration): the client asks the user to approve each call, on a line read out of the file (operation, UIT, route, vehicle, goods value in RON, environment). The declarant's code and any UIT must pass their check digits. Returns accepted, the uploadId etransport_status takes and the UIT, or accepted=false with ANAF's reasons; the UIT counts only once etransport_status says ok. The same bytes are not filed in production twice unless refile is true. Needs the ETRANSPORT role on the grant. Moves money or makes a legal document: the client asks the user to approve each call.

Parameterscifxmlrequiredtestrefile

etransport_list

List e-Transport messages

List e-Transport declarations from the last N days (at most 60): UIT code, declarant, trading partner, carrier, vehicle, and refused=true with ANAF's messages for a declaration it rejected. A notice field means ANAF refused or had nothing. Separate from e-Factura: it needs the ETRANSPORT role on the same grant.

Parametersciftestzile

etransport_status

Check an e-Transport filing

How an e-Transport declaration filed with etransport_file was processed, by its uploadId: state ok (the UIT is valid from now), processing, or failed with ANAF's reasons. Ask the environment it was filed in: test unless test is false.

Parameterstestid_incarcarerequired

health

Health

Check that the integration is alive and say so in detail: the ANAF app it authenticates as, the CIF it defaults to, the roles the grant carries, and how many days are left on each token. Run this before concluding the MCP is broken.

Takes no parameters.

invoice_pdf

Render an invoice as ANAF's PDF

Have ANAF render an invoice as its official PDF and save it to the downloads outbox. Give message_id (from list_invoices) for a filed invoice, or a UBL document as xml ({ path } for a file, or { content_base64, sha256 } from a script). A filed invoice is not validated again; a document is validated first unless validate is false, and when it fails nothing is saved and findings say why. Returns saved, path, bytes and sha256 of the PDF. The PDF itself needs no grant; fetching a filed invoice by message_id does.

Parametersxmltestsave_dirvalidatemessage_id

list_invoices

List e-Factura messages

List e-Factura messages from the last N days (ANAF allows at most 60). Each message has a kind (sent, received, error, buyerMessage), an id that download_invoice takes, a zoneless local timestamp, and details: the upload id, issuer CIF and beneficiary CIF lifted out of ANAF's prose. A notice field means ANAF refused rather than returned nothing.

Parametersciftestzile

list_invoices_window

List e-Factura messages over a date window

List messages over an inclusive YYYY-MM-DD window, paginated, in the same shape as list_invoices plus page counters. The window may span at most 60 days and is read as UTC. filtru narrows by type: E errors, T sent, P received, R awaiting a response.

Parameterscifpagetestfiltruend_daterequiredstart_daterequired

lookup_company

Look up Romanian companies

Look up companies in ANAF's public VAT registry by CIF: name, trade register number, registered office, CAEN code, and the statuses an invoice depends on (vatPayer with its current period, vatOnCollection, splitVat, inactive, and eFacturaRegistry for the RO e-Factura registry). notFound lists the CIFs ANAF does not know. A status ANAF did not report is absent, not false. Needs no grant.

Parameterscifsrequireddate

parse_invoice

Read an invoice as JSON

Read a UBL invoice or credit note into plain JSON: seller and buyer (names, VAT and legal ids, address, contact), every line (quantity, unit, price, net amount, VAT category and rate), the VAT breakdown, the totals, and the payment terms (due date, terms, IBAN). Every value is the document's own text, amounts included, never a number; a field the document does not fill is absent. Give xml ({ path } for a file, or { content_base64, sha256 } from a script) or archive, a .zip download_invoice saved to the outbox. Reads only: nothing goes to ANAF. To have ANAF check the document, use validate_invoice.

Parametersxmlarchive

refresh_token

Refresh the access token

Mint a new access token. Does nothing while the current one still has its three-day cushion unless force is set, because every refresh also ROTATES the refresh token and the old one dies with it. Safe to call on a schedule; pointless to call in a loop.

Parametersforce

token_status

Token status

Report on the stored OAuth tokens without contacting ANAF and without refreshing. Watch refreshTokenRemainingDays: once it reaches zero the grant can only be restored by hand, at a browser, with the qualified certificate.

Takes no parameters.

upload_invoice

Upload an invoice XML

Submit an invoice (UBL or CII XML) to ANAF, given as xml: { path } for a file, or { content_base64, sha256 } from a script. Every IBAN in it must pass its check digits. Goes to the TEST environment unless test is explicitly false, because a production upload is a filing that cannot be recalled. A money tool: the client asks the user to approve each call, on a line read out of the XML itself (number, date, buyer, amount due, the file's sha256). Returns accepted plus the uploadId that upload_status takes, or accepted=false with ANAF's reasons; sha256 and bytes of what was filed; invoice is what the XML says, and raw is ANAF's answer verbatim. The same bytes are not filed in production twice unless refile is true. Moves money or makes a legal document: the client asks the user to approve each call.

Parameterscifxmlrequiredtestexternrefilestandardexecutareautofactura

upload_invoice_b2c

Upload an invoice to a person (B2C)

Submit an invoice issued to a PERSON, not a company (B2C), through ANAF's /uploadb2c: the same as upload_invoice in every other way. The XML as xml: { path } (build_invoice's path works) or { content_base64, sha256 } from a script; every IBAN must pass its check digits. Goes to the TEST environment unless test is explicitly false. A money tool: the client asks the user to approve each call, on a line read out of the XML and marked B2C. Returns accepted plus the uploadId upload_status takes, or accepted=false with ANAF's reasons. The same bytes are not filed in production twice, by either upload tool, unless refile is true. Moves money or makes a legal document: the client asks the user to approve each call.

Parameterscifxmlrequiredtestexternrefilestandardexecutareautofactura

upload_status

Check an upload

Check how an earlier upload was processed, by the uploadId that upload_invoice or upload_invoice_b2c returned. Answers a state of ok, processing or failed, with a downloadId once it is ok. Must be asked of the same environment the upload went to.

Parameterstestid_incarcarerequired

validate_invoice

Validate an invoice with ANAF

Check a UBL invoice or credit note against ANAF's own schema and CIUS-RO rules without filing it. Answers valid, and when not, findings: each with the rule it broke (e.g. BR-RO-001), where in the document, and ANAF's text. invoice is what the XML says it is, and file which file was read (name, size, sha256). Needs no grant. CII is not accepted: ANAF validates UBL only. The document goes as xml: { path } for a file, or { content_base64, sha256 } from a script.

Parametersxmlrequired

verify_signature

Verify ANAF's seal on an invoice

Check that an invoice is the file ANAF stored, under ANAF's seal (semnatura_*.xml). Give ONE of: message_id (downloads the archive: ANAF allows 10 downloads a day per message), archive (a .zip already in the downloads outbox, from download_invoice; costs nothing), or the two files from an archive kept elsewhere (costs nothing): xml and signature, each { path } or { content_base64, sha256 }, read byte for byte, since the seal covers the invoice's exact bytes. Checked locally first: the certificate against Trans Sped's seal CA and the Ministry of Finance, the signature, and the digest of the invoice's raw bytes. Only when that cannot decide is ANAF asked. Answers valid, checkedBy (local or anaf), a reason when not valid, and the certificate. Revocation is not checked locally.

Parametersxmltestarchivesignaturemessage_id

Version history

2
  1. v0.1.1LatestOct 31, 2026
  2. v0.1.0Oct 26, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts

Other Factura integrations