campai-client
A Python SDK for campai.com, generated from campai's OpenAPI spec, with pydantic v2 models, sync + async clients, and a durable patch layer for spec-vs-reality fixes.
Status: early / alpha. See
docs/DESIGN.mdfor the full architecture anddocs/adr/for key decisions.
Install
pip install campai-client
Requires Python ≥ 3.13. Runtime deps: httpx, pydantic>=2, pydantic-settings.
Quickstart
from campai_client import CampaiClient
from campai_client import models
client = CampaiClient(
api_key="...", # or env CAMPAI_API_KEY
organization_id="...", # or env CAMPAI_ORGANIZATION_ID
mandate_id="...", # optional default; overridable per call
# base_url defaults to https://cloud.campai.com/api
)
# Ergonomic namespaces mirror campai's tag hierarchy. List bodies (pagination +
# filters) are typed via the generated request model:
page = client.crm.applications.forms.list(
body=models.CrmApplicationsFormsListFormsRequest(limit=50)
)
print(page.count, len(page.items))
form = client.crm.applications.forms.get(application_form_id="...")
# Walk every page transparently:
for form in client.crm.applications.forms.iterate():
...
client.close()
Async mirror:
from campai_client import AsyncCampaiClient
async with AsyncCampaiClient(api_key="...", organization_id="...") as client:
form = await client.crm.applications.forms.get(application_form_id="...")
async for f in client.crm.applications.forms.aiterate():
...
Configuration is resolved from constructor args or CAMPAI_* env vars
(CAMPAI_API_KEY, CAMPAI_ORGANIZATION_ID, CAMPAI_MANDATE_ID, CAMPAI_BASE_URL).
Errors
HTTP status + campai's error envelope map to a typed hierarchy: CampaiAPIError
(BadRequestError/AuthenticationError/PermissionError/NotFoundError/ServerError),
plus CampaiValidationError (response didn't match the models) and
CampaiConfigError.
How it's built (three layers)
- Spec pipeline (
spec/,scripts/): download → normalize/overlay → committed spec. - Generated layer (
src/campai_client/_generated/, never hand-edited): 2 200+ pydantic v2 models via openapi-generator (models-only), plus the operations manifest (operations.py) built from the spec. - Facade + patch layer (
src/campai_client/, durable): clients, resource namespaces, the patch pipeline, pagination, errors.
See docs/adr/0002 for how the runtime call
path and resource tree are driven by the manifest.
The agent maintenance loop
When campai's spec changes, regenerate and let the tests triage the drift:
pixi run regen # fetch + normalize + generate + manifest + format
pixi run test # replay (VCR) tests
Triage failures:
CampaiValidationError/ import error → schema drift. Fixspec/overlay.yaml(schema-level, JSON-Pointer keyed), thenpixi run regen.- Behavioural mismatch (id-only response, aliasing, follow-up needed) → add or
adjust a patch in
src/campai_client/patches/plus a test. Patches without tests are not allowed. - New/removed operations → the manifest + resource namespaces regenerate automatically; add smoke tests for important new resources.
Cassettes are re-recorded (CAMPAI_RECORD=1 pixi run test-record) only when the real
API behaviour changed.
Development
pixi manages the dev environment (the shipped package uses standard
pyproject.toml deps).
pixi run check # lint + typecheck + test (CI aggregate)
pixi run lint # ruff check
pixi run format # ruff format
pixi run typecheck # ty check
pixi run test # pytest (VCR replay)
pixi run regen # full regeneration (openapi-generator via pixi's JVM)
lefthook runs format/lint/typecheck on staged files and guards against hand-edits
to _generated/**.
Regeneration (
pixi run regen/generate) runs theopenapi-generatorJAR with a JVM supplied by pixi (openjdk) — no Docker required (it runs fine inside containers). The JAR is pinned by version + SHA-256 and cached under.cache/. Using or testing the shipped client needs neither Java nor the JAR.
Integration tests (real campai org)
Most tests use a mock transport. The integration tests
(tests/resources/test_live.py) hit the real API and are recorded with VCR, then
replayed from committed cassettes (no credentials on replay). Provide credentials
via env vars to record:
CAMPAI_RECORD=1 CAMPAI_API_KEY=... CAMPAI_ORGANIZATION_ID=... \
CAMPAI_MANDATE_ID=... pixi run test-record
Credentials are scrubbed from cassettes. See tests/README.md
for the full flow and scrubbing tradeoffs.
License
Apache-2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file campai_client-2026.8.5.tar.gz.
File metadata
- Download URL: campai_client-2026.8.5.tar.gz
- Upload date:
- Size: 863.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c124bda5b22f1b38a6d8a84674b6fd6c7f9690adc80bd4ad90df9542c406bcae
|
|
| MD5 |
efc953c05411d3ae8830f20952ffe2fd
|
|
| BLAKE2b-256 |
d11c9d8f67791303f925377eee6a88b6ecc7a833391483bd07224e152825aa11
|
Provenance
The following attestation bundles were made for campai_client-2026.8.5.tar.gz:
Publisher:
release.yml on janjagusch/campai-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
campai_client-2026.8.5.tar.gz -
Subject digest:
c124bda5b22f1b38a6d8a84674b6fd6c7f9690adc80bd4ad90df9542c406bcae - Sigstore transparency entry: 2381317280
- Sigstore integration time:
-
Permalink:
janjagusch/campai-client@24d369bcf3787c7c98dba7710c31bb9b0920e339 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/janjagusch
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@24d369bcf3787c7c98dba7710c31bb9b0920e339 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file campai_client-2026.8.5-py3-none-any.whl.
File metadata
- Download URL: campai_client-2026.8.5-py3-none-any.whl
- Upload date:
- Size: 3.7 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72e99be576229aa0962309214bca02751e9f0b4b1f19ccd399fc26261d532d23
|
|
| MD5 |
45814f7e5c3e61ce655aa93da93679bc
|
|
| BLAKE2b-256 |
1bef5c7fbd2e0a695e53a1f9562b7d75bd56c7bc659ab4fc52edf1a7c6fbcbad
|
Provenance
The following attestation bundles were made for campai_client-2026.8.5-py3-none-any.whl:
Publisher:
release.yml on janjagusch/campai-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
campai_client-2026.8.5-py3-none-any.whl -
Subject digest:
72e99be576229aa0962309214bca02751e9f0b4b1f19ccd399fc26261d532d23 - Sigstore transparency entry: 2381317908
- Sigstore integration time:
-
Permalink:
janjagusch/campai-client@24d369bcf3787c7c98dba7710c31bb9b0920e339 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/janjagusch
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@24d369bcf3787c7c98dba7710c31bb9b0920e339 -
Trigger Event:
workflow_dispatch
-
Statement type: