agencyzoom-sdk 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.
- agencyzoom_sdk-0.1.0/.gitignore +21 -0
- agencyzoom_sdk-0.1.0/CHANGELOG.md +40 -0
- agencyzoom_sdk-0.1.0/LICENSE +21 -0
- agencyzoom_sdk-0.1.0/MAINTAINING.md +192 -0
- agencyzoom_sdk-0.1.0/PKG-INFO +491 -0
- agencyzoom_sdk-0.1.0/README.md +456 -0
- agencyzoom_sdk-0.1.0/pyproject.toml +108 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/__init__.py +63 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/__init__.py +1 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/_auth.py +116 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/_compat.py +56 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/_http.py +312 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/_resource.py +134 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/client.py +279 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/__init__.py +51 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/auth.py +104 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/customers.py +137 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/leads.py +376 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/misc.py +117 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/opportunities.py +209 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/pipelines.py +38 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/policies.py +88 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/service_tickets.py +116 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/settings.py +111 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/tasks.py +93 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_async/resources/threads.py +106 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_config.py +180 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_endpoints.py +251 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_exceptions.py +193 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_rate_limit.py +48 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_retry.py +58 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/__init__.py +1 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/_auth.py +118 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/_compat.py +18 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/_http.py +312 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/_resource.py +136 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/client.py +281 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/__init__.py +53 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/auth.py +104 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/customers.py +139 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/leads.py +364 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/misc.py +117 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/opportunities.py +209 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/pipelines.py +40 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/policies.py +86 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/service_tickets.py +118 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/settings.py +105 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/tasks.py +95 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_sync/resources/threads.py +98 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_tokens.py +70 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_utils.py +299 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/_version.py +1 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/models/__init__.py +12 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/models/_base.py +87 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/models/_generated.py +4186 -0
- agencyzoom_sdk-0.1.0/src/agencyzoom/py.typed +0 -0
- agencyzoom_sdk-0.1.0/tests/__init__.py +0 -0
- agencyzoom_sdk-0.1.0/tests/conftest.py +75 -0
- agencyzoom_sdk-0.1.0/tests/test_auth.py +128 -0
- agencyzoom_sdk-0.1.0/tests/test_edge_cases.py +568 -0
- agencyzoom_sdk-0.1.0/tests/test_endpoint_coverage.py +105 -0
- agencyzoom_sdk-0.1.0/tests/test_resources.py +182 -0
- agencyzoom_sdk-0.1.0/tests/test_transport.py +184 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.venv/
|
|
4
|
+
venv/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
.env
|
|
14
|
+
.DS_Store
|
|
15
|
+
.venvs/
|
|
16
|
+
|
|
17
|
+
# AgencyZoom's OpenAPI spec is proprietary: never commit it. `make spec` downloads it.
|
|
18
|
+
# Only its fingerprint (openapi/agencyzoom.yaml.sha256) is tracked.
|
|
19
|
+
openapi/*.yaml
|
|
20
|
+
openapi/*.yml
|
|
21
|
+
openapi/*.json
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Lenient parsing now repairs every off-spec field in one pass. Previously it fixed one field per
|
|
11
|
+
pass and could give up, returning a raw `dict` (seen on service tickets). Each response now logs
|
|
12
|
+
one concise warning naming each field and the reason.
|
|
13
|
+
- Corrected field types that contradict AgencyZoom's spec, verified on a live account:
|
|
14
|
+
service ticket `createdBy`/`modifiedBy` (names), `dueDate`/`enterStageTS` (date strings);
|
|
15
|
+
dashboard `monthlyItems`/`monthlyPolicies` (fractional); life lead `status` (single int);
|
|
16
|
+
note `attr` (may be `[]`); policy `tagNames` (list).
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `make smoke`: a read-only check against a real account. `ARGS=--diagnose` reports the masked shape of
|
|
20
|
+
off-spec fields without exposing data.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- AgencyZoom's OpenAPI spec is no longer stored in the repository. `make spec` downloads it,
|
|
24
|
+
and a weekly *Spec drift* workflow detects API changes.
|
|
25
|
+
|
|
26
|
+
## [0.1.0] - 2026-10-22
|
|
27
|
+
|
|
28
|
+
Initial release.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- Sync (`AgencyZoom`) and async (`AsyncAgencyZoom`) clients covering all 117
|
|
32
|
+
endpoints of the AgencyZoom API spec (2026.08.17).
|
|
33
|
+
- Endpoint registry (`agencyzoom.ENDPOINTS`) with `api_version`, `api_root`,
|
|
34
|
+
`endpoint_overrides` and `with_options()` for API-version changes without an SDK release.
|
|
35
|
+
- Generated Pydantic v2 models for every schema, with lenient response parsing.
|
|
36
|
+
- Automatic login, proactive token refresh, single-flight 401 recovery.
|
|
37
|
+
- Client-side rate limiting (120/min default); retries with backoff, jitter and `Retry-After`.
|
|
38
|
+
- Lazy auto-pagination with protection against servers that ignore `page`.
|
|
39
|
+
- Multipart uploads (paths, bytes, binary or text file handles), optional reference-data cache.
|
|
40
|
+
- Python 3.8 – 3.14 support; installable with uv, pip, Poetry, PDM and Pipenv.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jatin Yadav
|
|
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,192 @@
|
|
|
1
|
+
# Maintaining the AgencyZoom Python SDK
|
|
2
|
+
|
|
3
|
+
The working manual for whoever maintains this package: how the code is laid out, the routine tasks, and how to release.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. One-time setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
brew install uv # or: curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
11
|
+
git clone https://github.com/iamjatinyadav/agencyzoom-python-sdk && cd agencyzoom-python-sdk
|
|
12
|
+
uv sync # .venv with runtime + dev dependencies, pinned by uv.lock
|
|
13
|
+
make spec # download AgencyZoom's spec (it is deliberately not committed)
|
|
14
|
+
make check # lint + mypy --strict + tests + generated-code check
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`make help` lists every target. Run `make test-all` before a release: it runs the suite on Python 3.8 through 3.14, and uv downloads any interpreter you don't have.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. Architecture in one page
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
AgencyZoom's public docs ──► scripts/fetch_spec.py ──► openapi/agencyzoom.yaml (git-ignored)
|
|
25
|
+
│ fingerprint pinned in
|
|
26
|
+
│ openapi/agencyzoom.yaml.sha256
|
|
27
|
+
▼
|
|
28
|
+
scripts/generate_models.py ──► src/agencyzoom/models/_generated.py
|
|
29
|
+
(230+ Pydantic models, never edited by hand)
|
|
30
|
+
|
|
31
|
+
src/agencyzoom/_endpoints.py ENDPOINTS registry: ID -> (method, version-relative path)
|
|
32
|
+
src/agencyzoom/_async/ HAND-WRITTEN async client ──► scripts/unasync.py ──► src/agencyzoom/_sync/
|
|
33
|
+
(GENERATED sync client)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| Layer | Files | Responsibility |
|
|
37
|
+
| --- | --- | --- |
|
|
38
|
+
| Public API | `__init__.py`, `_async/client.py` | Client constructor, resource attributes, `with_options`, `request` |
|
|
39
|
+
| Resources | `_async/resources/*.py` | One method per endpoint: builds the body, calls `self._call("<id>", ...)` |
|
|
40
|
+
| Endpoint registry | `_endpoints.py` | **The only place API paths live.** Resolves ID, version and overrides to a URL path |
|
|
41
|
+
| Transport | `_async/_http.py` | Auth header, rate limit, retries, 401 recovery, decoding, cache |
|
|
42
|
+
| Auth | `_async/_auth.py`, `_tokens.py` | Login, refresh, expiry, single-flight lock |
|
|
43
|
+
| Pure logic | `_retry.py`, `_rate_limit.py`, `_utils.py`, `_config.py`, `_exceptions.py` | No I/O. Shared by sync and async |
|
|
44
|
+
| Models | `models/_base.py` (hand-written), `models/_generated.py` | Lenient Pydantic models |
|
|
45
|
+
| Concurrency shims | `_async/_compat.py`, `_sync/_compat.py` | Lock, sleep and blocking-call helpers; the only hand-written twins |
|
|
46
|
+
|
|
47
|
+
**Golden rules**
|
|
48
|
+
|
|
49
|
+
1. **Never edit `_sync/` or `models/_generated.py`.** Edit `_async/` or the spec/generator, then run `make generate`. CI fails if the generated files are stale.
|
|
50
|
+
2. **Never write a URL path in a resource.** Add it to `ENDPOINTS` and call it by ID.
|
|
51
|
+
3. **Keep runtime code Python 3.8-safe.** Annotations can use `X | None` and `list[int]` because every module has `from __future__ import annotations`. Expressions evaluated at runtime must use `typing.List`, `typing.Union` and so on: `cast_to=`, type aliases, and everything in Pydantic models. `make test-all` catches mistakes.
|
|
52
|
+
4. **Keep the pure core pure.** Backoff, rate-limit math, parsing and validation belong in the shared modules, where they can be unit-tested without HTTP.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 3. Routine tasks
|
|
57
|
+
|
|
58
|
+
### 3.1 AgencyZoom updated its API spec
|
|
59
|
+
|
|
60
|
+
You'll usually find out from the **Spec drift** workflow, which checks the live spec every Monday and on every push, and fails (GitHub emails you) when it changes.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cp openapi/agencyzoom.yaml /tmp/old-spec.yaml # if you have the previous copy
|
|
64
|
+
make spec-update # download the new spec and pin its fingerprint
|
|
65
|
+
diff /tmp/old-spec.yaml openapi/agencyzoom.yaml | less # see what changed
|
|
66
|
+
make generate # regenerate models + sync client
|
|
67
|
+
make test
|
|
68
|
+
git add openapi/agencyzoom.yaml.sha256 src/ # commit the new pin and regenerated code, NOT the spec
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`tests/test_endpoint_coverage.py` then tells you exactly what's left to do:
|
|
72
|
+
|
|
73
|
+
- **"Spec endpoints missing from the registry"**: a new endpoint. See §3.2.
|
|
74
|
+
- **"Registry endpoints not in the spec"**: an endpoint was removed or renamed. Keep it if it still works (deprecate it in the CHANGELOG), or remove the method and its registry entry in a major release.
|
|
75
|
+
|
|
76
|
+
Read the changelog at the top of the spec (`info.description`) for behaviour changes such as new rate limits. Update `DEFAULT_RATE_LIMIT_PER_MINUTE` in `_config.py` if the limit changed.
|
|
77
|
+
|
|
78
|
+
### 3.2 Adding an endpoint
|
|
79
|
+
|
|
80
|
+
1. **Registry.** Add it to `ENDPOINTS` in `_endpoints.py`. The ID is the attribute path users will call, and the path is relative to `/v1/api`:
|
|
81
|
+
```python
|
|
82
|
+
"leads.archive": Endpoint("POST", "/leads/{lead_id}/archive"),
|
|
83
|
+
```
|
|
84
|
+
2. **Method.** Add the method in `_async/resources/<group>.py`, following the neighbouring methods:
|
|
85
|
+
```python
|
|
86
|
+
async def archive(self, lead_id: int, body: LeadArchiveRequest | Body | None = None, /, **fields: Any) -> GenericSuccessResponse:
|
|
87
|
+
"""Archive a lead. ``POST /leads/{leadId}/archive``"""
|
|
88
|
+
payload = build_body(LeadArchiveRequest, body, fields)
|
|
89
|
+
return await self._call("leads.archive", path_params={"lead_id": lead_id}, body=payload, cast_to=GenericSuccessResponse)
|
|
90
|
+
```
|
|
91
|
+
- The `path_params` keys must equal the `{placeholders}`; a test enforces this.
|
|
92
|
+
- Use `List[Model]` (not `list[Model]`) for `cast_to`.
|
|
93
|
+
- Paginated searches get a `paginate()` that calls `self._paginate("<same id>", ...)`.
|
|
94
|
+
3. **New resource group?** Create the class, export it from `_async/resources/__init__.py`, and add a `cached_property` on `AsyncAgencyZoom`.
|
|
95
|
+
4. Run `make generate && make check`.
|
|
96
|
+
5. Add a request-shaping test in `tests/test_resources.py`, and add the method to the README's API map and the CHANGELOG.
|
|
97
|
+
|
|
98
|
+
### 3.3 AgencyZoom ships a v2 API
|
|
99
|
+
|
|
100
|
+
Users can move without a release: `api_version="v2"`, `api_root=...`, or `endpoint_overrides=...`. To make v2 the SDK's default:
|
|
101
|
+
|
|
102
|
+
- **Everything moved:** change `DEFAULT_API_VERSION` in `_config.py`. That's a **major** release, because default behaviour changes.
|
|
103
|
+
- **Some endpoints moved:** registry paths are relative, so for an endpoint that lives only under v2, store an **absolute** path in the registry entry and teach `EndpointResolver.resolve` to skip prefixing paths that start with the version (or add a `version` field to `Endpoint`). Add a test in `tests/test_edge_cases.py::TestVersioning`.
|
|
104
|
+
- **Request or response shapes changed:** teach `scripts/fetch_spec.py` to download the v2 spec as `openapi/agencyzoom-v2.yaml` (git-ignored like v1, with its own `.sha256` pin), generate its models into a separate module (e.g. `models/v2.py`), and keep the v1 models for compatibility.
|
|
105
|
+
|
|
106
|
+
### 3.4 A model field has the wrong type
|
|
107
|
+
|
|
108
|
+
AgencyZoom sometimes returns data that contradicts its spec. The SDK already copes by setting the field to `None` and logging a warning, but if a field is *consistently* different, fix it at the source:
|
|
109
|
+
|
|
110
|
+
- Run `make smoke ARGS=--diagnose` against a real account. It prints each off-spec field with a masked shape of what was actually received (e.g. `str "9999-99-99"`), never real values.
|
|
111
|
+
- Add an entry to `FIELD_TYPE_OVERRIDES` in `scripts/generate_models.py` with the corrected annotation and a short note (shown in the field's docstring as "In practice: …"), then run `make generate`. Overrides survive spec downloads. The generator **fails** if an override no longer matches the spec, so a renamed field can't silently lose its fix.
|
|
112
|
+
- Add the observed shape to `test_real_world_shapes_parse_cleanly` in `tests/test_edge_cases.py`.
|
|
113
|
+
- Only override what you've actually observed. Don't "fix" similar-looking fields on a guess.
|
|
114
|
+
- Don't patch `openapi/agencyzoom.yaml`: it isn't committed, so a patch would be lost on the next download.
|
|
115
|
+
|
|
116
|
+
### 3.5 Updating dependencies
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
uv lock --upgrade # refresh uv.lock within the pyproject ranges
|
|
120
|
+
make test-all
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- The runtime ranges in `pyproject.toml` are deliberately wide (`httpx>=0.24`, `pydantic>=2.7`). CI's **lowest-deps** job tests the oldest allowed versions on Python 3.8, so raise a lower bound only if you need a newer feature.
|
|
124
|
+
- Dropping a Python version is a minor release at the earliest. Update `requires-python`, the classifiers, the CI matrix, the `PYTHONS` list in the Makefile, the README badge, and ruff's `target-version`.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 4. Testing
|
|
129
|
+
|
|
130
|
+
| Suite | What it guarantees |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `test_endpoint_coverage.py` | Registry == spec (117 endpoints); every ID is used; path params match placeholders; every ID maps to a public method |
|
|
133
|
+
| `test_auth.py` | Lazy login, refresh, 401 recovery, fallback to login, single-flight under concurrency |
|
|
134
|
+
| `test_transport.py` | Retry rules, backoff, `Retry-After`, error mapping, cache, headers |
|
|
135
|
+
| `test_resources.py` | Request shaping (snake/camel, model/dict), pagination, uploads, nested resources |
|
|
136
|
+
| `test_edge_cases.py` | Versioning and overrides, config validation, malformed responses, lifecycle, threads, event loops, rate limiting |
|
|
137
|
+
|
|
138
|
+
- HTTP is mocked with [respx](https://lundberg.github.io/respx/). Nothing touches the real API.
|
|
139
|
+
- Sleeps are patched (`conftest._no_sleep`), so the whole suite runs in about a second, and tests assert on the requested delays.
|
|
140
|
+
|
|
141
|
+
**Smoke-testing against the real API** before a release:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
make smoke # = uv run python scripts/smoke_test.py; prompts for email and password
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
It is **read-only** (searches and gets only), runs about 48 checks across every API group, and prints counts and field names only, so its output is safe to share. Exit code 0 means every call worked and every response matched the models. Any "Model mismatch warnings" point at fields to fix in the generator (§3.4) before releasing.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 5. Releasing
|
|
152
|
+
|
|
153
|
+
Versioning follows [SemVer](https://semver.org/):
|
|
154
|
+
|
|
155
|
+
- **Patch**: bug fixes.
|
|
156
|
+
- **Minor**: new endpoints or options.
|
|
157
|
+
- **Major**: removed methods, changed defaults, dropped Python versions.
|
|
158
|
+
|
|
159
|
+
1. Move the CHANGELOG's *Unreleased* items under the new version.
|
|
160
|
+
2. Bump `src/agencyzoom/_version.py`.
|
|
161
|
+
3. Run `make test-all && make check && make build`, then check that `dist/` contains one sdist and one wheel.
|
|
162
|
+
4. Commit and tag:
|
|
163
|
+
```bash
|
|
164
|
+
git commit -am "Release v0.2.0"
|
|
165
|
+
git tag v0.2.0 && git push origin main v0.2.0
|
|
166
|
+
```
|
|
167
|
+
5. `.github/workflows/release.yml` checks that the tag matches `_version.py`, runs the tests, builds, publishes to PyPI with **trusted publishing**, and creates a GitHub release.
|
|
168
|
+
|
|
169
|
+
**First-time PyPI setup**, once:
|
|
170
|
+
|
|
171
|
+
1. On PyPI, go to *Account settings* → *Publishing* → *Add a new pending publisher*. Enter PyPI project name `agencyzoom-sdk`, owner `iamjatinyadav`, repository `agencyzoom-python-sdk`, workflow `release.yml`, environment `pypi`. A pending publisher creates the project on the first successful upload.
|
|
172
|
+
2. In GitHub → *Settings* → *Environments*, create `pypi`. Optionally require a reviewer.
|
|
173
|
+
|
|
174
|
+
Without CI, `uv build && uv publish` also works (it asks for a PyPI token).
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 6. Legal hygiene (keep doing this)
|
|
179
|
+
|
|
180
|
+
This is an unofficial client for a proprietary product, so:
|
|
181
|
+
|
|
182
|
+
- **Never commit AgencyZoom's spec or other docs.** `.gitignore` blocks `openapi/*.yaml|yml|json`. Commit only the `.sha256` fingerprint. The spec is marked "Propriety license", and the repo history was rewritten once to remove a copy.
|
|
183
|
+
- **Keep "unofficial" and the "not affiliated with or endorsed by AgencyZoom or Vertafore" notice** at the top of the README, which is also the PyPI page.
|
|
184
|
+
- **Don't use their logos**, and don't word anything as if it came from AgencyZoom.
|
|
185
|
+
- **The governing terms** are the Vertafore Standard Terms (https://www.vertafore.com/terms, linked from agencyzoom.com). The `termsOfService` link inside the spec (`agencyzoom.com/tos`) returned 404 when checked on 2026-10-22.
|
|
186
|
+
- If AgencyZoom or Vertafore ever asks for a change (a rename, a takedown, or adopting the project), act on it promptly. The package can be renamed, or yanked on PyPI.
|
|
187
|
+
|
|
188
|
+
## 7. Sister SDKs
|
|
189
|
+
|
|
190
|
+
`agencyzoom-go-sdk/` and `agencyzoom-rust-sdk/` implement the same design: the same endpoint IDs, the same versioning options, and the same retry and auth rules.
|
|
191
|
+
|
|
192
|
+
When you change behaviour here, such as a retry rule or a new option, open matching issues in the other two so they stay in step. Each SDK vendors its own copy of the spec. Update all three together.
|