Factur-X MCP: generate and validate e-invoices¶
Create Factur-X PDF/A-3, CII XML and UBL 2.1 invoices, check EN 16931 rules, and extract invoice data from your AI assistant.
Try invoice validation now¶
No signup, API key or card. Check a fixed synthetic invoice with the production XSD and EN 16931 engine, then try the same invoice with a missing buyer name to see the error.
Without JavaScript: valid result · invalid result.
Try it from your assistant¶
Connect to https://facturx.orvel.dev/mcp without a key and ask:
Call view_invoice_demo with sample="invalid". Explain the missing buyer name using explain_finding for BR-07, then run sample="valid" to compare.
Calling view_invoice_demo with {} now validates the passing sample. The sample selector has three choices: valid (default), invalid, or downloads. On Glama, use Try in Browser, connect with no authentication, select the demo and click Execute.
Results show findings, checks performed, engine version, validation timestamp and cache status. Reports are cached for up to 24 hours per engine version and sample; a cache hit is not a fresh execution. This demo checks CII XML, not PDFs or company registers. It accepts no custom document or URL. Shared limits are 120 requests/minute and two uncached computations/minute; unavailable checks return an error.
Download sample PDF Published validation evidence
Check your own invoice, no key¶
validate_invoice works on https://facturx.orvel.dev/mcp without authentication: attach a Factur-X PDF, CII or UBL file and ask your assistant to check it and explain each finding. Files up to 5 MB, shared daily capacity. Generation, embedding and extraction need an allowance.
Check an invoice in the browser
For PDF/CII/UBL download links, call view_invoice_demo with {"sample":"downloads"} or open /v1/demo?sample=downloads. Try your own invoices with 10 free operations over 14 days, or use the EUR 10 prepaid pack: 100 operations, no expiry or subscription.
Connect a client¶
Recommended for your own documents: use https://facturx.orvel.dev/mcp/account. This endpoint requests OAuth authentication on the first connection, so clients can display Authenticate immediately. In Claude Code:
Then open /mcp, select facturx-account, authenticate and approve the browser request. Tokens are specific to the endpoint: authenticate again if you switch URLs. Existing connections and the public sample continue to work at /mcp. The configurations below request login immediately; use /mcp instead only for the no-account sample and helpers.
For Cursor, add this to .cursor/mcp.json:
For VS Code, add this to .vscode/mcp.json:
For Claude Code:
In Claude Code, open /mcp, choose Factur-X and choose Authenticate. On the separate public /mcp endpoint, clients may defer authentication until a custom-document call; the fixed demo and discovery remain public there.
The browser opens Factur-X. Sign in with email/password, Google or your Orvel account. Start the bounded trial or use your linked plan, check the requesting app and callback domain, then choose Approve connection. If you are already signed in to Factur-X, you go directly to approval. The client receives revocable tokens; your password and API key stay with Orvel. The exact button label depends on your client.
Directory authentication¶
The endpoint accepts either Authorization: Bearer YOUR_API_KEY or
X-API-Key: YOUR_API_KEY. Both use the same key, plan checks and document allowance.
Use one header, not both. Never put the key in the URL or a tool argument.
- Smithery: leave the optional
apiKeysetting empty for public samples and helpers. For your documents, enter your key without aBearerprefix; it is forwarded asX-API-Key. - Claude directory: use OAuth when Claude asks you to sign in. On the Orvel consent page, sign in and approve your trial or linked plan; no OAuth client ID or static header is needed. A directory submission is not a guarantee of availability; until approved, use a custom connector.
- Glama and other clients: use either supported header in the client's credential settings. Discovery and the fixed sample demo do not require a key. OAuth clients discover authorization automatically from the endpoint; sign in and approve access to your linked allowance.
Process your own documents¶
- Start with a verified-account trial, or buy 100 operations for EUR 10: no expiry, subscription or automatic recharge. For regular use, Starter is EUR 29/month for 1,000 operations.
- Open your Factur-X workspace. Purchases made from the signed-in workspace link automatically after payment confirmation. Older purchases can be linked once with their existing key.
- Connect the same MCP endpoint using OAuth and approve access on the Factur-X consent page,
or configure
Authorization: Bearer <your-key>in clients that support headers. - Ask your assistant to call
get_invoice_example, replace the sample data, then callgenerate_invoice. Choosecii-xml,ubl-xml, orfacturx-pdfas the output. Generation counts as one document; a separate validation call counts as another.
Cursor's authenticated entry:
{"mcpServers":{"facturx":{"url":"https://facturx.orvel.dev/mcp","headers":{"Authorization":"Bearer YOUR_API_KEY"}}}}
For VS Code, use the same headers object inside the facturx entry under servers. For Claude Code, add --header "Authorization: Bearer YOUR_API_KEY" to the command above. Keep key-containing configuration out of Git and public chats.
For Claude Desktop with Node.js installed, use the mcp-remote bridge in claude_desktop_config.json:
{
"mcpServers": {
"facturx": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://facturx.orvel.dev/mcp", "--header", "Authorization:${FACTURX_AUTH_HEADER}"],
"env": {"FACTURX_AUTH_HEADER": "Bearer YOUR_API_KEY"}
}
}
}
REST uses the same key and allowance. Check it with GET /v1/usage. Existing Free keys retain their monthly allowance; existing trial keys retain their original expiry and total allowance. Anonymous trials and renewable free subscriptions remain closed. Verified Orvel accounts can claim the bounded trial described on the invoice workspace.
Troubleshooting¶
| Result | What to do |
|---|---|
Tools appear but a document call says 401 |
Sign in through OAuth with your existing key, configure a bearer key, or use view_invoice_demo without a key. |
402 / quota reached |
Open pricing or the customer portal. Creating another trial key is not supported. |
503 / usage checks unavailable |
Retry later. A storage outage cannot reset an allowance. |
plan_excluded from check_party |
Registry checks need Pro or Scale. Starter still supports document processing. |
| Tools are missing after editing configuration | Reconnect or restart the client so it reloads the tool list. |
| Invalid invoice / rule finding | Use explain_finding with the returned rule ID, fix the input, and retry. Input/service errors do not consume document credits. A completed validation report, including an invalid verdict, consumes one operation; trial attempts are separately capped. |
Tools¶
Complete a stateless invoice workflow¶
- Start with
get_invoice_exampleand replace the sample data. - On Pro or Scale, use
check_invoice_partiesto check seller and buyer together before generation. - Generate the file, or use
embed_xmlfor an existing PDF and CII XML. Correct the input and regenerate to revise a document; usedraft_credit_notefor a full or partial credit. - Save the returned file and evidence in your own system, and hand it to your delivery workflow. For received documents, extract the data, validate it and explain any findings.
The server returns documents and evidence directly. Your application owns persistence, delivery and deletion; there is no server-side invoice record or delivery queue to manage.
The paired check accepts exactly one seller and one buyer, without invoice lines:
{
"seller": {"siren": "552081317", "country_code": "FR"},
"buyer": {"vat": "DE123456789", "country_code": "DE"}
}
Replace these illustrative identifiers with your parties' real identifiers. Roles are assigned by argument name. Optional requester_vat is shared by both lookups. status is invoice_ready only when both checks pass, not_ready when either has a definite failure, and inconclusive otherwise. Always inspect parties.seller and parties.buyer for individual reasons and evidence, including a partial outage alongside a definite failure. The call consumes no Factur-X document quota, but each of the two lookups is subject to EU Verify's access and usage limits. A denied plan or upstream limit never counts as readiness.
| Tool | What it does | Input | Output |
|---|---|---|---|
view_invoice_demo |
Free fixed-input validation or downloads | sample: valid (default), invalid, or downloads |
Findings, engine version, validation time and cache status, or sample links |
generate_invoice |
Invoice JSON to Factur-X PDF/A-3, CII XML or UBL 2.1. Refuses the file when a party is not invoice-ready | invoice (object), profile, output, language, check, footer_text, party_check |
Text summary (totals, evidence id, warnings) + the document as an embedded resource (base64 PDF or XML text) |
embed_xml |
Attach CII XML to your own PDF | pdf_base64, xml, check, language |
Summary + PDF/A-3 as embedded resource |
validate_invoice |
XSD + EN 16931 schematron (+ French fr-ctc) |
document_base64 or xml, check |
Structured report: valid, profile, findings[] with rule ids and json_pointer |
extract_invoice |
Read an e-invoice | document_base64 or xml, include_xml |
fields (parties, totals, VAT, lines) and optionally the XML |
check_party |
Ask EU Verify if one seller or buyer is invoice-ready. Does not count as a document. Pro and Scale keys reach the registers; a trial key gets plan_excluded |
role, and siren, siret or vat. Optional iban, address, eori |
status (invoice_ready, not_ready, upstream_unavailable, plan_excluded), reasons, evidence |
check_invoice_parties |
Preflight seller and buyer together; Pro/Scale registry access | seller, buyer, optional requester_vat |
Overall status plus each party's status, reasons and evidence |
explain_finding |
Field to fix for a rule id. No key, and it does not count | rule, optional message and json_pointer |
fix and example for the documented rules; known: false otherwise |
get_invoice_example |
A complete invoice or credit-note body to copy. No key or quota | profile, document_type: invoice (default) or credit_note |
invoice, plus profile, check and party_check. Replace sample parties and original-invoice references |
draft_credit_note |
Full or selected-line credit-note body. No key or quota | Original invoice, number, issue_date, optional due_date and credit_lines |
Type 381 with original reference. Call generate_invoice to emit the file |
Full and partial credits¶
For a synthetic credit-note body, call get_invoice_example with {"document_type":"credit_note"}. Replace its parties, dates, number, lines and preceding_invoices before generation.
For an actual invoice, pass the original input object to draft_credit_note. Omit credit_lines for a full credit. To credit two units of the original second line, include:
line_index is the 1-based position in the original invoice.lines array, not the line's id. Positions must be distinct, the list cannot be empty, and quantities must be positive and no larger than the original. Unselected lines are excluded. Prices, units and VAT rates are retained; original totals, prepayments, rounding and paid status are cleared in selected-line mode so generation recalculates them.
Document-level allowances/charges, and reduced quantities on lines with allowances/charges or overridden net amounts, need an explicit allocation. The helper rejects these cases rather than choosing an allocation; prepare the credit body explicitly and use generate_invoice. Whole selected lines retain their own adjustments. This is a stateless draft, not a payment refund: your system must track previous credits to prevent cumulative over-crediting.
Resources:
facturx://schema/invoice: JSON Schema of theinvoiceargument (agents read it to fill the object correctly).facturx://guide/french-reform: one-page cheat sheet of the French reform.
Tools are annotated readOnlyHint / idempotentHint: invoice documents are not stored or delivered. Repeated successful document calls still consume quota. OAuth connection records and usage counters are stored separately.
The server creates and checks documents. It does not send invoices to recipients or act as a Peppol access point. Party checks and any unavailable registry evidence are reported separately from document validation.
OAuth connections¶
Connect to https://facturx.orvel.dev/mcp. When your client asks you to authorize, sign in to Factur-X, verify the requesting application's callback domain, then choose Approve connection. Connecting does not create a subscription or reset your allowance. Starting the 10-operation trial is a separate, explicit action. Older purchases can be linked with their API key once in the workspace.
Authorization uses the code flow with SHA-256 PKCE, exact registered callback URLs and an MCP resource audience. Client registration is supported. Access tokens last 15 minutes; refresh tokens rotate and the connection expires after at most 30 days. Revoking a connection invalidates its tokens; underlying key expiry and plan restrictions still apply. Use your client's revocation action or revoke the underlying API key to end access. API-key headers remain supported for clients that prefer them.
Technical discovery: /.well-known/oauth-protected-resource/mcp and /.well-known/oauth-authorization-server. Request resource https://facturx.orvel.dev/mcp and scope mcp. OAuth access tokens are for this MCP endpoint, not the REST API.
Understand the result¶
status is passed, failed or incomplete. Inspect coverage before interpreting valid: a passing XML report is not full PDF/A certification, a register lookup or delivery acceptance. Raw findings remain available. issues groups related findings and includes curated remediation where known. business_rule identifies rules such as BR-07 without changing the original engine rule. json_pointer names the Orvel generation JSON (pointer_scope=orvel_generate_request); location remains the original XML XPath. Unknown repairs remain null rather than guessed. Reports include engine versions, input hash and validation timestamp.