reconify-python 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 (33) hide show
  1. reconify_python-0.1.0/.bumpversion.cfg +14 -0
  2. reconify_python-0.1.0/.github/workflows/ci.yml +22 -0
  3. reconify_python-0.1.0/.github/workflows/release.yml +21 -0
  4. reconify_python-0.1.0/.gitignore +9 -0
  5. reconify_python-0.1.0/CHANGELOG.md +8 -0
  6. reconify_python-0.1.0/LICENSE +21 -0
  7. reconify_python-0.1.0/PKG-INFO +125 -0
  8. reconify_python-0.1.0/README.md +107 -0
  9. reconify_python-0.1.0/pyproject.toml +51 -0
  10. reconify_python-0.1.0/reports/api-error-data-report.md +37 -0
  11. reconify_python-0.1.0/src/reconify/__init__.py +36 -0
  12. reconify_python-0.1.0/src/reconify/client.py +264 -0
  13. reconify_python-0.1.0/src/reconify/errors.py +132 -0
  14. reconify_python-0.1.0/src/reconify/models.py +1067 -0
  15. reconify_python-0.1.0/src/reconify/pagination.py +111 -0
  16. reconify_python-0.1.0/src/reconify/py.typed +0 -0
  17. reconify_python-0.1.0/src/reconify/resources/__init__.py +124 -0
  18. reconify_python-0.1.0/src/reconify/resources/alerts.py +58 -0
  19. reconify_python-0.1.0/src/reconify/resources/base.py +96 -0
  20. reconify_python-0.1.0/src/reconify/resources/events.py +79 -0
  21. reconify_python-0.1.0/src/reconify/resources/ingestion.py +94 -0
  22. reconify_python-0.1.0/src/reconify/resources/issues.py +207 -0
  23. reconify_python-0.1.0/src/reconify/resources/ledger.py +205 -0
  24. reconify_python-0.1.0/src/reconify/resources/reconciliations.py +236 -0
  25. reconify_python-0.1.0/src/reconify/resources/search.py +37 -0
  26. reconify_python-0.1.0/src/reconify/resources/setup.py +311 -0
  27. reconify_python-0.1.0/src/reconify/resources/transactions.py +58 -0
  28. reconify_python-0.1.0/src/reconify/resources/wallets.py +79 -0
  29. reconify_python-0.1.0/src/reconify/transport.py +299 -0
  30. reconify_python-0.1.0/tests/test_client.py +204 -0
  31. reconify_python-0.1.0/tests/test_openapi_coverage.py +88 -0
  32. reconify_python-0.1.0/tests/test_pagination.py +80 -0
  33. reconify_python-0.1.0/uv.lock +707 -0
@@ -0,0 +1,14 @@
1
+ [bumpversion]
2
+ current_version = 0.1.0
3
+ commit = True
4
+ tag = True
5
+ tag_name = v{new_version}
6
+ message = release: bump version {current_version} -> {new_version}
7
+
8
+ [bumpversion:file:pyproject.toml]
9
+ search = version = "{current_version}"
10
+ replace = version = "{new_version}"
11
+
12
+ [bumpversion:file:src/reconify/__init__.py]
13
+ search = __version__ = "{current_version}"
14
+ replace = __version__ = "{new_version}"
@@ -0,0 +1,22 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: actions/setup-python@v5
13
+ with:
14
+ python-version: "3.12"
15
+ - run: python -m pip install --upgrade pip
16
+ - run: pip install ".[dev]"
17
+ - run: ruff check .
18
+ - run: mypy src
19
+ - run: pytest -q
20
+ - run: python -m build
21
+ - run: python -m pip install --force-reinstall --no-deps dist/*.whl
22
+ - run: python -c "import reconify; print(reconify.Reconify.__name__)"
@@ -0,0 +1,21 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.12"
16
+ - run: python -m pip install --upgrade pip build
17
+ - run: python -m build
18
+ - name: Publish to PyPI
19
+ uses: pypa/gh-action-pypi-publish@release/v1
20
+ with:
21
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .mypy_cache/
5
+ .ruff_cache/
6
+ .venv/
7
+ dist/
8
+ build/
9
+ *.egg-info/
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial typed Reconify Python SDK.
6
+ - Added sync and async clients for the retained 50-operation API surface.
7
+ - Excluded deep reconciliation adjustment, evidence, lifecycle, report-item,
8
+ and signoff operations.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Reconify
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,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: reconify-python
3
+ Version: 0.1.0
4
+ Summary: Typed Python client for the Reconify Public API
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: httpx<1,>=0.27
9
+ Requires-Dist: pydantic<3,>=2.7
10
+ Provides-Extra: dev
11
+ Requires-Dist: build>=1.2; extra == 'dev'
12
+ Requires-Dist: bump2version>=1.0; extra == 'dev'
13
+ Requires-Dist: mypy>=1.10; extra == 'dev'
14
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
15
+ Requires-Dist: pytest>=8; extra == 'dev'
16
+ Requires-Dist: ruff>=0.5; extra == 'dev'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # Reconify Python SDK
20
+
21
+ Typed synchronous and asynchronous clients for the Reconify Public API.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install reconify-python
27
+ ```
28
+
29
+ ## Quickstart
30
+
31
+ ```python
32
+ from reconify import Reconify
33
+
34
+ with Reconify(api_key="rk_...") as client:
35
+ sources = client.ledger.list_ledger_sources(limit=25)
36
+ for source in sources.sources or []:
37
+ print(source.id, source.name)
38
+ ```
39
+
40
+ The key may also be supplied through `RECONIFY_API_KEY`. The default endpoint is
41
+ `https://api.reconifyhq.com/v1`; pass `base_url="https://staging.example/v1"`
42
+ for staging or self-hosted deployments. `/v1` is added when it is absent.
43
+
44
+ ## Async usage and pagination
45
+
46
+ ```python
47
+ from reconify import AsyncReconify
48
+
49
+ async with AsyncReconify() as client:
50
+ async for event in client.iter_events(limit=100):
51
+ print(event.id)
52
+ ```
53
+
54
+ Cursor and offset iterators preserve opaque cursors and the server's page size.
55
+ When an endpoint accepts both cursor and offset pagination, `after` takes
56
+ precedence.
57
+
58
+ Every list operation also has a natural iterator on the client, for example
59
+ `client.iter_reconciliations(limit=100)` or
60
+ `client.iter_wallet_transactions(after="cursor")`. The async equivalent is
61
+ an async iterator. Iterators forward query parameters using keyword arguments,
62
+ so they never expose transport details.
63
+
64
+ ## Errors and retries
65
+
66
+ HTTP failures raise typed `ReconifyError` subclasses. Every HTTP error exposes
67
+ `status_code`, `detail`, `code`, validation details, response headers, and the
68
+ response `request_id` without including credentials or request bodies.
69
+
70
+ 429, 503, and transient HTTP transport failures such as timeouts are retried
71
+ for safe methods with bounded exponential backoff and jitter. Mutating methods
72
+ are not retried unless `RetryConfig(retry_unsafe_methods=True)` is supplied.
73
+ Transaction ingestion retries must reuse each row's `idempotencyKey`.
74
+
75
+ The client default timeout is 30 seconds. Individual operations can override
76
+ it with `timeout=...`, including an `httpx.Timeout` object. Async operations
77
+ also support normal `asyncio` cancellation, which is the Python equivalent of
78
+ context cancellation in other SDKs.
79
+
80
+ Use `raw=True` on any operation to receive status, headers, request ID, and raw
81
+ body through `RawResponse`.
82
+
83
+ ## Test-session and ingestion headers
84
+
85
+ Integrity ingestion and test-session submission accept
86
+ `integrity_test_session=...`, which is sent as `X-Integrity-Test-Session`.
87
+ Integrity batches support 1–500 events and ledger transaction batches support
88
+ 1–5000 transactions; the SDK does not truncate caller input.
89
+
90
+ The SDK intentionally excludes reconciliation adjustment, evidence, lifecycle,
91
+ report-item, and signoff operations. The retained reconciliation surface is
92
+ integrity sources, reconciliation list/create/get, and all schedule operations.
93
+
94
+ ## API reference
95
+
96
+ The public operation methods are grouped by API module. Request bodies use the
97
+ typed Pydantic models exported from `reconify.models`; list query parameters use
98
+ the OpenAPI names in snake_case. Every operation accepts `raw=True` and a
99
+ per-request `timeout` override.
100
+
101
+ | Module | Methods |
102
+ | --- | --- |
103
+ | Alerts | `list_alert_rules`, `put_alert_rule` |
104
+ | Events | `list_events`, `get_event`, `reveal_event_field` |
105
+ | Ingestion | `ingest_integrity_events`, `ingest_integrity_test_events` |
106
+ | Issues | `list_issues`, `get_issue_summary`, `get_issue`, `update_issue`, `list_issue_deliveries`, `retry_issue_delivery`, `add_issue_note`, `resolve_issue` |
107
+ | Ledger | `list_ledger_sources`, `create_ledger_source`, `delete_ledger_source`, `get_ledger_source`, `update_ledger_source`, `list_source_periods`, `list_transactions`, `ingest_transactions` |
108
+ | Reconciliations | `list_integrity_sources_for_reconciliation`, `list_reconciliation_schedules`, `create_reconciliation_schedule`, `delete_reconciliation_schedule`, `get_reconciliation_schedule`, `update_reconciliation_schedule`, `list_reconciliations`, `create_reconciliation`, `get_reconciliation` |
109
+ | Search | `search_integrity_resources` |
110
+ | Setup | `list_setup_integrations`, `get_setup_integration`, `list_setup_sources`, `create_setup_source`, `get_setup_source`, `update_setup_source`, `disable_setup_source`, `create_test_session`, `get_test_session`, `get_test_session_result`, `retry_test_session`, `submit_test_session_events` |
111
+ | Transactions | `list_wallet_transactions`, `get_wallet_transaction` |
112
+ | Wallets | `list_wallets`, `get_wallet`, `get_wallet_balance` |
113
+
114
+ ## Build and deploy
115
+
116
+ Build the distributable artifacts locally or in CI:
117
+
118
+ ```bash
119
+ python -m pip install build
120
+ python -m build
121
+ ```
122
+
123
+ The resulting wheel and source archive in `dist/` are ready for publication to
124
+ an internal or public Python package registry. CI builds both artifacts after
125
+ running lint, type checking, and tests.
@@ -0,0 +1,107 @@
1
+ # Reconify Python SDK
2
+
3
+ Typed synchronous and asynchronous clients for the Reconify Public API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install reconify-python
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```python
14
+ from reconify import Reconify
15
+
16
+ with Reconify(api_key="rk_...") as client:
17
+ sources = client.ledger.list_ledger_sources(limit=25)
18
+ for source in sources.sources or []:
19
+ print(source.id, source.name)
20
+ ```
21
+
22
+ The key may also be supplied through `RECONIFY_API_KEY`. The default endpoint is
23
+ `https://api.reconifyhq.com/v1`; pass `base_url="https://staging.example/v1"`
24
+ for staging or self-hosted deployments. `/v1` is added when it is absent.
25
+
26
+ ## Async usage and pagination
27
+
28
+ ```python
29
+ from reconify import AsyncReconify
30
+
31
+ async with AsyncReconify() as client:
32
+ async for event in client.iter_events(limit=100):
33
+ print(event.id)
34
+ ```
35
+
36
+ Cursor and offset iterators preserve opaque cursors and the server's page size.
37
+ When an endpoint accepts both cursor and offset pagination, `after` takes
38
+ precedence.
39
+
40
+ Every list operation also has a natural iterator on the client, for example
41
+ `client.iter_reconciliations(limit=100)` or
42
+ `client.iter_wallet_transactions(after="cursor")`. The async equivalent is
43
+ an async iterator. Iterators forward query parameters using keyword arguments,
44
+ so they never expose transport details.
45
+
46
+ ## Errors and retries
47
+
48
+ HTTP failures raise typed `ReconifyError` subclasses. Every HTTP error exposes
49
+ `status_code`, `detail`, `code`, validation details, response headers, and the
50
+ response `request_id` without including credentials or request bodies.
51
+
52
+ 429, 503, and transient HTTP transport failures such as timeouts are retried
53
+ for safe methods with bounded exponential backoff and jitter. Mutating methods
54
+ are not retried unless `RetryConfig(retry_unsafe_methods=True)` is supplied.
55
+ Transaction ingestion retries must reuse each row's `idempotencyKey`.
56
+
57
+ The client default timeout is 30 seconds. Individual operations can override
58
+ it with `timeout=...`, including an `httpx.Timeout` object. Async operations
59
+ also support normal `asyncio` cancellation, which is the Python equivalent of
60
+ context cancellation in other SDKs.
61
+
62
+ Use `raw=True` on any operation to receive status, headers, request ID, and raw
63
+ body through `RawResponse`.
64
+
65
+ ## Test-session and ingestion headers
66
+
67
+ Integrity ingestion and test-session submission accept
68
+ `integrity_test_session=...`, which is sent as `X-Integrity-Test-Session`.
69
+ Integrity batches support 1–500 events and ledger transaction batches support
70
+ 1–5000 transactions; the SDK does not truncate caller input.
71
+
72
+ The SDK intentionally excludes reconciliation adjustment, evidence, lifecycle,
73
+ report-item, and signoff operations. The retained reconciliation surface is
74
+ integrity sources, reconciliation list/create/get, and all schedule operations.
75
+
76
+ ## API reference
77
+
78
+ The public operation methods are grouped by API module. Request bodies use the
79
+ typed Pydantic models exported from `reconify.models`; list query parameters use
80
+ the OpenAPI names in snake_case. Every operation accepts `raw=True` and a
81
+ per-request `timeout` override.
82
+
83
+ | Module | Methods |
84
+ | --- | --- |
85
+ | Alerts | `list_alert_rules`, `put_alert_rule` |
86
+ | Events | `list_events`, `get_event`, `reveal_event_field` |
87
+ | Ingestion | `ingest_integrity_events`, `ingest_integrity_test_events` |
88
+ | Issues | `list_issues`, `get_issue_summary`, `get_issue`, `update_issue`, `list_issue_deliveries`, `retry_issue_delivery`, `add_issue_note`, `resolve_issue` |
89
+ | Ledger | `list_ledger_sources`, `create_ledger_source`, `delete_ledger_source`, `get_ledger_source`, `update_ledger_source`, `list_source_periods`, `list_transactions`, `ingest_transactions` |
90
+ | Reconciliations | `list_integrity_sources_for_reconciliation`, `list_reconciliation_schedules`, `create_reconciliation_schedule`, `delete_reconciliation_schedule`, `get_reconciliation_schedule`, `update_reconciliation_schedule`, `list_reconciliations`, `create_reconciliation`, `get_reconciliation` |
91
+ | Search | `search_integrity_resources` |
92
+ | Setup | `list_setup_integrations`, `get_setup_integration`, `list_setup_sources`, `create_setup_source`, `get_setup_source`, `update_setup_source`, `disable_setup_source`, `create_test_session`, `get_test_session`, `get_test_session_result`, `retry_test_session`, `submit_test_session_events` |
93
+ | Transactions | `list_wallet_transactions`, `get_wallet_transaction` |
94
+ | Wallets | `list_wallets`, `get_wallet`, `get_wallet_balance` |
95
+
96
+ ## Build and deploy
97
+
98
+ Build the distributable artifacts locally or in CI:
99
+
100
+ ```bash
101
+ python -m pip install build
102
+ python -m build
103
+ ```
104
+
105
+ The resulting wheel and source archive in `dist/` are ready for publication to
106
+ an internal or public Python package registry. CI builds both artifacts after
107
+ running lint, type checking, and tests.
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "reconify-python"
7
+ version = "0.1.0"
8
+ description = "Typed Python client for the Reconify Public API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ dependencies = [
13
+ "httpx>=0.27,<1",
14
+ "pydantic>=2.7,<3",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "build>=1.2",
20
+ "bump2version>=1.0",
21
+ "mypy>=1.10",
22
+ "pytest>=8",
23
+ "pytest-asyncio>=0.23",
24
+ "ruff>=0.5",
25
+ ]
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["src/reconify"]
29
+
30
+ [tool.pytest.ini_options]
31
+ asyncio_mode = "auto"
32
+ testpaths = ["tests"]
33
+ pythonpath = ["src"]
34
+
35
+ [tool.ruff]
36
+ line-length = 100
37
+ target-version = "py310"
38
+
39
+ [tool.ruff.lint]
40
+ select = ["E", "F", "I", "UP"]
41
+
42
+ [tool.mypy]
43
+ python_version = "3.10"
44
+ strict = true
45
+ plugins = ["pydantic.mypy"]
46
+
47
+ [tool.pydantic-mypy]
48
+ init_forbid_extra = true
49
+ init_typed = true
50
+ warn_required_dynamic_aliases = true
51
+ warn_untyped_fields = true
@@ -0,0 +1,37 @@
1
+ # Local API Error-Data Report
2
+
3
+ Date: 2026-07-21
4
+ Environment: local Docker stack at `http://localhost:3002/v1`
5
+ Client: Reconify Python SDK
6
+
7
+ ## Results
8
+
9
+ - A mixed ledger batch with one valid `status: "failed"` row and one invalid
10
+ date returned HTTP 200 with `ingested: 1` and one per-row error. The failed
11
+ row was persisted and returned by the failed-transaction filter.
12
+ - Replaying the valid row with the same `idempotencyKey` returned
13
+ `duplicates: 1`, confirming idempotent ingestion.
14
+ - An integrity event missing the required `eventType` returned HTTP 422 and
15
+ was surfaced as `ReconifyRequestError` with the server request ID.
16
+ - An alert rule using a valid-but-unknown UUID `controlId` returned HTTP 404
17
+ (`control not found`), as expected.
18
+
19
+ ## API issue found
20
+
21
+ An alert rule using a syntactically invalid `controlId` such as
22
+ `missing-control-format` returned HTTP 500 with `internal_error` instead of a
23
+ client validation error. The public OpenAPI schema currently describes
24
+ `controlId` as an unconstrained string, while the database query casts it to a
25
+ UUID.
26
+
27
+ Recommended API fix: validate `controlId` as a UUID before persistence and
28
+ return HTTP 400 for malformed values. Keep HTTP 404 for syntactically valid
29
+ UUIDs that do not identify a control in the organization.
30
+
31
+ ## Alert and issue state
32
+
33
+ The clean local tenant had no seeded integrity controls, so no alert rule could
34
+ be created and no alert or issue was generated by this scenario. The failed
35
+ ledger transaction path is independent of integrity-control setup and worked
36
+ as documented.
37
+
@@ -0,0 +1,36 @@
1
+ """Typed clients for the Reconify Public API."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from .client import AsyncReconify, Reconify
6
+ from .errors import (
7
+ ReconifyAuthenticationError,
8
+ ReconifyConflictError,
9
+ ReconifyError,
10
+ ReconifyNotFoundError,
11
+ ReconifyPermissionError,
12
+ ReconifyRateLimitError,
13
+ ReconifyRequestError,
14
+ ReconifyServerError,
15
+ ReconifyServiceUnavailableError,
16
+ ReconifyValidationError,
17
+ )
18
+ from .transport import RawResponse, RetryConfig
19
+
20
+ __all__ = [
21
+ "AsyncReconify",
22
+ "Reconify",
23
+ "__version__",
24
+ "RawResponse",
25
+ "RetryConfig",
26
+ "ReconifyError",
27
+ "ReconifyValidationError",
28
+ "ReconifyAuthenticationError",
29
+ "ReconifyPermissionError",
30
+ "ReconifyNotFoundError",
31
+ "ReconifyConflictError",
32
+ "ReconifyRequestError",
33
+ "ReconifyRateLimitError",
34
+ "ReconifyServiceUnavailableError",
35
+ "ReconifyServerError",
36
+ ]