pennylane-client 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,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: Lint and test
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v4
20
+ with:
21
+ version: "latest"
22
+
23
+ - name: Set up Python
24
+ run: uv python install 3.13
25
+
26
+ - name: Install dependencies
27
+ run: uv sync --dev
28
+
29
+ - name: Ruff format
30
+ run: uv run ruff format --check .
31
+
32
+ - name: Ruff check
33
+ run: uv run ruff check .
34
+
35
+ - name: Tests
36
+ run: uv run pytest
@@ -0,0 +1,52 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ name: Build distribution
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v4
20
+ with:
21
+ version: "latest"
22
+
23
+ - name: Set up Python
24
+ run: uv python install 3.13
25
+
26
+ - name: Build package
27
+ run: uv build
28
+
29
+ - name: Store distribution packages
30
+ uses: actions/upload-artifact@v4
31
+ with:
32
+ name: python-package-distributions
33
+ path: dist/
34
+
35
+ publish:
36
+ name: Publish to PyPI
37
+ needs: build
38
+ runs-on: ubuntu-latest
39
+ environment:
40
+ name: pypi
41
+ url: https://pypi.org/p/pennylane-client
42
+ permissions:
43
+ id-token: write # required for OIDC trusted publishing
44
+ steps:
45
+ - name: Download distributions
46
+ uses: actions/download-artifact@v4
47
+ with:
48
+ name: python-package-distributions
49
+ path: dist/
50
+
51
+ - name: Publish to PyPI
52
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,29 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+
9
+ # Virtual environments
10
+ .venv/
11
+ venv/
12
+ env/
13
+
14
+ # Testing
15
+ .pytest_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # Type checkers
20
+ .mypy_cache/
21
+ .ruff_cache/
22
+
23
+ # Editors
24
+ .idea/
25
+ .vscode/
26
+ *.swp
27
+
28
+ # macOS
29
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lenstra
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,128 @@
1
+ Metadata-Version: 2.5
2
+ Name: pennylane-client
3
+ Version: 0.1.0
4
+ Summary: Async Python client for the Pennylane API
5
+ Project-URL: Homepage, https://github.com/Lenstra/pennylane-client-python
6
+ Project-URL: Repository, https://github.com/Lenstra/pennylane-client-python
7
+ Project-URL: Issues, https://github.com/Lenstra/pennylane-client-python/issues
8
+ Author-email: Lenstra <tech@lenstra.co>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: accounting,api,client,invoicing,pennylane
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.13
20
+ Requires-Dist: httpx>=0.27.0
21
+ Requires-Dist: jsonschema>=4.0.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # pennylane-client
25
+
26
+ Async Python client for the [Pennylane API](https://pennylane.readme.io/)
27
+ (Company API v2).
28
+
29
+ ```bash
30
+ uv add pennylane-client
31
+ ```
32
+
33
+ ## Why it's built this way
34
+
35
+ The client is generated from Pennylane's own published OpenAPI spec, the same
36
+ way [boondmanager-client](https://github.com/Lenstra/boondmanager-client-python)
37
+ is generated from BoondManager's spec:
38
+
39
+ - `pennylane_client/_spec/spec.json.gz`: vendored bundle with the full
40
+ operation registry (175 operations) and every JSON request-body schema,
41
+ extracted from the public OpenAPI file.
42
+ - `pennylane_client/api.py`: generated surface, one method per operation,
43
+ named after the spec's `operationId` (so the names match Pennylane's docs).
44
+ - Every JSON request body is validated against the matching schema **before
45
+ sending**: a mismatch raises `PennylaneValidationError` with the exact
46
+ violations, instead of an opaque 4xx from the API.
47
+
48
+ ## Usage
49
+
50
+ ```python
51
+ from pennylane_client import PennylaneClient
52
+
53
+ async with PennylaneClient(api_token) as client:
54
+ # One page of a list operation: {"items", "has_more", "next_cursor"}
55
+ page = await client.api.customer_invoices.get_customer_invoices(limit=100)
56
+
57
+ # A single object
58
+ invoice = await client.api.customer_invoices.get_customer_invoice(42)
59
+
60
+ # Every item of a cursor-paginated list, across pages
61
+ async for invoice in client.paginate(
62
+ client.api.customer_invoices.get_customer_invoices,
63
+ filter=[{"field": "date", "operator": "gteq", "value": "2026-01-01"}],
64
+ ):
65
+ print(invoice["invoice_number"])
66
+
67
+ # Or all at once
68
+ customers = await client.fetch_all(client.api.customers.get_customers)
69
+
70
+ # Writes are validated against the spec before sending
71
+ await client.api.customers.post_company_customer(
72
+ {"name": "Acme", "billing_address": {...}}
73
+ )
74
+ ```
75
+
76
+ - `client.api.<group>.<method>(...)`: groups are the spec's tags
77
+ (`customer_invoices`, `customers`, `supplier_invoices`, `transactions`, ...).
78
+ Path parameters are positional, query parameters are keyword-only, and every
79
+ method returns the decoded JSON.
80
+ - `filter` accepts a list of dicts, JSON-encoded for you.
81
+ - Find the right method by grepping [`docs/API.md`](docs/API.md) or with
82
+ `pennylane_client.ENDPOINT_INDEX`; each method's docstring lists its query
83
+ parameters and request body.
84
+
85
+ ### Client options
86
+
87
+ - `max_concurrent_requests` (default 5) caps the requests in flight, or pass
88
+ `semaphore` to share one limit across clients.
89
+ - `max_retries` (default 3): HTTP 429 (rate limit) responses are retried,
90
+ waiting for the `Retry-After` header.
91
+ - `validate=False` on a write method skips the schema check (escape hatch
92
+ for a payload the published spec does not describe correctly).
93
+
94
+ ### Errors
95
+
96
+ - `PennylaneValidationError`: the request body does not match the schema
97
+ (raised before sending).
98
+ - `PennylaneAPIError`: the API returned an HTTP error (`status_code`,
99
+ `endpoint`, `detail`).
100
+ - `PennylaneError`: base class, also raised on network errors.
101
+
102
+ ## Development
103
+
104
+ ```bash
105
+ uv sync --dev
106
+ uv run ruff format --check . && uv run ruff check .
107
+ uv run pytest
108
+ ```
109
+
110
+ ### Updating the spec
111
+
112
+ ```bash
113
+ uv run python scripts/fetch_spec.py # download and vendor the OpenAPI file
114
+ uv run python scripts/generate_api.py # regenerate api.py and docs/API.md
115
+ ```
116
+
117
+ Review the diff of `docs/API.md` to see what changed in the API.
118
+
119
+ ### Releasing
120
+
121
+ Bump `version` in `pyproject.toml`, merge, then push a tag:
122
+
123
+ ```bash
124
+ git tag v0.1.0 && git push origin v0.1.0
125
+ ```
126
+
127
+ The `publish.yml` workflow builds the package and publishes it to PyPI with
128
+ trusted publishing (no token needed).
@@ -0,0 +1,105 @@
1
+ # pennylane-client
2
+
3
+ Async Python client for the [Pennylane API](https://pennylane.readme.io/)
4
+ (Company API v2).
5
+
6
+ ```bash
7
+ uv add pennylane-client
8
+ ```
9
+
10
+ ## Why it's built this way
11
+
12
+ The client is generated from Pennylane's own published OpenAPI spec, the same
13
+ way [boondmanager-client](https://github.com/Lenstra/boondmanager-client-python)
14
+ is generated from BoondManager's spec:
15
+
16
+ - `pennylane_client/_spec/spec.json.gz`: vendored bundle with the full
17
+ operation registry (175 operations) and every JSON request-body schema,
18
+ extracted from the public OpenAPI file.
19
+ - `pennylane_client/api.py`: generated surface, one method per operation,
20
+ named after the spec's `operationId` (so the names match Pennylane's docs).
21
+ - Every JSON request body is validated against the matching schema **before
22
+ sending**: a mismatch raises `PennylaneValidationError` with the exact
23
+ violations, instead of an opaque 4xx from the API.
24
+
25
+ ## Usage
26
+
27
+ ```python
28
+ from pennylane_client import PennylaneClient
29
+
30
+ async with PennylaneClient(api_token) as client:
31
+ # One page of a list operation: {"items", "has_more", "next_cursor"}
32
+ page = await client.api.customer_invoices.get_customer_invoices(limit=100)
33
+
34
+ # A single object
35
+ invoice = await client.api.customer_invoices.get_customer_invoice(42)
36
+
37
+ # Every item of a cursor-paginated list, across pages
38
+ async for invoice in client.paginate(
39
+ client.api.customer_invoices.get_customer_invoices,
40
+ filter=[{"field": "date", "operator": "gteq", "value": "2026-01-01"}],
41
+ ):
42
+ print(invoice["invoice_number"])
43
+
44
+ # Or all at once
45
+ customers = await client.fetch_all(client.api.customers.get_customers)
46
+
47
+ # Writes are validated against the spec before sending
48
+ await client.api.customers.post_company_customer(
49
+ {"name": "Acme", "billing_address": {...}}
50
+ )
51
+ ```
52
+
53
+ - `client.api.<group>.<method>(...)`: groups are the spec's tags
54
+ (`customer_invoices`, `customers`, `supplier_invoices`, `transactions`, ...).
55
+ Path parameters are positional, query parameters are keyword-only, and every
56
+ method returns the decoded JSON.
57
+ - `filter` accepts a list of dicts, JSON-encoded for you.
58
+ - Find the right method by grepping [`docs/API.md`](docs/API.md) or with
59
+ `pennylane_client.ENDPOINT_INDEX`; each method's docstring lists its query
60
+ parameters and request body.
61
+
62
+ ### Client options
63
+
64
+ - `max_concurrent_requests` (default 5) caps the requests in flight, or pass
65
+ `semaphore` to share one limit across clients.
66
+ - `max_retries` (default 3): HTTP 429 (rate limit) responses are retried,
67
+ waiting for the `Retry-After` header.
68
+ - `validate=False` on a write method skips the schema check (escape hatch
69
+ for a payload the published spec does not describe correctly).
70
+
71
+ ### Errors
72
+
73
+ - `PennylaneValidationError`: the request body does not match the schema
74
+ (raised before sending).
75
+ - `PennylaneAPIError`: the API returned an HTTP error (`status_code`,
76
+ `endpoint`, `detail`).
77
+ - `PennylaneError`: base class, also raised on network errors.
78
+
79
+ ## Development
80
+
81
+ ```bash
82
+ uv sync --dev
83
+ uv run ruff format --check . && uv run ruff check .
84
+ uv run pytest
85
+ ```
86
+
87
+ ### Updating the spec
88
+
89
+ ```bash
90
+ uv run python scripts/fetch_spec.py # download and vendor the OpenAPI file
91
+ uv run python scripts/generate_api.py # regenerate api.py and docs/API.md
92
+ ```
93
+
94
+ Review the diff of `docs/API.md` to see what changed in the API.
95
+
96
+ ### Releasing
97
+
98
+ Bump `version` in `pyproject.toml`, merge, then push a tag:
99
+
100
+ ```bash
101
+ git tag v0.1.0 && git push origin v0.1.0
102
+ ```
103
+
104
+ The `publish.yml` workflow builds the package and publishes it to PyPI with
105
+ trusted publishing (no token needed).
@@ -0,0 +1,279 @@
1
+ # Pennylane client: API index (generated, do not edit)
2
+
3
+ One line per method of `client.api`. Grep this file to find the right call,
4
+ then read its docstring in `pennylane_client/api.py` for the query parameters
5
+ and the request body.
6
+
7
+ Conventions:
8
+
9
+ - `client.api.<group>.<method>(...)`: all methods are async and return the
10
+ decoded JSON. Method names are the spec's operationIds in snake_case.
11
+ - Path parameters are positional, query parameters are keyword-only.
12
+ - `[paginated]` methods return `{"items", "has_more", "next_cursor"}`: pass
13
+ them to `client.paginate(...)` or `client.fetch_all(...)` to walk all pages.
14
+
15
+
16
+ ## client.api.bank_accounts
17
+
18
+ - `get_bank_accounts()` [paginated], GET /api/external/v2/bank_accounts: List bank accounts
19
+ - `post_bank_account(body)`, POST /api/external/v2/bank_accounts: Create a bank account
20
+ - `get_bank_account(id)`, GET /api/external/v2/bank_accounts/{id}: Retrieve a bank account
21
+
22
+ ## client.api.bank_establishments
23
+
24
+ - `get_bank_establishments()` [paginated], GET /api/external/v2/bank_establishments: List bank establishments
25
+
26
+ ## client.api.billing_subscriptions
27
+
28
+ - `get_billing_subscriptions()` [paginated], GET /api/external/v2/billing_subscriptions: List billing subscriptions
29
+ - `post_billing_subscriptions(body)`, POST /api/external/v2/billing_subscriptions: Create a billing subscription
30
+ - `get_billing_subscription_invoice_line_sections(billing_subscription_id)` [paginated], GET /api/external/v2/billing_subscriptions/{billing_subscription_id}/invoice_line_sections: List the invoice line sections of a billing subscription
31
+ - `get_billing_subscription_invoice_lines(billing_subscription_id)` [paginated], GET /api/external/v2/billing_subscriptions/{billing_subscription_id}/invoice_lines: List invoice lines for a billing subscription
32
+ - `get_billing_subscription(id)`, GET /api/external/v2/billing_subscriptions/{id}: Get a billing subscription
33
+ - `put_billing_subscriptions(id, body)`, PUT /api/external/v2/billing_subscriptions/{id}: Update a billing subscription
34
+
35
+ ## client.api.categories
36
+
37
+ - `get_categories()` [paginated], GET /api/external/v2/categories: List categories
38
+ - `post_categories(body)`, POST /api/external/v2/categories: Create a category
39
+ - `get_category(id)`, GET /api/external/v2/categories/{id}: Retrieve a category
40
+ - `update_category(id, body)`, PUT /api/external/v2/categories/{id}: Update a category
41
+ - `get_category_groups()` [paginated], GET /api/external/v2/category_groups: List category groups
42
+
43
+ ## client.api.category_groups
44
+
45
+ - `post_category_groups(body)`, POST /api/external/v2/category_groups: Create a category group
46
+ - `get_category_group_categories(category_group_id)` [paginated], GET /api/external/v2/category_groups/{category_group_id}/categories: List categories of a category group
47
+ - `get_category_group(id)`, GET /api/external/v2/category_groups/{id}: Retrieve a category group
48
+ - `put_category_group(id, body)`, PUT /api/external/v2/category_groups/{id}: Update a category group
49
+
50
+ ## client.api.changelogs
51
+
52
+ - `get_customer_invoices_changes()` [paginated], GET /api/external/v2/changelogs/customer_invoices: Get customer invoices changes events
53
+ - `get_customer_changes()` [paginated], GET /api/external/v2/changelogs/customers: Get customer changes events
54
+ - `get_ledger_entries_category_changes()` [paginated], GET /api/external/v2/changelogs/ledger_entries_categories: Get ledger entry category change events
55
+ - `get_ledger_entry_line_changes()` [paginated], GET /api/external/v2/changelogs/ledger_entry_lines: Get ledger entry line change events
56
+ - `get_ledger_entry_lines_category_changes()` [paginated], GET /api/external/v2/changelogs/ledger_entry_lines_categories: Get ledger entry line category change events
57
+ - `get_product_changes()` [paginated], GET /api/external/v2/changelogs/products: Get product change events
58
+ - `get_supplier_invoices_changes()` [paginated], GET /api/external/v2/changelogs/supplier_invoices: Get supplier invoices changes events
59
+ - `get_supplier_changes()` [paginated], GET /api/external/v2/changelogs/suppliers: Get supplier changes events
60
+ - `get_transaction_changes()` [paginated], GET /api/external/v2/changelogs/transactions: Get transaction change events
61
+
62
+ ## client.api.commercial_documents
63
+
64
+ - `list_commercial_documents()` [paginated], GET /api/external/v2/commercial_documents: List commercial documents
65
+ - `post_commercial_documents(body)`, POST /api/external/v2/commercial_documents: Create a commercial document
66
+ - `get_commercial_document_appendices(commercial_document_id)` [paginated], GET /api/external/v2/commercial_documents/{commercial_document_id}/appendices: List appendices of a commercial document
67
+ - `post_commercial_document_appendices(commercial_document_id, files)`, POST /api/external/v2/commercial_documents/{commercial_document_id}/appendices: Upload an appendix for a commercial document
68
+ - `get_commercial_document_invoice_line_sections(commercial_document_id)` [paginated], GET /api/external/v2/commercial_documents/{commercial_document_id}/invoice_line_sections: List invoice line sections for a commercial document
69
+ - `get_commercial_document_invoice_lines(commercial_document_id)` [paginated], GET /api/external/v2/commercial_documents/{commercial_document_id}/invoice_lines: List invoice lines for a commercial document
70
+ - `get_commercial_document(id)`, GET /api/external/v2/commercial_documents/{id}: Retrieve a commercial document
71
+ - `update_commercial_document(id, body)`, PUT /api/external/v2/commercial_documents/{id}: Update a commercial document
72
+
73
+ ## client.api.customer_invoice_templates
74
+
75
+ - `get_customer_invoice_templates()` [paginated], GET /api/external/v2/customer_invoice_templates: List customer invoice templates
76
+
77
+ ## client.api.customer_invoices
78
+
79
+ - `get_customer_invoices()` [paginated], GET /api/external/v2/customer_invoices: List customer invoices
80
+ - `post_customer_invoices(body)`, POST /api/external/v2/customer_invoices: Create a customer invoice
81
+ - `create_customer_invoice_from_quote(body)`, POST /api/external/v2/customer_invoices/create_from_quote: Create a customer invoice from a quote
82
+ - `create_customer_invoice_einvoice_import(files)`, POST /api/external/v2/customer_invoices/e_invoices/imports: Import a customer e-invoice
83
+ - `import_customer_invoices(body)`, POST /api/external/v2/customer_invoices/import: Import an invoice with file attached
84
+ - `get_customer_invoice_appendices(customer_invoice_id)` [paginated], GET /api/external/v2/customer_invoices/{customer_invoice_id}/appendices: List appendices of a customer invoice
85
+ - `post_customer_invoice_appendices(customer_invoice_id, files)`, POST /api/external/v2/customer_invoices/{customer_invoice_id}/appendices: Upload an appendix for a customer invoice
86
+ - `get_customer_invoice_categories(customer_invoice_id)` [paginated], GET /api/external/v2/customer_invoices/{customer_invoice_id}/categories: List categories of a customer invoice
87
+ - `put_customer_invoice_categories(customer_invoice_id, body)`, PUT /api/external/v2/customer_invoices/{customer_invoice_id}/categories: Categorize a customer invoice
88
+ - `get_customer_invoice_custom_header_fields(customer_invoice_id)` [paginated], GET /api/external/v2/customer_invoices/{customer_invoice_id}/custom_header_fields: List custom header fields for a customer invoice
89
+ - `get_customer_invoice_invoice_line_sections(customer_invoice_id)` [paginated], GET /api/external/v2/customer_invoices/{customer_invoice_id}/invoice_line_sections: List invoice line sections for a customer invoice
90
+ - `get_customer_invoice_invoice_lines(customer_invoice_id)` [paginated], GET /api/external/v2/customer_invoices/{customer_invoice_id}/invoice_lines: List invoice lines for a customer invoice
91
+ - `get_customer_invoice_matched_transactions(customer_invoice_id)` [paginated], GET /api/external/v2/customer_invoices/{customer_invoice_id}/matched_transactions: List matched transactions for a customer invoice
92
+ - `get_customer_invoice_payments(customer_invoice_id)` [paginated], GET /api/external/v2/customer_invoices/{customer_invoice_id}/payments: List payments for a customer invoice
93
+ - `delete_customer_invoices(id)`, DELETE /api/external/v2/customer_invoices/{id}: Delete draft invoice
94
+ - `get_customer_invoice(id)`, GET /api/external/v2/customer_invoices/{id}: Retrieve a customer invoice
95
+ - `update_customer_invoice(id, body)`, PUT /api/external/v2/customer_invoices/{id}: Update a customer invoice
96
+ - `finalize_customer_invoice(id)`, PUT /api/external/v2/customer_invoices/{id}/finalize: Turn the draft invoice into a finalized invoice.
97
+ - `link_credit_note(id, body)`, POST /api/external/v2/customer_invoices/{id}/link_credit_note: Link a credit note to a customer invoice
98
+ - `mark_as_paid_customer_invoice(id)`, PUT /api/external/v2/customer_invoices/{id}/mark_as_paid: Mark a customer invoice as paid
99
+ - `send_by_email_customer_invoice(id, body)`, POST /api/external/v2/customer_invoices/{id}/send_by_email: Send a customer invoice by email
100
+ - `send_to_pa_customer_invoice(id)`, POST /api/external/v2/customer_invoices/{id}/send_to_pa: Send a customer e-invoice to PA
101
+ - `update_imported_customer_invoice(id, body)`, PUT /api/external/v2/customer_invoices/{id}/update_imported: Update an Imported customer invoice
102
+
103
+ ## client.api.customers
104
+
105
+ - `post_company_customer(body)`, POST /api/external/v2/company_customers: Create a company customer
106
+ - `get_company_customer(id)`, GET /api/external/v2/company_customers/{id}: Retrieve a company customer
107
+ - `put_company_customer(id, body)`, PUT /api/external/v2/company_customers/{id}: Update a company customer
108
+ - `get_customers()` [paginated], GET /api/external/v2/customers: List customers (company and individual)
109
+ - `get_customer_categories(customer_id)` [paginated], GET /api/external/v2/customers/{customer_id}/categories: List categories of a customer
110
+ - `put_customer_categories(customer_id, body)`, PUT /api/external/v2/customers/{customer_id}/categories: Categorize a customer
111
+ - `get_customer_contacts(customer_id)` [paginated], GET /api/external/v2/customers/{customer_id}/contacts: List contacts of a customer
112
+ - `post_customer_contact(customer_id, body)`, POST /api/external/v2/customers/{customer_id}/contacts: Create a contact of a customer
113
+ - `delete_customer_contact(customer_id, id)`, DELETE /api/external/v2/customers/{customer_id}/contacts/{id}: Delete a contact of a customer
114
+ - `get_customer_contact(customer_id, id)`, GET /api/external/v2/customers/{customer_id}/contacts/{id}: Retrieve a contact of a customer
115
+ - `put_customer_contact(customer_id, id, body)`, PUT /api/external/v2/customers/{customer_id}/contacts/{id}: Update a contact of a customer
116
+ - `get_customer(id)`, GET /api/external/v2/customers/{id}: Retrieve a customer
117
+ - `post_individual_customer(body)`, POST /api/external/v2/individual_customers: Create an individual customer
118
+ - `get_individual_customer(id)`, GET /api/external/v2/individual_customers/{id}: Retrieve an individual customer
119
+ - `put_individual_customer(id, body)`, PUT /api/external/v2/individual_customers/{id}: Update an individual customer
120
+
121
+ ## client.api.exports
122
+
123
+ - `export_analytical_general_ledger(body)`, POST /api/external/v2/exports/analytical_general_ledgers: Create an Analytical General Ledger export
124
+ - `get_analytical_general_ledger_export(id)`, GET /api/external/v2/exports/analytical_general_ledgers/{id}: Retrieve an Analytical General Ledger export
125
+ - `export_fec(body)`, POST /api/external/v2/exports/fecs: Create a FEC export
126
+ - `get_fec_export(id)`, GET /api/external/v2/exports/fecs/{id}: Retrieve a FEC export
127
+ - `export_general_ledger(body)`, POST /api/external/v2/exports/general_ledgers: Create a General Ledger export
128
+ - `get_general_ledger_export(id)`, GET /api/external/v2/exports/general_ledgers/{id}: Retrieve a General Ledger export
129
+
130
+ ## client.api.file_attachments
131
+
132
+ - `post_file_attachments(files)`, POST /api/external/v2/file_attachments: Upload a file
133
+
134
+ ## client.api.fiscal_years
135
+
136
+ - `company_fiscal_years()` [paginated], GET /api/external/v2/fiscal_years: List Company's Fiscal Years
137
+
138
+ ## client.api.journals
139
+
140
+ - `get_journals()` [paginated], GET /api/external/v2/journals: List journals
141
+ - `post_journals(body)`, POST /api/external/v2/journals: Create a journal
142
+ - `get_journal(id)`, GET /api/external/v2/journals/{id}: Retrieve a journal
143
+
144
+ ## client.api.ledger_accounts
145
+
146
+ - `get_ledger_accounts()` [paginated], GET /api/external/v2/ledger_accounts: List Ledger Accounts
147
+ - `post_ledger_accounts(body)`, POST /api/external/v2/ledger_accounts: Create a ledger account
148
+ - `get_ledger_account(id)`, GET /api/external/v2/ledger_accounts/{id}: Get a ledger account
149
+ - `update_ledger_account(id, body)`, PUT /api/external/v2/ledger_accounts/{id}: Update a ledger account
150
+
151
+ ## client.api.ledger_attachments
152
+
153
+ - `post_ledger_attachments(files)`, POST /api/external/v2/ledger_attachments: Upload a file
154
+
155
+ ## client.api.ledger_entries
156
+
157
+ - `get_ledger_entries()` [paginated], GET /api/external/v2/ledger_entries: List Ledger Entries
158
+ - `post_ledger_entries(body)`, POST /api/external/v2/ledger_entries: Create a ledger entry
159
+ - `get_ledger_entry(id)`, GET /api/external/v2/ledger_entries/{id}: Retrieve a Ledger entry
160
+ - `put_ledger_entries(id, body)`, PUT /api/external/v2/ledger_entries/{id}: Update a ledger entry
161
+ - `get_ledger_entries_ledger_entry_lines(ledger_entry_id)` [paginated], GET /api/external/v2/ledger_entries/{ledger_entry_id}/ledger_entry_lines: List ledger entry lines of a Ledger Entry
162
+
163
+ ## client.api.ledger_entry_lines
164
+
165
+ - `get_ledger_entry_lines()` [paginated], GET /api/external/v2/ledger_entry_lines: List ledger entry lines
166
+ - `delete_ledger_entry_lines_unletter(body)`, DELETE /api/external/v2/ledger_entry_lines/lettering: Unletter ledger entry lines
167
+ - `post_ledger_entry_lines_letter(body)`, POST /api/external/v2/ledger_entry_lines/lettering: Letter ledger entry lines
168
+ - `get_ledger_entry_line(id)`, GET /api/external/v2/ledger_entry_lines/{id}: Retrieve a Ledger entry line
169
+ - `get_ledger_entry_lines_categories(ledger_entry_line_id)` [paginated], GET /api/external/v2/ledger_entry_lines/{ledger_entry_line_id}/categories: List categories of a Ledger Entry line
170
+ - `put_ledger_entry_lines_categories(ledger_entry_line_id, body)`, PUT /api/external/v2/ledger_entry_lines/{ledger_entry_line_id}/categories: Link Analytical Categories to a Ledger Entry line
171
+ - `get_ledger_entry_lines_lettered_ledger_entry_lines(ledger_entry_line_id)` [paginated], GET /api/external/v2/ledger_entry_lines/{ledger_entry_line_id}/lettered_ledger_entry_lines: List ledger entry lines lettered to a given ledger entry line
172
+
173
+ ## client.api.mandates
174
+
175
+ - `get_gocardless_mandates()` [paginated], GET /api/external/v2/gocardless_mandates: List gocardless mandates
176
+ - `post_gocardless_mandate_mail_requests(body)`, POST /api/external/v2/gocardless_mandates/mail_requests: Send a GoCardless mandate email request
177
+ - `post_gocardless_mandate_associations(gocardless_mandate_id, body)`, POST /api/external/v2/gocardless_mandates/{gocardless_mandate_id}/associations: Associate a GoCardless mandate to a customer
178
+ - `post_gocardless_mandate_cancellations(gocardless_mandate_id)`, POST /api/external/v2/gocardless_mandates/{gocardless_mandate_id}/cancellations: Cancel a Gocardless mandate
179
+ - `get_gocardless_mandate(id)`, GET /api/external/v2/gocardless_mandates/{id}: Get a Gocardless mandate
180
+ - `get_pro_account_mandate_migrations()` [paginated], GET /api/external/v2/pro_account/mandate_migrations: List mandate migration candidates
181
+ - `post_pro_account_mandate_migrations(body)`, POST /api/external/v2/pro_account/mandate_migrations: Migrate a mandate to Pro Account
182
+ - `post_pro_account_mandate_mail_requests(body)`, POST /api/external/v2/pro_account/mandate_requests: Send a Pro Account SEPA mandate request
183
+ - `get_pro_account_mandates()` [paginated], GET /api/external/v2/pro_account/mandates: List Pro Account payment mandates
184
+ - `get_sepa_mandates()` [paginated], GET /api/external/v2/sepa_mandates: List SEPA mandates
185
+ - `post_sepa_mandates(body)`, POST /api/external/v2/sepa_mandates: Create a SEPA mandate
186
+ - `delete_sepa_mandate(id)`, DELETE /api/external/v2/sepa_mandates/{id}: Delete a SEPA mandate
187
+ - `get_sepa_mandate(id)`, GET /api/external/v2/sepa_mandates/{id}: Get a SEPA mandate
188
+ - `put_sepa_mandate(id, body)`, PUT /api/external/v2/sepa_mandates/{id}: Update a SEPA mandate
189
+
190
+ ## client.api.numberings
191
+
192
+ - `get_numberings()` [paginated], GET /api/external/v2/numberings: List document numberings
193
+
194
+ ## client.api.pa_registrations
195
+
196
+ - `get_pa_registrations()`, GET /api/external/v2/pa_registrations: List PA Registrations
197
+
198
+ ## client.api.products
199
+
200
+ - `get_products()` [paginated], GET /api/external/v2/products: List products
201
+ - `post_products(body)`, POST /api/external/v2/products: Create a product
202
+ - `get_product(id)`, GET /api/external/v2/products/{id}: Retrieve a product
203
+ - `put_product(id, body)`, PUT /api/external/v2/products/{id}: Update a product
204
+
205
+ ## client.api.purchase_requests
206
+
207
+ - `get_purchase_requests()` [paginated], GET /api/external/v2/purchase_requests: List purchase requests
208
+ - `create_purchase_request_import(body)`, POST /api/external/v2/purchase_requests/imports: Import a purchase order.
209
+ - `get_purchase_request(id)`, GET /api/external/v2/purchase_requests/{id}: Retrieve a purchase request
210
+
211
+ ## client.api.quotes
212
+
213
+ - `get_quote_changes()` [paginated], GET /api/external/v2/changelogs/quotes: Get quotes changes events
214
+ - `list_quotes()` [paginated], GET /api/external/v2/quotes: List quotes
215
+ - `post_quotes(body)`, POST /api/external/v2/quotes: Create a quote
216
+ - `get_quote(id)`, GET /api/external/v2/quotes/{id}: Retrieve a quote
217
+ - `update_quote(id, body)`, PUT /api/external/v2/quotes/{id}: Update a quote
218
+ - `send_by_email_quote(id, body)`, POST /api/external/v2/quotes/{id}/send_by_email: Send a quote by email
219
+ - `update_status_quote(id, body)`, PUT /api/external/v2/quotes/{id}/update_status: Update status of a quote
220
+ - `get_quote_appendices(quote_id)` [paginated], GET /api/external/v2/quotes/{quote_id}/appendices: List appendices of a quote
221
+ - `post_quote_appendices(quote_id, files)`, POST /api/external/v2/quotes/{quote_id}/appendices: Upload an appendix for a quote
222
+ - `get_quote_invoice_line_sections(quote_id)` [paginated], GET /api/external/v2/quotes/{quote_id}/invoice_line_sections: List invoice line sections for a quote
223
+ - `get_quote_invoice_lines(quote_id)` [paginated], GET /api/external/v2/quotes/{quote_id}/invoice_lines: List invoice lines for a quote
224
+
225
+ ## client.api.supplier_invoices
226
+
227
+ - `get_supplier_invoices()` [paginated], GET /api/external/v2/supplier_invoices: List supplier invoices
228
+ - `create_supplier_invoice_einvoice_import(files)`, POST /api/external/v2/supplier_invoices/e_invoices/imports: Import a supplier e-invoice
229
+ - `import_supplier_invoice(body)`, POST /api/external/v2/supplier_invoices/import: Import a supplier invoice with a file attached
230
+ - `get_supplier_invoice(id)`, GET /api/external/v2/supplier_invoices/{id}: Retrieve a supplier invoice
231
+ - `put_supplier_invoice(id, body)`, PUT /api/external/v2/supplier_invoices/{id}: Update a supplier invoice
232
+ - `validate_accounting_supplier_invoice(id)`, PUT /api/external/v2/supplier_invoices/{id}/validate_accounting: Validate the accounting of a supplier invoice
233
+ - `get_supplier_invoice_categories(supplier_invoice_id)` [paginated], GET /api/external/v2/supplier_invoices/{supplier_invoice_id}/categories: List categories of a supplier invoice
234
+ - `put_supplier_invoice_categories(supplier_invoice_id, body)`, PUT /api/external/v2/supplier_invoices/{supplier_invoice_id}/categories: Categorize a supplier invoice
235
+ - `put_supplier_invoice_einvoice_status(supplier_invoice_id, body)`, PUT /api/external/v2/supplier_invoices/{supplier_invoice_id}/e_invoice_status: Update e-invoice status for a supplier invoice
236
+ - `get_supplier_invoice_lines(supplier_invoice_id)` [paginated], GET /api/external/v2/supplier_invoices/{supplier_invoice_id}/invoice_lines: List invoice lines for a supplier invoice
237
+ - `post_supplier_invoice_linked_purchase_requests(supplier_invoice_id, body)`, POST /api/external/v2/supplier_invoices/{supplier_invoice_id}/linked_purchase_requests: Link a purchase request to a supplier invoice
238
+ - `get_supplier_invoice_matched_transactions(supplier_invoice_id)` [paginated], GET /api/external/v2/supplier_invoices/{supplier_invoice_id}/matched_transactions: List matched transactions for a supplier invoice
239
+ - `update_supplier_invoice_payment_status(supplier_invoice_id, body)`, PUT /api/external/v2/supplier_invoices/{supplier_invoice_id}/payment_status: Update a supplier invoice payment status
240
+ - `get_supplier_invoice_payments(supplier_invoice_id)` [paginated], GET /api/external/v2/supplier_invoices/{supplier_invoice_id}/payments: List payments for a supplier invoice
241
+
242
+ ## client.api.suppliers
243
+
244
+ - `get_suppliers()` [paginated], GET /api/external/v2/suppliers: List suppliers
245
+ - `post_supplier(body)`, POST /api/external/v2/suppliers: Create a Supplier
246
+ - `get_supplier(id)`, GET /api/external/v2/suppliers/{id}: Retrieve a supplier
247
+ - `put_supplier(id, body)`, PUT /api/external/v2/suppliers/{id}: Update a supplier
248
+ - `get_supplier_categories(supplier_id)` [paginated], GET /api/external/v2/suppliers/{supplier_id}/categories: List categories of a supplier
249
+ - `put_supplier_categories(supplier_id, body)`, PUT /api/external/v2/suppliers/{supplier_id}/categories: Categorize a supplier
250
+
251
+ ## client.api.transactions
252
+
253
+ - `post_customer_invoice_matched_transactions(customer_invoice_id, body)`, POST /api/external/v2/customer_invoices/{customer_invoice_id}/matched_transactions: Match a transaction to a customer invoice
254
+ - `delete_customer_invoice_matched_transactions(customer_invoice_id, id)`, DELETE /api/external/v2/customer_invoices/{customer_invoice_id}/matched_transactions/{id}: Unmatch a transaction to a customer invoice
255
+ - `post_supplier_invoice_matched_transactions(supplier_invoice_id, body)`, POST /api/external/v2/supplier_invoices/{supplier_invoice_id}/matched_transactions: Match a transaction to a supplier invoice
256
+ - `delete_supplier_invoice_matched_transactions(supplier_invoice_id, id)`, DELETE /api/external/v2/supplier_invoices/{supplier_invoice_id}/matched_transactions/{id}: Unmatch a transaction to a supplier invoice
257
+ - `get_transactions()` [paginated], GET /api/external/v2/transactions: List transactions
258
+ - `create_transaction(body)`, POST /api/external/v2/transactions: Create a transaction
259
+ - `get_transaction(id)`, GET /api/external/v2/transactions/{id}: Retrieve a transaction
260
+ - `update_transaction(id, body)`, PUT /api/external/v2/transactions/{id}: Update a transaction
261
+ - `get_transaction_categories(transaction_id)` [paginated], GET /api/external/v2/transactions/{transaction_id}/categories: List categories of a bank transaction
262
+ - `put_transaction_categories(transaction_id, body)`, PUT /api/external/v2/transactions/{transaction_id}/categories: Categorize a bank transaction
263
+ - `get_transaction_matched_invoices(transaction_id)` [paginated], GET /api/external/v2/transactions/{transaction_id}/matched_invoices: List invoices matched to a bank transaction
264
+
265
+ ## client.api.trial_balance
266
+
267
+ - `get_trial_balance(period_start=..., period_end=...)` [paginated], GET /api/external/v2/trial_balance: Get the trial balance
268
+
269
+ ## client.api.users
270
+
271
+ - `get_me()`, GET /api/external/v2/me: User Profile
272
+
273
+ ## client.api.webhooks
274
+
275
+ - `get_webhook_subscriptions()` [paginated], GET /api/external/v2/webhook_subscriptions: List webhook subscriptions
276
+ - `post_webhook_subscriptions(body)`, POST /api/external/v2/webhook_subscriptions: Create a webhook subscription
277
+ - `delete_webhook_subscription(id)`, DELETE /api/external/v2/webhook_subscriptions/{id}: Delete a webhook subscription
278
+ - `get_webhook_subscription(id)`, GET /api/external/v2/webhook_subscriptions/{id}: Get a webhook subscription
279
+ - `put_webhook_subscription(id, body)`, PUT /api/external/v2/webhook_subscriptions/{id}: Update a webhook subscription