invoicexml-facturx 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. invoicexml_facturx-0.1.0/.gitignore +11 -0
  2. invoicexml_facturx-0.1.0/CHANGELOG.md +12 -0
  3. invoicexml_facturx-0.1.0/LICENSE +21 -0
  4. invoicexml_facturx-0.1.0/PKG-INFO +190 -0
  5. invoicexml_facturx-0.1.0/README.md +161 -0
  6. invoicexml_facturx-0.1.0/pyproject.toml +51 -0
  7. invoicexml_facturx-0.1.0/src/invoicexml_facturx/__init__.py +50 -0
  8. invoicexml_facturx-0.1.0/src/invoicexml_facturx/_client.py +301 -0
  9. invoicexml_facturx-0.1.0/src/invoicexml_facturx/_errors.py +129 -0
  10. invoicexml_facturx-0.1.0/src/invoicexml_facturx/_files.py +82 -0
  11. invoicexml_facturx-0.1.0/src/invoicexml_facturx/_serde.py +138 -0
  12. invoicexml_facturx-0.1.0/src/invoicexml_facturx/_version.py +6 -0
  13. invoicexml_facturx-0.1.0/src/invoicexml_facturx/models.py +1070 -0
  14. invoicexml_facturx-0.1.0/src/invoicexml_facturx/profiles.py +19 -0
  15. invoicexml_facturx-0.1.0/src/invoicexml_facturx/py.typed +0 -0
  16. invoicexml_facturx-0.1.0/src/invoicexml_facturx/transport.py +76 -0
  17. invoicexml_facturx-0.1.0/src/invoicexml_facturx/validation.py +145 -0
  18. invoicexml_facturx-0.1.0/tests/conftest.py +111 -0
  19. invoicexml_facturx-0.1.0/tests/fixtures/create-400.json +26 -0
  20. invoicexml_facturx-0.1.0/tests/fixtures/extract-json.json +44 -0
  21. invoicexml_facturx-0.1.0/tests/fixtures/validate-facturx-invalid.json +46 -0
  22. invoicexml_facturx-0.1.0/tests/fixtures/validate-facturx-valid.json +25 -0
  23. invoicexml_facturx-0.1.0/tests/test_client.py +311 -0
  24. invoicexml_facturx-0.1.0/tests/test_live.py +186 -0
  25. invoicexml_facturx-0.1.0/tests/test_models.py +68 -0
  26. invoicexml_facturx-0.1.0/tools/field-docs.json +186 -0
  27. invoicexml_facturx-0.1.0/tools/generate_models.py +339 -0
  28. invoicexml_facturx-0.1.0/tools/generator-config.json +25 -0
  29. invoicexml_facturx-0.1.0/tools/openapi.json +1721 -0
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .venv/
5
+ venv/
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+ .idea/
10
+ .vscode/
11
+ .DS_Store
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ First release.
6
+
7
+ - `FacturXClient` with create, validate, embed and extract calls for Factur-X.
8
+ - Optional French BR-FR rule set on create, validate and embed, and the official profile identifiers.
9
+ - EN 16931 dataclasses generated from the InvoiceXML OpenAPI schema, with exact decimal amounts.
10
+ - Typed validation results and rule findings on rejected create requests.
11
+ - Automatic retry on HTTP 429 with `Retry-After` support.
12
+ - No dependencies outside the standard library.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 InvoiceXML
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.5
2
+ Name: invoicexml-facturx
3
+ Version: 0.1.0
4
+ Summary: Python client for Factur-X e-invoices: create Factur-X PDF/A-3 from EN 16931 data, validate (including the French BR-FR rules), embed and extract, via the InvoiceXML API.
5
+ Project-URL: Homepage, https://invoicexml.com
6
+ Project-URL: Documentation, https://api.invoicexml.com/v1/scalar/
7
+ Project-URL: Repository, https://github.com/InvoiceXML/invoicexml-python-facturx
8
+ Project-URL: Issues, https://github.com/InvoiceXML/invoicexml-python-facturx/issues
9
+ Project-URL: Changelog, https://github.com/InvoiceXML/invoicexml-python-facturx/blob/main/CHANGELOG.md
10
+ Author-email: InvoiceXML <support@invoicexml.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: br-fr,cii,e-invoicing,einvoice,en16931,factur-x,facture electronique,facturx,invoice,pdf/a-3
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.10
28
+ Description-Content-Type: text/markdown
29
+
30
+ # InvoiceXML Factur-X client for Python
31
+
32
+ Create, validate, embed and extract **Factur-X** e-invoices from Python with the [InvoiceXML API](https://invoicexml.com). Build the invoice from typed EN 16931 dataclasses and get back a compliant Factur-X PDF/A-3, already validated against the official rule set and, on request, the French mandate's BR-FR rules.
33
+
34
+ - Python 3.10 or later, no dependencies outside the standard library
35
+ - Typed EN 16931 dataclasses generated from the API's OpenAPI schema
36
+ - All Factur-X profiles, from MINIMUM to EXTENDED
37
+ - Exact decimal amounts, never floats on the wire
38
+ - Rejected invoices surface the violated rules as typed findings
39
+ - Automatic retry on HTTP 429, honouring `Retry-After`
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install invoicexml-facturx
45
+ ```
46
+
47
+ ## Get an API key
48
+
49
+ Sign up at [invoicexml.com/account/signup](https://invoicexml.com/account/signup) for a 30-day free trial with 100 credits. No credit card is needed. The client reads the key from the `INVOICEXML_API_KEY` environment variable, or you pass it as `api_key`.
50
+
51
+ ## Create a Factur-X invoice
52
+
53
+ ```python
54
+ from pathlib import Path
55
+ from invoicexml_facturx import *
56
+
57
+ client = FacturXClient()
58
+ invoice = InvoiceDocument(
59
+ invoice_number="FA-2026-0042", issue_date="2026-09-01", currency="EUR",
60
+ seller=SellerParty(name="Atelier Numerique SARL", vat_identifier="FR32123456789",
61
+ legal_registration=SellerLegalRegistration("123456789", "0002"),
62
+ postal_address=SellerPostalAddress(line1="18 Rue de la Republique", city="Lyon", post_code="69002", country="FR")),
63
+ buyer=BuyerParty(name="Librairie Grand Siecle SAS",
64
+ postal_address=BuyerPostalAddress(line1="5 Boulevard Saint-Germain", city="Paris", post_code="75005", country="FR")),
65
+ payment_details=PaymentDetails(payment_account_identifier="FR7630006000011234567890189"),
66
+ lines=[InvoiceLine(quantity=12, item=ItemInformation(name="Maintenance applicative"),
67
+ price_details=PriceDetails(net_price="180.00"), vat_information=LineVatInformation(rate=20))],
68
+ )
69
+ Path("facture-facturx.pdf").write_bytes(client.create_facturx(invoice, InvoiceOptions(language=PdfLanguage.FR)))
70
+ ```
71
+
72
+ Totals and the VAT breakdown are calculated from the lines. The PDF is a Factur-X PDF/A-3 with the EN 16931 profile and the CII XML embedded as `factur-x.xml`. Amounts accept `Decimal`, `int`, `float` or a numeric string, and are sent as exact JSON numbers.
73
+
74
+ ## API surface
75
+
76
+ | Method | Endpoint | Returns |
77
+ |---|---|---|
78
+ | `create_facturx(invoice, options=None)` | `POST /v1/create/facturx` | Factur-X PDF/A-3 (`bytes`) |
79
+ | `validate_facturx(pdf, *, rules=None)` | `POST /v1/validate/facturx` | `ValidationResult` |
80
+ | `embed_facturx(pdf, xml, *, skip_validation=False, rules=None)` | `POST /v1/embed/facturx` | Factur-X PDF/A-3 (`bytes`) |
81
+ | `extract_json(file)` | `POST /v1/extract/json` | `InvoiceDocument` |
82
+ | `extract_attachments(file)` | `POST /v1/extract/attachments` | ZIP of embedded documents (`bytes`) |
83
+
84
+ File parameters accept a path, the raw bytes of a PDF or XML document, or a `FileUpload`. The invoice and options also accept plain dicts in the API's JSON shape.
85
+
86
+ ## The French e-invoicing mandate
87
+
88
+ Receiving e-invoices is mandatory for every business in France from September 2026, and issuing follows in phases. Add the `BR_FR` rule set to check an invoice against the mandate's BR-FR rules on top of its profile. It works on create, validate and embed, and needs at least the BASIC profile:
89
+
90
+ ```python
91
+ invoice.business_process_type = "S1" # billing mode, here services
92
+ invoice.notes = [InvoiceNote("Indemnite forfaitaire pour frais de recouvrement : 40 EUR", "PMT")]
93
+ pdf = client.create_facturx(invoice, InvoiceOptions(rules=[RuleSet.BR_FR]))
94
+
95
+ result = client.validate_facturx("facture.pdf", rules=[RuleSet.BR_FR])
96
+ ```
97
+
98
+ The BR-FR rules ask for the billing mode in `business_process_type`, for example `B1` for goods, `S1` for services or `M1` for mixed invoices. French invoices also carry the legal mentions as notes with the subject codes `PMT`, `PMD` and `AAB`. Violations come back with the official rule identifiers and French messages.
99
+
100
+ ## Profiles
101
+
102
+ EN 16931 is the default. To create another profile, set its identifier from `FacturXProfile` as the invoice's `specification_id`:
103
+
104
+ ```python
105
+ invoice.specification_id = FacturXProfile.BASIC
106
+ pdf = client.create_facturx(invoice)
107
+ ```
108
+
109
+ `MINIMUM` and `BASIC_WL` carry header data only and serve as booking aids. `BASIC` is the lowest profile the French mandate accepts, and `EXTENDED` adds data for complex invoicing.
110
+
111
+ ## Rejected invoices
112
+
113
+ Every create call validates the invoice before a document is produced. When rules are violated, the call raises `InvoiceValidationError` with the findings, so a non-compliant invoice never leaves your system:
114
+
115
+ ```python
116
+ try:
117
+ client.create_facturx(invoice)
118
+ except InvoiceValidationError as e:
119
+ for finding in e.errors:
120
+ print(finding.rule, finding.fields, finding.message)
121
+ ```
122
+
123
+ Each finding carries the rule id (for example `BR-CO-10`), the layer (`xsd`, `en16931` or `cius`), a plain-language message, the field paths into your invoice, and the verbatim validator output.
124
+
125
+ ## Validate incoming invoices
126
+
127
+ Valid and invalid invoices both return a `ValidationResult`. Check `result.valid`:
128
+
129
+ ```python
130
+ result = client.validate_facturx("facture-fournisseur.pdf")
131
+ if not result.valid:
132
+ for finding in result.errors:
133
+ print(finding)
134
+ ```
135
+
136
+ The result also carries the detected profile, the schema and Schematron verdicts, and a field report with one row per invoice field.
137
+
138
+ ## Errors, rate limits and retries
139
+
140
+ All exceptions extend `InvoiceXmlError`.
141
+
142
+ | Exception | When |
143
+ |---|---|
144
+ | `InvoiceValidationError` | HTTP 400 with rule findings |
145
+ | `AuthenticationError` | HTTP 401, the API key is missing or inactive |
146
+ | `PayloadTooLargeError` | HTTP 413, a file exceeds 20 MB |
147
+ | `RateLimitError` | HTTP 429 after the automatic retries |
148
+ | `InvoiceXmlApiError` | Any other error response, with `error_code` from `ErrorCode` |
149
+ | `InvoiceXmlConnectionError` | The API could not be reached |
150
+
151
+ On HTTP 429 the client waits for the `Retry-After` delay and retries, three times by default. Rate-limited requests consume no credits.
152
+
153
+ ```python
154
+ client = FacturXClient(api_key, max_retries=5, max_retry_wait=30, timeout=60)
155
+ ```
156
+
157
+ The client holds no connection state and is safe to share between threads. To send requests through `requests`, `httpx` or your own tracing, pass any object with a `send(request)` method as `transport`; see `invoicexml_facturx.transport`.
158
+
159
+ ## Why InvoiceXML
160
+
161
+ - **Rule sets updated on their legal effective dates.** New Factur-X, BR-FR and EN 16931 rule versions go live on the API the day they take legal effect. The rules run on the server, so you never update this package or redeploy for a new rule version.
162
+ - **Validated before delivery.** Every document is checked against the official XSD and Schematron rules before it is returned.
163
+ - **Zero data retention.** Invoices are processed in memory and purged when the response is delivered. Nothing is stored or logged, and nothing is used for AI training.
164
+ - **Processing in Frankfurt.** All processing happens in the EU, in Frankfurt, Germany. See [invoicexml.com/trust](https://invoicexml.com/trust).
165
+ - **Integration assistance.** Questions about your integration go to [support@invoicexml.com](mailto:support@invoicexml.com), from setup to your first production invoice.
166
+
167
+ ## Documentation
168
+
169
+ - API reference: [api.invoicexml.com/v1/scalar](https://api.invoicexml.com/v1/scalar/)
170
+ - OpenAPI 3.1 schema: [api.invoicexml.com/v1/openapi](https://api.invoicexml.com/v1/openapi)
171
+ - ZUGFeRD and XRechnung: see the companion package [invoicexml-zugferd-xrechnung](https://github.com/InvoiceXML/invoicexml-python-zugferd-xrechnung)
172
+
173
+ ## Development
174
+
175
+ ```bash
176
+ pip install pytest
177
+ pytest
178
+ ```
179
+
180
+ The unit tests run against a stubbed HTTP layer. A live suite against the real API runs only when you opt in, and it consumes about 13 credits per run:
181
+
182
+ ```bash
183
+ INVOICEXML_API_KEY=your-key INVOICEXML_LIVE=1 pytest -m live
184
+ ```
185
+
186
+ The module `invoicexml_facturx/models.py` is generated from the OpenAPI schema. To refresh it after a schema change, run `python tools/generate_models.py --fetch` and commit the result.
187
+
188
+ ## License
189
+
190
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,161 @@
1
+ # InvoiceXML Factur-X client for Python
2
+
3
+ Create, validate, embed and extract **Factur-X** e-invoices from Python with the [InvoiceXML API](https://invoicexml.com). Build the invoice from typed EN 16931 dataclasses and get back a compliant Factur-X PDF/A-3, already validated against the official rule set and, on request, the French mandate's BR-FR rules.
4
+
5
+ - Python 3.10 or later, no dependencies outside the standard library
6
+ - Typed EN 16931 dataclasses generated from the API's OpenAPI schema
7
+ - All Factur-X profiles, from MINIMUM to EXTENDED
8
+ - Exact decimal amounts, never floats on the wire
9
+ - Rejected invoices surface the violated rules as typed findings
10
+ - Automatic retry on HTTP 429, honouring `Retry-After`
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pip install invoicexml-facturx
16
+ ```
17
+
18
+ ## Get an API key
19
+
20
+ Sign up at [invoicexml.com/account/signup](https://invoicexml.com/account/signup) for a 30-day free trial with 100 credits. No credit card is needed. The client reads the key from the `INVOICEXML_API_KEY` environment variable, or you pass it as `api_key`.
21
+
22
+ ## Create a Factur-X invoice
23
+
24
+ ```python
25
+ from pathlib import Path
26
+ from invoicexml_facturx import *
27
+
28
+ client = FacturXClient()
29
+ invoice = InvoiceDocument(
30
+ invoice_number="FA-2026-0042", issue_date="2026-09-01", currency="EUR",
31
+ seller=SellerParty(name="Atelier Numerique SARL", vat_identifier="FR32123456789",
32
+ legal_registration=SellerLegalRegistration("123456789", "0002"),
33
+ postal_address=SellerPostalAddress(line1="18 Rue de la Republique", city="Lyon", post_code="69002", country="FR")),
34
+ buyer=BuyerParty(name="Librairie Grand Siecle SAS",
35
+ postal_address=BuyerPostalAddress(line1="5 Boulevard Saint-Germain", city="Paris", post_code="75005", country="FR")),
36
+ payment_details=PaymentDetails(payment_account_identifier="FR7630006000011234567890189"),
37
+ lines=[InvoiceLine(quantity=12, item=ItemInformation(name="Maintenance applicative"),
38
+ price_details=PriceDetails(net_price="180.00"), vat_information=LineVatInformation(rate=20))],
39
+ )
40
+ Path("facture-facturx.pdf").write_bytes(client.create_facturx(invoice, InvoiceOptions(language=PdfLanguage.FR)))
41
+ ```
42
+
43
+ Totals and the VAT breakdown are calculated from the lines. The PDF is a Factur-X PDF/A-3 with the EN 16931 profile and the CII XML embedded as `factur-x.xml`. Amounts accept `Decimal`, `int`, `float` or a numeric string, and are sent as exact JSON numbers.
44
+
45
+ ## API surface
46
+
47
+ | Method | Endpoint | Returns |
48
+ |---|---|---|
49
+ | `create_facturx(invoice, options=None)` | `POST /v1/create/facturx` | Factur-X PDF/A-3 (`bytes`) |
50
+ | `validate_facturx(pdf, *, rules=None)` | `POST /v1/validate/facturx` | `ValidationResult` |
51
+ | `embed_facturx(pdf, xml, *, skip_validation=False, rules=None)` | `POST /v1/embed/facturx` | Factur-X PDF/A-3 (`bytes`) |
52
+ | `extract_json(file)` | `POST /v1/extract/json` | `InvoiceDocument` |
53
+ | `extract_attachments(file)` | `POST /v1/extract/attachments` | ZIP of embedded documents (`bytes`) |
54
+
55
+ File parameters accept a path, the raw bytes of a PDF or XML document, or a `FileUpload`. The invoice and options also accept plain dicts in the API's JSON shape.
56
+
57
+ ## The French e-invoicing mandate
58
+
59
+ Receiving e-invoices is mandatory for every business in France from September 2026, and issuing follows in phases. Add the `BR_FR` rule set to check an invoice against the mandate's BR-FR rules on top of its profile. It works on create, validate and embed, and needs at least the BASIC profile:
60
+
61
+ ```python
62
+ invoice.business_process_type = "S1" # billing mode, here services
63
+ invoice.notes = [InvoiceNote("Indemnite forfaitaire pour frais de recouvrement : 40 EUR", "PMT")]
64
+ pdf = client.create_facturx(invoice, InvoiceOptions(rules=[RuleSet.BR_FR]))
65
+
66
+ result = client.validate_facturx("facture.pdf", rules=[RuleSet.BR_FR])
67
+ ```
68
+
69
+ The BR-FR rules ask for the billing mode in `business_process_type`, for example `B1` for goods, `S1` for services or `M1` for mixed invoices. French invoices also carry the legal mentions as notes with the subject codes `PMT`, `PMD` and `AAB`. Violations come back with the official rule identifiers and French messages.
70
+
71
+ ## Profiles
72
+
73
+ EN 16931 is the default. To create another profile, set its identifier from `FacturXProfile` as the invoice's `specification_id`:
74
+
75
+ ```python
76
+ invoice.specification_id = FacturXProfile.BASIC
77
+ pdf = client.create_facturx(invoice)
78
+ ```
79
+
80
+ `MINIMUM` and `BASIC_WL` carry header data only and serve as booking aids. `BASIC` is the lowest profile the French mandate accepts, and `EXTENDED` adds data for complex invoicing.
81
+
82
+ ## Rejected invoices
83
+
84
+ Every create call validates the invoice before a document is produced. When rules are violated, the call raises `InvoiceValidationError` with the findings, so a non-compliant invoice never leaves your system:
85
+
86
+ ```python
87
+ try:
88
+ client.create_facturx(invoice)
89
+ except InvoiceValidationError as e:
90
+ for finding in e.errors:
91
+ print(finding.rule, finding.fields, finding.message)
92
+ ```
93
+
94
+ Each finding carries the rule id (for example `BR-CO-10`), the layer (`xsd`, `en16931` or `cius`), a plain-language message, the field paths into your invoice, and the verbatim validator output.
95
+
96
+ ## Validate incoming invoices
97
+
98
+ Valid and invalid invoices both return a `ValidationResult`. Check `result.valid`:
99
+
100
+ ```python
101
+ result = client.validate_facturx("facture-fournisseur.pdf")
102
+ if not result.valid:
103
+ for finding in result.errors:
104
+ print(finding)
105
+ ```
106
+
107
+ The result also carries the detected profile, the schema and Schematron verdicts, and a field report with one row per invoice field.
108
+
109
+ ## Errors, rate limits and retries
110
+
111
+ All exceptions extend `InvoiceXmlError`.
112
+
113
+ | Exception | When |
114
+ |---|---|
115
+ | `InvoiceValidationError` | HTTP 400 with rule findings |
116
+ | `AuthenticationError` | HTTP 401, the API key is missing or inactive |
117
+ | `PayloadTooLargeError` | HTTP 413, a file exceeds 20 MB |
118
+ | `RateLimitError` | HTTP 429 after the automatic retries |
119
+ | `InvoiceXmlApiError` | Any other error response, with `error_code` from `ErrorCode` |
120
+ | `InvoiceXmlConnectionError` | The API could not be reached |
121
+
122
+ On HTTP 429 the client waits for the `Retry-After` delay and retries, three times by default. Rate-limited requests consume no credits.
123
+
124
+ ```python
125
+ client = FacturXClient(api_key, max_retries=5, max_retry_wait=30, timeout=60)
126
+ ```
127
+
128
+ The client holds no connection state and is safe to share between threads. To send requests through `requests`, `httpx` or your own tracing, pass any object with a `send(request)` method as `transport`; see `invoicexml_facturx.transport`.
129
+
130
+ ## Why InvoiceXML
131
+
132
+ - **Rule sets updated on their legal effective dates.** New Factur-X, BR-FR and EN 16931 rule versions go live on the API the day they take legal effect. The rules run on the server, so you never update this package or redeploy for a new rule version.
133
+ - **Validated before delivery.** Every document is checked against the official XSD and Schematron rules before it is returned.
134
+ - **Zero data retention.** Invoices are processed in memory and purged when the response is delivered. Nothing is stored or logged, and nothing is used for AI training.
135
+ - **Processing in Frankfurt.** All processing happens in the EU, in Frankfurt, Germany. See [invoicexml.com/trust](https://invoicexml.com/trust).
136
+ - **Integration assistance.** Questions about your integration go to [support@invoicexml.com](mailto:support@invoicexml.com), from setup to your first production invoice.
137
+
138
+ ## Documentation
139
+
140
+ - API reference: [api.invoicexml.com/v1/scalar](https://api.invoicexml.com/v1/scalar/)
141
+ - OpenAPI 3.1 schema: [api.invoicexml.com/v1/openapi](https://api.invoicexml.com/v1/openapi)
142
+ - ZUGFeRD and XRechnung: see the companion package [invoicexml-zugferd-xrechnung](https://github.com/InvoiceXML/invoicexml-python-zugferd-xrechnung)
143
+
144
+ ## Development
145
+
146
+ ```bash
147
+ pip install pytest
148
+ pytest
149
+ ```
150
+
151
+ The unit tests run against a stubbed HTTP layer. A live suite against the real API runs only when you opt in, and it consumes about 13 credits per run:
152
+
153
+ ```bash
154
+ INVOICEXML_API_KEY=your-key INVOICEXML_LIVE=1 pytest -m live
155
+ ```
156
+
157
+ The module `invoicexml_facturx/models.py` is generated from the OpenAPI schema. To refresh it after a schema change, run `python tools/generate_models.py --fetch` and commit the result.
158
+
159
+ ## License
160
+
161
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "invoicexml-facturx"
7
+ version = "0.1.0"
8
+ description = "Python client for Factur-X e-invoices: create Factur-X PDF/A-3 from EN 16931 data, validate (including the French BR-FR rules), embed and extract, via the InvoiceXML API."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "InvoiceXML", email = "support@invoicexml.com" }]
14
+ keywords = ["factur-x", "facturx", "facture electronique", "e-invoicing", "einvoice", "en16931", "br-fr", "cii", "pdf/a-3", "invoice"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Office/Business :: Financial :: Accounting",
27
+ "Topic :: Software Development :: Libraries :: Python Modules",
28
+ "Typing :: Typed",
29
+ ]
30
+ dependencies = []
31
+
32
+ [project.urls]
33
+ Homepage = "https://invoicexml.com"
34
+ Documentation = "https://api.invoicexml.com/v1/scalar/"
35
+ Repository = "https://github.com/InvoiceXML/invoicexml-python-facturx"
36
+ Issues = "https://github.com/InvoiceXML/invoicexml-python-facturx/issues"
37
+ Changelog = "https://github.com/InvoiceXML/invoicexml-python-facturx/blob/main/CHANGELOG.md"
38
+
39
+ [dependency-groups]
40
+ dev = ["pytest>=8"]
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["src/invoicexml_facturx"]
44
+
45
+ [tool.hatch.build.targets.sdist]
46
+ include = ["src", "tests", "tools", "README.md", "CHANGELOG.md", "LICENSE"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
50
+ pythonpath = ["src", "tests"]
51
+ markers = ["live: calls the real API and consumes credits (opt in with INVOICEXML_LIVE=1)"]
@@ -0,0 +1,50 @@
1
+ """Python client for Factur-X e-invoices via the InvoiceXML API.
2
+
3
+ Create Factur-X PDF/A-3 hybrids from the EN 16931 model, validate incoming
4
+ files (optionally against the French BR-FR rules), embed CII XML into existing
5
+ PDFs, and extract invoices back into the model.
6
+
7
+ >>> from invoicexml_facturx import FacturXClient
8
+ >>> client = FacturXClient() # reads INVOICEXML_API_KEY
9
+ """
10
+
11
+ from ._client import DEFAULT_BASE_URL, FacturXClient
12
+ from ._errors import (
13
+ AuthenticationError,
14
+ ErrorCode,
15
+ InvoiceValidationError,
16
+ InvoiceXmlApiError,
17
+ InvoiceXmlConnectionError,
18
+ InvoiceXmlError,
19
+ PayloadTooLargeError,
20
+ RateLimitError,
21
+ )
22
+ from ._files import FileInput, FileUpload
23
+ from ._version import __version__
24
+ from .models import * # noqa: F401,F403
25
+ from .models import __all__ as _model_names
26
+ from .profiles import FacturXProfile
27
+ from .validation import ReportFinding, ReportRow, ValidationData, ValidationFinding, ValidationResult
28
+
29
+ __all__ = [
30
+ "DEFAULT_BASE_URL",
31
+ "AuthenticationError",
32
+ "ErrorCode",
33
+ "FileInput",
34
+ "FileUpload",
35
+ "InvoiceValidationError",
36
+ "InvoiceXmlApiError",
37
+ "InvoiceXmlConnectionError",
38
+ "InvoiceXmlError",
39
+ "PayloadTooLargeError",
40
+ "RateLimitError",
41
+ "ReportFinding",
42
+ "ReportRow",
43
+ "ValidationData",
44
+ "ValidationFinding",
45
+ "ValidationResult",
46
+ "FacturXClient",
47
+ "FacturXProfile",
48
+ "__version__",
49
+ *_model_names,
50
+ ]