beliq 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.
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python-version: ['3.10', '3.11', '3.12', '3.13']
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - run: python -m pip install -e ".[dev]"
23
+ - run: ruff check src tests
24
+ - run: mypy
25
+ # Includes tests/test_spec_contract.py, which fails if the hand-written
26
+ # contracts drift from the vendored openapi.json.
27
+ - run: pytest
@@ -0,0 +1,50 @@
1
+ name: Release
2
+
3
+ # Publishes beliq to PyPI via Trusted Publishing (OIDC) - no API token needed.
4
+ # Attestations are generated automatically. Trigger by pushing a version tag
5
+ # (e.g. v0.1.1) or running the workflow manually.
6
+ #
7
+ # One-time setup on pypi.org: register beliq-eu/beliq-sdk-python as a Trusted
8
+ # Publisher for the beliq project (workflow `release.yml`, environment `pypi`).
9
+ # Prefer a pending publisher so the very first tag run creates the project via
10
+ # OIDC with no local upload.
11
+ on:
12
+ push:
13
+ tags:
14
+ - 'v*.*.*'
15
+ workflow_dispatch:
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ jobs:
21
+ test:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: '3.12'
28
+ - run: python -m pip install -e ".[dev]"
29
+ - run: ruff check src tests
30
+ - run: mypy
31
+ - run: pytest
32
+
33
+ publish:
34
+ needs: test
35
+ runs-on: ubuntu-latest
36
+ environment: pypi
37
+ permissions:
38
+ id-token: write # required for PyPI Trusted Publishing (OIDC)
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ - uses: actions/setup-python@v5
42
+ with:
43
+ python-version: '3.12'
44
+ - name: Build
45
+ run: |
46
+ python -m pip install build
47
+ python -m build
48
+ # No `password`: the action exchanges the GitHub OIDC token with PyPI.
49
+ - name: Publish to PyPI
50
+ uses: pypa/gh-action-pypi-publish@release/v1
beliq-0.1.0/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ .venv
2
+ __pycache__
3
+ *.pyc
4
+ dist
5
+ build
6
+ *.egg-info
7
+ .mypy_cache
8
+ .ruff_cache
9
+ .pytest_cache
10
+ .DS_Store
beliq-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) beliq
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.
beliq-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: beliq
3
+ Version: 0.1.0
4
+ Summary: Official beliq SDK: generate, validate, parse, and convert EU-compliant e-invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, drift-checked rules.
5
+ Project-URL: Homepage, https://beliq.eu
6
+ Project-URL: Repository, https://github.com/beliq-eu/beliq-sdk-python
7
+ Project-URL: Issues, https://github.com/beliq-eu/beliq-sdk-python/issues
8
+ Author-email: beliq <hello@beliq.eu>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: beliq,compliance,e-invoice,einvoice,en16931,factur-x,peppol,xrechnung,zugferd
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: httpx>=0.27
17
+ Requires-Dist: pydantic>=2
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy>=1.11; extra == 'dev'
20
+ Requires-Dist: pytest>=8; extra == 'dev'
21
+ Requires-Dist: respx>=0.21; extra == 'dev'
22
+ Requires-Dist: ruff>=0.6; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # beliq
26
+
27
+ Official Python SDK for the [beliq](https://beliq.eu) e-invoicing compliance API. Generate, validate, parse, and convert EN 16931 invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, nightly-drift-checked rules.
28
+
29
+ beliq produces and checks the compliant document. Transmission (Peppol, PDP, KSeF, SDI), archiving, and tax-authority reporting stay with your access point.
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install beliq
35
+ ```
36
+
37
+ Requires Python >= 3.10.
38
+
39
+ ## Quick start
40
+
41
+ ```python
42
+ from beliq import Beliq
43
+
44
+ beliq = Beliq(api_key="blq_...")
45
+
46
+ # Account, plan, and quota context (no quota cost).
47
+ account = beliq.me()
48
+
49
+ # Generate an XRechnung document from an EN 16931 invoice.
50
+ generated = beliq.generate(
51
+ standard="xrechnung",
52
+ verify=True,
53
+ invoice={
54
+ "number": "INV-2026-001",
55
+ "issueDate": "2026-01-15",
56
+ "currencyCode": "EUR",
57
+ "seller": {"name": "Seller GmbH", "address": {"city": "Berlin", "postalCode": "10115", "countryCode": "DE"}},
58
+ "buyer": {"name": "Buyer GmbH", "address": {"city": "Munich", "postalCode": "80331", "countryCode": "DE"}},
59
+ "lines": [
60
+ {"description": "Consulting", "quantity": 10, "unitCode": "HUR", "unitPrice": 100, "lineTotal": 1000, "vatRate": 19, "vatCategoryCode": "S"}
61
+ ],
62
+ "totalNetAmount": 1000,
63
+ "totalTaxAmount": 190,
64
+ "totalGrossAmount": 1190,
65
+ },
66
+ )
67
+ print(generated.xml, generated.meta.schematron_version)
68
+
69
+ # Validate any document against authority-pinned rules.
70
+ result = beliq.validate(generated.xml, format="auto")
71
+ if not result.valid:
72
+ for issue in result.errors:
73
+ print(issue.rule_id, issue.message)
74
+ ```
75
+
76
+ ## Authentication
77
+
78
+ Create an API key in the beliq dashboard under API Keys:
79
+
80
+ ```python
81
+ Beliq(api_key="blq_...") # sends X-API-Key (default)
82
+ Beliq(api_key="blq_...", auth="bearer") # sends Authorization: Bearer
83
+ Beliq(api_key="blq_...", base_url="https://staging.beliq.eu")
84
+ ```
85
+
86
+ ## Async
87
+
88
+ `AsyncBeliq` mirrors the sync client with `await`:
89
+
90
+ ```python
91
+ import asyncio
92
+ from beliq import AsyncBeliq
93
+
94
+ async def main():
95
+ async with AsyncBeliq(api_key="blq_...") as beliq:
96
+ result = await beliq.validate(open("invoice.xml", "rb").read(), format="auto")
97
+ print(result.valid)
98
+
99
+ asyncio.run(main())
100
+ ```
101
+
102
+ ## API
103
+
104
+ | Method | Endpoint | Input | Returns |
105
+ |---|---|---|---|
106
+ | `me()` | GET /v1/me | none | `AccountInfo` (no quota cost) |
107
+ | `generate(...)` | POST /v1/generate | EN 16931 invoice dict | `GenerateResult` |
108
+ | `validate(document, ...)` | POST /v1/validate | XML or PDF | `ValidationResult` |
109
+ | `parse(document, ...)` | POST /v1/parse | XML or PDF | `ParseResult` |
110
+ | `convert(document, ...)` | POST /v1/convert | XML or PDF | `ConvertResult` |
111
+
112
+ `document` accepts a `str`, `bytes`, or `bytearray`. The content type is sniffed from the bytes (PDF vs XML) unless you pass `content_type=`. `generate` and `convert` return the raw document `content` (bytes) plus the response-header metadata (`schematron_version`, `pdf_kind`, `source_format`/`target_format`, `lost_elements`, `conversion_tools`); for an XML output, `generate` also decodes `xml`.
113
+
114
+ JSON responses are Pydantic models. Any field not explicitly typed (such as the per-country authority versions on a validation result) is preserved and accessible. Errors raise `BeliqApiError` with a typed `.code`, HTTP `.status`, and any `.details`:
115
+
116
+ ```python
117
+ from beliq import BeliqApiError
118
+
119
+ try:
120
+ beliq.validate("not xml")
121
+ except BeliqApiError as err:
122
+ print(err.code, err.status, err.message)
123
+ ```
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ python -m venv .venv && . .venv/bin/activate
129
+ pip install -e ".[dev]"
130
+ ruff check src tests
131
+ mypy
132
+ pytest # unit tests (no network)
133
+ BELIQ_API_KEY=blq_xxx pytest tests/test_integration.py # hits the live API; draws quota
134
+ ```
135
+
136
+ `tests/test_spec_contract.py` reads the vendored `openapi.json` and fails if the error-code set or the core validate fields drift from the spec. Refresh the vendored spec with `python scripts/sync_spec.py`.
137
+
138
+ ## Publishing
139
+
140
+ Released to PyPI as [`beliq`](https://pypi.org/project/beliq/). Releases run from `.github/workflows/release.yml` via PyPI Trusted Publishing (OIDC, with attestations): push a `v*.*.*` tag to publish. No token is stored in the repo.
141
+
142
+ ## License
143
+
144
+ MIT
beliq-0.1.0/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # beliq
2
+
3
+ Official Python SDK for the [beliq](https://beliq.eu) e-invoicing compliance API. Generate, validate, parse, and convert EN 16931 invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, nightly-drift-checked rules.
4
+
5
+ beliq produces and checks the compliant document. Transmission (Peppol, PDP, KSeF, SDI), archiving, and tax-authority reporting stay with your access point.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install beliq
11
+ ```
12
+
13
+ Requires Python >= 3.10.
14
+
15
+ ## Quick start
16
+
17
+ ```python
18
+ from beliq import Beliq
19
+
20
+ beliq = Beliq(api_key="blq_...")
21
+
22
+ # Account, plan, and quota context (no quota cost).
23
+ account = beliq.me()
24
+
25
+ # Generate an XRechnung document from an EN 16931 invoice.
26
+ generated = beliq.generate(
27
+ standard="xrechnung",
28
+ verify=True,
29
+ invoice={
30
+ "number": "INV-2026-001",
31
+ "issueDate": "2026-01-15",
32
+ "currencyCode": "EUR",
33
+ "seller": {"name": "Seller GmbH", "address": {"city": "Berlin", "postalCode": "10115", "countryCode": "DE"}},
34
+ "buyer": {"name": "Buyer GmbH", "address": {"city": "Munich", "postalCode": "80331", "countryCode": "DE"}},
35
+ "lines": [
36
+ {"description": "Consulting", "quantity": 10, "unitCode": "HUR", "unitPrice": 100, "lineTotal": 1000, "vatRate": 19, "vatCategoryCode": "S"}
37
+ ],
38
+ "totalNetAmount": 1000,
39
+ "totalTaxAmount": 190,
40
+ "totalGrossAmount": 1190,
41
+ },
42
+ )
43
+ print(generated.xml, generated.meta.schematron_version)
44
+
45
+ # Validate any document against authority-pinned rules.
46
+ result = beliq.validate(generated.xml, format="auto")
47
+ if not result.valid:
48
+ for issue in result.errors:
49
+ print(issue.rule_id, issue.message)
50
+ ```
51
+
52
+ ## Authentication
53
+
54
+ Create an API key in the beliq dashboard under API Keys:
55
+
56
+ ```python
57
+ Beliq(api_key="blq_...") # sends X-API-Key (default)
58
+ Beliq(api_key="blq_...", auth="bearer") # sends Authorization: Bearer
59
+ Beliq(api_key="blq_...", base_url="https://staging.beliq.eu")
60
+ ```
61
+
62
+ ## Async
63
+
64
+ `AsyncBeliq` mirrors the sync client with `await`:
65
+
66
+ ```python
67
+ import asyncio
68
+ from beliq import AsyncBeliq
69
+
70
+ async def main():
71
+ async with AsyncBeliq(api_key="blq_...") as beliq:
72
+ result = await beliq.validate(open("invoice.xml", "rb").read(), format="auto")
73
+ print(result.valid)
74
+
75
+ asyncio.run(main())
76
+ ```
77
+
78
+ ## API
79
+
80
+ | Method | Endpoint | Input | Returns |
81
+ |---|---|---|---|
82
+ | `me()` | GET /v1/me | none | `AccountInfo` (no quota cost) |
83
+ | `generate(...)` | POST /v1/generate | EN 16931 invoice dict | `GenerateResult` |
84
+ | `validate(document, ...)` | POST /v1/validate | XML or PDF | `ValidationResult` |
85
+ | `parse(document, ...)` | POST /v1/parse | XML or PDF | `ParseResult` |
86
+ | `convert(document, ...)` | POST /v1/convert | XML or PDF | `ConvertResult` |
87
+
88
+ `document` accepts a `str`, `bytes`, or `bytearray`. The content type is sniffed from the bytes (PDF vs XML) unless you pass `content_type=`. `generate` and `convert` return the raw document `content` (bytes) plus the response-header metadata (`schematron_version`, `pdf_kind`, `source_format`/`target_format`, `lost_elements`, `conversion_tools`); for an XML output, `generate` also decodes `xml`.
89
+
90
+ JSON responses are Pydantic models. Any field not explicitly typed (such as the per-country authority versions on a validation result) is preserved and accessible. Errors raise `BeliqApiError` with a typed `.code`, HTTP `.status`, and any `.details`:
91
+
92
+ ```python
93
+ from beliq import BeliqApiError
94
+
95
+ try:
96
+ beliq.validate("not xml")
97
+ except BeliqApiError as err:
98
+ print(err.code, err.status, err.message)
99
+ ```
100
+
101
+ ## Development
102
+
103
+ ```bash
104
+ python -m venv .venv && . .venv/bin/activate
105
+ pip install -e ".[dev]"
106
+ ruff check src tests
107
+ mypy
108
+ pytest # unit tests (no network)
109
+ BELIQ_API_KEY=blq_xxx pytest tests/test_integration.py # hits the live API; draws quota
110
+ ```
111
+
112
+ `tests/test_spec_contract.py` reads the vendored `openapi.json` and fails if the error-code set or the core validate fields drift from the spec. Refresh the vendored spec with `python scripts/sync_spec.py`.
113
+
114
+ ## Publishing
115
+
116
+ Released to PyPI as [`beliq`](https://pypi.org/project/beliq/). Releases run from `.github/workflows/release.yml` via PyPI Trusted Publishing (OIDC, with attestations): push a `v*.*.*` tag to publish. No token is stored in the repo.
117
+
118
+ ## License
119
+
120
+ MIT