kuahr 0.3.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.
kuahr-0.3.0/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .DS_Store
kuahr-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kua
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.
kuahr-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.5
2
+ Name: kuahr
3
+ Version: 0.3.0
4
+ Summary: Official Python client for Kua company people, payroll drafts, events and webhooks.
5
+ Project-URL: Homepage, https://docs.kuahr.com
6
+ Project-URL: Repository, https://github.com/kuahr/kua-sdk/tree/main/python
7
+ Author: Kua
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: africa,api,kua,nigeria,payments,payroll,wallet
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.9
15
+ Requires-Dist: httpx>=0.24
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=7; extra == 'dev'
18
+ Description-Content-Type: text/markdown
19
+
20
+ <picture>
21
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kuahr/.github/main/profile/dark.svg">
22
+ <img src="https://raw.githubusercontent.com/kuahr/.github/main/profile/light.svg" alt="Kua" width="150">
23
+ </picture>
24
+
25
+ # Kua Python SDK
26
+
27
+ Official client for Kua company people, payroll drafts, workforce resources,
28
+ events and webhooks. [Documentation](https://docs.kuahr.com/sdks/python).
29
+
30
+ ## Install from public source
31
+
32
+ Source version 0.3.0 is available here; registry publication is pending. The
33
+ distribution name is `kuahr`, with `import kua`. The unrelated PyPI package
34
+ named `kua` is not this SDK.
35
+
36
+ ```bash
37
+ python -m venv .venv
38
+ source .venv/bin/activate
39
+ git clone --branch main https://github.com/kuahr/kua-sdk.git
40
+ python -m pip install ./kua-sdk/python
41
+ ```
42
+
43
+ Requires Python 3.9+. On Windows activate `.venv\Scripts\activate` instead.
44
+ Store a sandbox-issued Test key with `payroll:read` in `KUA_API_KEY`:
45
+
46
+ ```python
47
+ import os
48
+ from kua import Kua
49
+ with Kua(api_key=os.environ["KUA_API_KEY"], base_url="https://sandbox-api.kuahr.com") as kua:
50
+ print(kua.payroll_runs.list())
51
+ ```
52
+
53
+ ## Development
54
+
55
+ ```bash
56
+ python -m pip install -e ".[dev]"
57
+ python -m pytest -q
58
+ ```
59
+
60
+ Expected result: an array of this company's Test payroll runs, including `[]`.
61
+ Choose the host explicitly: both clients otherwise default to production.
62
+
63
+ ## Supported boundaries
64
+
65
+ Employee creation and payroll draft/list operations support Test mode. Events,
66
+ webhooks and workforce add-ons require a separately authorised Live key with the
67
+ matching resource scopes. Draft creation is not approval or payment; salary setup
68
+ is outside this public SDK. Registered-client Kola OIDC uses HTTP separately.
69
+
70
+ The client does not retry automatically. Persist an operation-specific idempotency
71
+ key and exact request before writes. Credential rotation changes that namespace;
72
+ reconcile uncertain outcomes before retrying under another credential. Webhook
73
+ replay uses a separate stable request key/reason. Queued is not delivered.
74
+
75
+ ## Webhooks and production readiness
76
+
77
+ Use the included raw-body signature helper and timestamp tolerance, then
78
+ deduplicate the verified body's event ID. Commit durable acceptance before
79
+ acknowledging a delivery. Follow setup, recovery, rotation and go-live guidance at
80
+ https://docs.kuahr.com. Never log credentials or signing secrets.
81
+
82
+ ## Licence
83
+
84
+ MIT. See LICENSE.
kuahr-0.3.0/README.md ADDED
@@ -0,0 +1,65 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kuahr/.github/main/profile/dark.svg">
3
+ <img src="https://raw.githubusercontent.com/kuahr/.github/main/profile/light.svg" alt="Kua" width="150">
4
+ </picture>
5
+
6
+ # Kua Python SDK
7
+
8
+ Official client for Kua company people, payroll drafts, workforce resources,
9
+ events and webhooks. [Documentation](https://docs.kuahr.com/sdks/python).
10
+
11
+ ## Install from public source
12
+
13
+ Source version 0.3.0 is available here; registry publication is pending. The
14
+ distribution name is `kuahr`, with `import kua`. The unrelated PyPI package
15
+ named `kua` is not this SDK.
16
+
17
+ ```bash
18
+ python -m venv .venv
19
+ source .venv/bin/activate
20
+ git clone --branch main https://github.com/kuahr/kua-sdk.git
21
+ python -m pip install ./kua-sdk/python
22
+ ```
23
+
24
+ Requires Python 3.9+. On Windows activate `.venv\Scripts\activate` instead.
25
+ Store a sandbox-issued Test key with `payroll:read` in `KUA_API_KEY`:
26
+
27
+ ```python
28
+ import os
29
+ from kua import Kua
30
+ with Kua(api_key=os.environ["KUA_API_KEY"], base_url="https://sandbox-api.kuahr.com") as kua:
31
+ print(kua.payroll_runs.list())
32
+ ```
33
+
34
+ ## Development
35
+
36
+ ```bash
37
+ python -m pip install -e ".[dev]"
38
+ python -m pytest -q
39
+ ```
40
+
41
+ Expected result: an array of this company's Test payroll runs, including `[]`.
42
+ Choose the host explicitly: both clients otherwise default to production.
43
+
44
+ ## Supported boundaries
45
+
46
+ Employee creation and payroll draft/list operations support Test mode. Events,
47
+ webhooks and workforce add-ons require a separately authorised Live key with the
48
+ matching resource scopes. Draft creation is not approval or payment; salary setup
49
+ is outside this public SDK. Registered-client Kola OIDC uses HTTP separately.
50
+
51
+ The client does not retry automatically. Persist an operation-specific idempotency
52
+ key and exact request before writes. Credential rotation changes that namespace;
53
+ reconcile uncertain outcomes before retrying under another credential. Webhook
54
+ replay uses a separate stable request key/reason. Queued is not delivered.
55
+
56
+ ## Webhooks and production readiness
57
+
58
+ Use the included raw-body signature helper and timestamp tolerance, then
59
+ deduplicate the verified body's event ID. Commit durable acceptance before
60
+ acknowledging a delivery. Follow setup, recovery, rotation and go-live guidance at
61
+ https://docs.kuahr.com. Never log credentials or signing secrets.
62
+
63
+ ## Licence
64
+
65
+ MIT. See LICENSE.
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "kuahr"
7
+ version = "0.3.0"
8
+ description = "Official Python client for Kua company people, payroll drafts, events and webhooks."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ authors = [{ name = "Kua" }]
13
+ keywords = ["kua", "payroll", "payments", "wallet", "api", "nigeria", "africa"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Intended Audience :: Developers",
18
+ ]
19
+ dependencies = ["httpx>=0.24"]
20
+
21
+ [project.urls]
22
+ Homepage = "https://docs.kuahr.com"
23
+ Repository = "https://github.com/kuahr/kua-sdk/tree/main/python"
24
+
25
+ [project.optional-dependencies]
26
+ dev = ["pytest>=7"]
27
+
28
+ [tool.hatch.build.targets.wheel]
29
+ packages = ["src/kua"]
@@ -0,0 +1,67 @@
1
+ """Official Python client for the Kua API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .client import Kua
6
+ from .errors import KuaError
7
+ from .models import (
8
+ Advance,
9
+ AppraisalCycle,
10
+ AppraisalReview,
11
+ CheckIn,
12
+ EmployeeCreated,
13
+ EventItem,
14
+ KeyResult,
15
+ LeaveAllocation,
16
+ LeaveBalance,
17
+ LeaveRequest,
18
+ LeaveType,
19
+ Objective,
20
+ ObjectiveDetail,
21
+ PayrollRunCreated,
22
+ PayrollRunSummary,
23
+ Promotion,
24
+ TestDataCleared,
25
+ WebhookCreated,
26
+ WebhookHistory,
27
+ WebhookReplayAccepted,
28
+ WebhookDeliveryAttempt,
29
+ WebhookReplayRecord,
30
+ WebhookDeliveryItem,
31
+ WebhookPingAccepted,
32
+ WebhookSummary,
33
+ )
34
+ from .webhooks_verify import verify_signature
35
+
36
+ __version__ = "0.3.0"
37
+
38
+ __all__ = [
39
+ "Kua",
40
+ "KuaError",
41
+ "verify_signature",
42
+ "PayrollRunSummary",
43
+ "PayrollRunCreated",
44
+ "EventItem",
45
+ "EmployeeCreated",
46
+ "TestDataCleared",
47
+ "WebhookCreated",
48
+ "WebhookHistory",
49
+ "WebhookReplayAccepted",
50
+ "WebhookDeliveryAttempt",
51
+ "WebhookReplayRecord",
52
+ "WebhookSummary",
53
+ "WebhookDeliveryItem",
54
+ "WebhookPingAccepted",
55
+ "LeaveType",
56
+ "LeaveRequest",
57
+ "LeaveBalance",
58
+ "LeaveAllocation",
59
+ "Advance",
60
+ "Objective",
61
+ "KeyResult",
62
+ "CheckIn",
63
+ "ObjectiveDetail",
64
+ "AppraisalCycle",
65
+ "AppraisalReview",
66
+ "Promotion",
67
+ ]
@@ -0,0 +1,80 @@
1
+ """Internal HTTP transport."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ import httpx
8
+
9
+ from .errors import KuaError
10
+
11
+ DEFAULT_BASE_URL = "https://api.kuahr.com"
12
+ CLIENT_VERSION = "0.3.0"
13
+
14
+
15
+ class _Transport:
16
+ def __init__(
17
+ self,
18
+ api_key: str,
19
+ base_url: Optional[str] = None,
20
+ timeout: float = 30.0,
21
+ _client: Optional[httpx.Client] = None,
22
+ ) -> None:
23
+ if not api_key:
24
+ raise ValueError("Kua: `api_key` is required.")
25
+ self._client = _client or httpx.Client(
26
+ base_url=(base_url or DEFAULT_BASE_URL).rstrip("/"),
27
+ timeout=timeout,
28
+ )
29
+ # Apply auth/identity headers to the client regardless of whether it was
30
+ # constructed here or injected (e.g. in tests).
31
+ self._client.headers.update(
32
+ {
33
+ "X-Api-Key": api_key,
34
+ "Accept": "application/json",
35
+ "User-Agent": f"kua-python/{CLIENT_VERSION}",
36
+ }
37
+ )
38
+
39
+ def request(
40
+ self,
41
+ method: str,
42
+ path: str,
43
+ *,
44
+ params: Optional[Dict[str, Any]] = None,
45
+ json: Optional[Any] = None,
46
+ idempotency_key: Optional[str] = None,
47
+ ) -> Any:
48
+ headers = {}
49
+ if idempotency_key:
50
+ headers["Idempotency-Key"] = idempotency_key
51
+
52
+ clean_params = {k: v for k, v in (params or {}).items() if v is not None}
53
+
54
+ res = self._client.request(
55
+ method, path, params=clean_params or None, json=json, headers=headers or None
56
+ )
57
+
58
+ if res.status_code == 204:
59
+ return None
60
+
61
+ data: Any = None
62
+ if res.content:
63
+ try:
64
+ data = res.json()
65
+ except ValueError:
66
+ data = None
67
+
68
+ if res.status_code >= 400:
69
+ code = (data or {}).get("code") if isinstance(data, dict) else None
70
+ message = (data or {}).get("message") if isinstance(data, dict) else None
71
+ raise KuaError(
72
+ res.status_code,
73
+ code or f"HTTP_{res.status_code}",
74
+ message or f"Kua request failed with status {res.status_code}.",
75
+ )
76
+
77
+ return data
78
+
79
+ def close(self) -> None:
80
+ self._client.close()
@@ -0,0 +1,65 @@
1
+ """The Kua API client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import httpx
8
+
9
+ from ._http import _Transport
10
+ from .resources import (
11
+ Advances,
12
+ Employees,
13
+ Events,
14
+ Leave,
15
+ PayrollRuns,
16
+ Performance,
17
+ TestData,
18
+ Webhooks,
19
+ )
20
+
21
+
22
+ class Kua:
23
+ """The Kua API client.
24
+
25
+ ```python
26
+ from kua import Kua
27
+
28
+ kua = Kua(api_key="kua_live_…")
29
+ runs = kua.payroll_runs.list()
30
+ ```
31
+
32
+ Can be used as a context manager to close the underlying HTTP connection::
33
+
34
+ with Kua(api_key=...) as kua:
35
+ kua.payroll_runs.list()
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ api_key: str,
41
+ *,
42
+ base_url: Optional[str] = None,
43
+ timeout: float = 30.0,
44
+ _http_client: Optional[httpx.Client] = None,
45
+ ) -> None:
46
+ self._transport = _Transport(
47
+ api_key, base_url=base_url, timeout=timeout, _client=_http_client
48
+ )
49
+ self.payroll_runs = PayrollRuns(self._transport)
50
+ self.employees = Employees(self._transport)
51
+ self.events = Events(self._transport)
52
+ self.webhooks = Webhooks(self._transport)
53
+ self.test_data = TestData(self._transport)
54
+ self.leave = Leave(self._transport)
55
+ self.advances = Advances(self._transport)
56
+ self.performance = Performance(self._transport)
57
+
58
+ def close(self) -> None:
59
+ self._transport.close()
60
+
61
+ def __enter__(self) -> "Kua":
62
+ return self
63
+
64
+ def __exit__(self, *_exc: object) -> None:
65
+ self.close()
@@ -0,0 +1,22 @@
1
+ """Error types for the Kua SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class KuaError(Exception):
7
+ """Raised when the Kua API returns a non-2xx response.
8
+
9
+ Attributes:
10
+ code: A stable, machine-readable identifier you can branch on
11
+ (e.g. ``"INSUFFICIENT_SCOPE"``).
12
+ status: The HTTP status code.
13
+ """
14
+
15
+ def __init__(self, status: int, code: str, message: str) -> None:
16
+ super().__init__(message)
17
+ self.status = status
18
+ self.code = code
19
+ self.message = message
20
+
21
+ def __repr__(self) -> str: # pragma: no cover - repr convenience
22
+ return f"KuaError(status={self.status!r}, code={self.code!r}, message={self.message!r})"