zoho 0.1.1__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.
- zoho-0.1.1/LICENSE +21 -0
- zoho-0.1.1/PKG-INFO +325 -0
- zoho-0.1.1/README.md +293 -0
- zoho-0.1.1/pyproject.toml +100 -0
- zoho-0.1.1/setup.cfg +4 -0
- zoho-0.1.1/src/zoho/__init__.py +37 -0
- zoho-0.1.1/src/zoho/client.py +638 -0
- zoho-0.1.1/src/zoho/connections.py +158 -0
- zoho-0.1.1/src/zoho/core/__init__.py +47 -0
- zoho-0.1.1/src/zoho/core/auth.py +172 -0
- zoho-0.1.1/src/zoho/core/cache.py +47 -0
- zoho-0.1.1/src/zoho/core/errors.py +74 -0
- zoho-0.1.1/src/zoho/core/logging.py +60 -0
- zoho-0.1.1/src/zoho/core/pagination.py +55 -0
- zoho-0.1.1/src/zoho/core/token_store.py +286 -0
- zoho-0.1.1/src/zoho/core/transport.py +194 -0
- zoho-0.1.1/src/zoho/creator/__init__.py +18 -0
- zoho-0.1.1/src/zoho/creator/client.py +93 -0
- zoho-0.1.1/src/zoho/creator/data.py +121 -0
- zoho-0.1.1/src/zoho/creator/generated/__init__.py +1 -0
- zoho-0.1.1/src/zoho/creator/meta.py +84 -0
- zoho-0.1.1/src/zoho/creator/models.py +37 -0
- zoho-0.1.1/src/zoho/creator/publish.py +110 -0
- zoho-0.1.1/src/zoho/crm/__init__.py +23 -0
- zoho-0.1.1/src/zoho/crm/client.py +150 -0
- zoho-0.1.1/src/zoho/crm/discovery.py +225 -0
- zoho-0.1.1/src/zoho/crm/generated/__init__.py +5 -0
- zoho-0.1.1/src/zoho/crm/models.py +82 -0
- zoho-0.1.1/src/zoho/crm/modules.py +50 -0
- zoho-0.1.1/src/zoho/crm/org.py +22 -0
- zoho-0.1.1/src/zoho/crm/records.py +136 -0
- zoho-0.1.1/src/zoho/crm/users.py +43 -0
- zoho-0.1.1/src/zoho/ingestion/__init__.py +18 -0
- zoho-0.1.1/src/zoho/ingestion/_common.py +13 -0
- zoho-0.1.1/src/zoho/ingestion/crm.py +204 -0
- zoho-0.1.1/src/zoho/ingestion/models.py +39 -0
- zoho-0.1.1/src/zoho/ingestion/people.py +77 -0
- zoho-0.1.1/src/zoho/ingestion/sheet.py +85 -0
- zoho-0.1.1/src/zoho/ingestion/workdrive.py +82 -0
- zoho-0.1.1/src/zoho/people/__init__.py +18 -0
- zoho-0.1.1/src/zoho/people/client.py +83 -0
- zoho-0.1.1/src/zoho/people/employees.py +84 -0
- zoho-0.1.1/src/zoho/people/files.py +82 -0
- zoho-0.1.1/src/zoho/people/forms.py +114 -0
- zoho-0.1.1/src/zoho/people/generated/__init__.py +1 -0
- zoho-0.1.1/src/zoho/people/models.py +43 -0
- zoho-0.1.1/src/zoho/projects/__init__.py +18 -0
- zoho-0.1.1/src/zoho/projects/client.py +101 -0
- zoho-0.1.1/src/zoho/projects/generated/__init__.py +1 -0
- zoho-0.1.1/src/zoho/projects/models.py +58 -0
- zoho-0.1.1/src/zoho/projects/portals.py +32 -0
- zoho-0.1.1/src/zoho/projects/projects.py +94 -0
- zoho-0.1.1/src/zoho/projects/tasks.py +109 -0
- zoho-0.1.1/src/zoho/py.typed +0 -0
- zoho-0.1.1/src/zoho/scripts/__init__.py +2 -0
- zoho-0.1.1/src/zoho/scripts/auth_cli.py +711 -0
- zoho-0.1.1/src/zoho/settings.py +91 -0
- zoho-0.1.1/src/zoho/sheet/__init__.py +18 -0
- zoho-0.1.1/src/zoho/sheet/client.py +83 -0
- zoho-0.1.1/src/zoho/sheet/generated/__init__.py +1 -0
- zoho-0.1.1/src/zoho/sheet/models.py +27 -0
- zoho-0.1.1/src/zoho/sheet/tabular.py +93 -0
- zoho-0.1.1/src/zoho/sheet/workbooks.py +61 -0
- zoho-0.1.1/src/zoho/sheet/worksheets.py +76 -0
- zoho-0.1.1/src/zoho/workdrive/__init__.py +18 -0
- zoho-0.1.1/src/zoho/workdrive/admin.py +48 -0
- zoho-0.1.1/src/zoho/workdrive/changes.py +40 -0
- zoho-0.1.1/src/zoho/workdrive/client.py +103 -0
- zoho-0.1.1/src/zoho/workdrive/files.py +57 -0
- zoho-0.1.1/src/zoho/workdrive/folders.py +73 -0
- zoho-0.1.1/src/zoho/workdrive/generated/__init__.py +1 -0
- zoho-0.1.1/src/zoho/workdrive/models.py +45 -0
- zoho-0.1.1/src/zoho/workdrive/search.py +43 -0
- zoho-0.1.1/src/zoho.egg-info/PKG-INFO +325 -0
- zoho-0.1.1/src/zoho.egg-info/SOURCES.txt +78 -0
- zoho-0.1.1/src/zoho.egg-info/dependency_links.txt +1 -0
- zoho-0.1.1/src/zoho.egg-info/entry_points.txt +2 -0
- zoho-0.1.1/src/zoho.egg-info/requires.txt +12 -0
- zoho-0.1.1/src/zoho.egg-info/top_level.txt +1 -0
- zoho-0.1.1/tests/test_client.py +112 -0
zoho-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 trisongz
|
|
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.
|
zoho-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zoho
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Async-first Zoho Python SDK focused on DX and performance
|
|
5
|
+
Author-email: Tri Nguyen <tnguyen@nu-devco.com>, AIUR <aiur@sparkenergy.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/SparkAIUR/zoho-sdk
|
|
8
|
+
Project-URL: Documentation, https://github.com/SparkAIUR/zoho-sdk
|
|
9
|
+
Project-URL: Repository, https://github.com/SparkAIUR/zoho-sdk
|
|
10
|
+
Project-URL: Issues, https://github.com/SparkAIUR/zoho-sdk/issues
|
|
11
|
+
Keywords: zoho,crm,sdk,async,api
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: httpx>=0.28.0
|
|
22
|
+
Requires-Dist: pydantic>=2.12.0
|
|
23
|
+
Requires-Dist: pydantic-settings>=2.11.0
|
|
24
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
25
|
+
Requires-Dist: structlog>=25.0.0
|
|
26
|
+
Requires-Dist: typer>=0.21.1
|
|
27
|
+
Provides-Extra: redis
|
|
28
|
+
Requires-Dist: redis>=6.0.0; extra == "redis"
|
|
29
|
+
Provides-Extra: orjson
|
|
30
|
+
Requires-Dist: orjson>=3.11.0; extra == "orjson"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# zoho
|
|
34
|
+
|
|
35
|
+
Async-first Python SDK for Zoho, designed for developer experience and performance.
|
|
36
|
+
|
|
37
|
+
## Highlights
|
|
38
|
+
|
|
39
|
+
- Async-first transport built on `httpx`
|
|
40
|
+
- Explicit credential-first initialization (`from_credentials`)
|
|
41
|
+
- Strong typing with `pydantic` / `pydantic-settings`
|
|
42
|
+
- Pluggable token stores (memory, SQLite, Redis)
|
|
43
|
+
- Structlog-powered logging (`pretty` or `json`)
|
|
44
|
+
- Multi-account connection manager (`client.connections`)
|
|
45
|
+
- Product clients:
|
|
46
|
+
- CRM (`records`, `modules`, `org`, `users`, `dynamic`)
|
|
47
|
+
- Creator (`meta`, `data`, `publish`)
|
|
48
|
+
- Projects V3 (`portals`, `projects`, `tasks`)
|
|
49
|
+
- People (`forms`, `employees`, `files`)
|
|
50
|
+
- Sheet (`workbooks`, `worksheets`, `tabular`)
|
|
51
|
+
- WorkDrive (`files`, `folders`, `search`, `changes`, `admin`)
|
|
52
|
+
- Ingestion iterators for connector workloads (`zoho.ingestion`)
|
|
53
|
+
- Codegen tooling + golden tests for spec drift
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uv add zoho
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Optional extras:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
uv add "zoho[redis]" # Redis token store
|
|
65
|
+
uv add "zoho[orjson]" # Faster JSON usage patterns
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick Start (Explicit Credentials)
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from zoho import Zoho
|
|
72
|
+
|
|
73
|
+
async def main() -> None:
|
|
74
|
+
async with Zoho.from_credentials(
|
|
75
|
+
client_id="your_client_id",
|
|
76
|
+
client_secret="your_client_secret",
|
|
77
|
+
refresh_token="your_refresh_token",
|
|
78
|
+
dc="US",
|
|
79
|
+
environment="production",
|
|
80
|
+
) as client:
|
|
81
|
+
lead = await client.crm.records.get(module="Leads", record_id="123456789")
|
|
82
|
+
print(lead.id)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Client Lifecycle: Context Manager vs Singleton
|
|
86
|
+
|
|
87
|
+
Both patterns are supported.
|
|
88
|
+
|
|
89
|
+
Use `async with` for one-shot scripts/jobs:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
async with Zoho.from_credentials(
|
|
93
|
+
client_id="...",
|
|
94
|
+
client_secret="...",
|
|
95
|
+
refresh_token="...",
|
|
96
|
+
) as client:
|
|
97
|
+
org = await client.crm.org.get()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use a long-lived singleton for web apps/workers and close on shutdown:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
zoho_client = Zoho.from_credentials(
|
|
104
|
+
client_id="...",
|
|
105
|
+
client_secret="...",
|
|
106
|
+
refresh_token="...",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
project_rows = await zoho_client.projects.projects.list(portal_id="12345678")
|
|
110
|
+
|
|
111
|
+
# shutdown hook
|
|
112
|
+
await zoho_client.close()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
After `close()`, `zoho_client.closed` is `True` and that instance must not be reused.
|
|
116
|
+
|
|
117
|
+
## Multi-Account Connections
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from zoho import Zoho, ZohoConnectionProfile
|
|
121
|
+
|
|
122
|
+
client = Zoho.from_credentials(
|
|
123
|
+
client_id="primary_client_id",
|
|
124
|
+
client_secret="primary_client_secret",
|
|
125
|
+
refresh_token="primary_refresh_token",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
client.register_connection(
|
|
129
|
+
ZohoConnectionProfile(
|
|
130
|
+
name="tenant_b",
|
|
131
|
+
client_id="tenant_b_client_id",
|
|
132
|
+
client_secret="tenant_b_client_secret",
|
|
133
|
+
refresh_token="tenant_b_refresh_token",
|
|
134
|
+
dc="EU",
|
|
135
|
+
token_store_backend="sqlite",
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
tenant_b = client.for_connection("tenant_b")
|
|
140
|
+
forms = await tenant_b.people.forms.list_forms()
|
|
141
|
+
print(forms.result_rows)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Product Usage Examples
|
|
145
|
+
|
|
146
|
+
### People
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
records = await client.people.forms.list_records(
|
|
150
|
+
form_link_name="employee",
|
|
151
|
+
limit=200,
|
|
152
|
+
)
|
|
153
|
+
print(records.result_rows)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Sheet
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
rows = await client.sheet.tabular.fetch_worksheet_records(
|
|
160
|
+
workbook_id="workbook_123",
|
|
161
|
+
worksheet_name="Data",
|
|
162
|
+
limit=500,
|
|
163
|
+
)
|
|
164
|
+
print(rows.records)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### WorkDrive
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
changes = await client.workdrive.changes.list_recent(
|
|
171
|
+
folder_id="folder_123",
|
|
172
|
+
limit=200,
|
|
173
|
+
)
|
|
174
|
+
print(changes.resources)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### CRM Dynamic Discovery
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
if await client.crm.dynamic.has_module("Leads"):
|
|
181
|
+
leads = client.crm.dynamic.Leads
|
|
182
|
+
rows = await leads.list(page=1, per_page=200)
|
|
183
|
+
print(rows.data)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Ingestion Helpers (`pipeshub-ai`-friendly)
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from zoho.ingestion import iter_people_form_documents
|
|
190
|
+
|
|
191
|
+
async for batch in iter_people_form_documents(
|
|
192
|
+
client,
|
|
193
|
+
form_link_name="employee",
|
|
194
|
+
connection_name="tenant_b",
|
|
195
|
+
page_size=200,
|
|
196
|
+
):
|
|
197
|
+
for doc in batch.documents:
|
|
198
|
+
print(doc.id, doc.title)
|
|
199
|
+
print(batch.checkpoint)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Additional iterators:
|
|
203
|
+
- `iter_crm_module_documents(...)`
|
|
204
|
+
- `iter_crm_documents(...)`
|
|
205
|
+
- `iter_sheet_worksheet_documents(...)`
|
|
206
|
+
- `iter_workdrive_recent_documents(...)`
|
|
207
|
+
|
|
208
|
+
## Getting OAuth Credentials
|
|
209
|
+
|
|
210
|
+
If you still need OAuth credentials, follow:
|
|
211
|
+
- `docs/auth-credentials.md`
|
|
212
|
+
- `docs/scopes.md`
|
|
213
|
+
|
|
214
|
+
At a high level:
|
|
215
|
+
1. Create a client in Zoho API Console.
|
|
216
|
+
2. Generate grant code(s) with required product scopes.
|
|
217
|
+
3. Exchange grant code for access/refresh tokens.
|
|
218
|
+
4. Use matching `dc` and accounts domain.
|
|
219
|
+
|
|
220
|
+
## Auth Helper CLI
|
|
221
|
+
|
|
222
|
+
Use the helper command for token exchange and self-client payload generation:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
export ZOHO_CREDENTIALS_FILE=refs/notes/zoho-live.env
|
|
226
|
+
uv run zoho-auth exchange-token --grant-code "<grant-code>"
|
|
227
|
+
|
|
228
|
+
uv run zoho-auth grant-code \
|
|
229
|
+
--self-client-id "1000..." \
|
|
230
|
+
--scopes "ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.users.ALL,ZohoCRM.org.ALL"
|
|
231
|
+
|
|
232
|
+
uv run zoho-auth scope-builder \
|
|
233
|
+
--product CRM \
|
|
234
|
+
--product WorkDrive \
|
|
235
|
+
--access read \
|
|
236
|
+
--format env
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
See `docs/auth-cli.md` for execute mode and header/cookie options.
|
|
240
|
+
|
|
241
|
+
## Environment-Based Setup (Convenience)
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
export ZOHO_CLIENT_ID="..."
|
|
245
|
+
export ZOHO_CLIENT_SECRET="..."
|
|
246
|
+
export ZOHO_REFRESH_TOKEN="..."
|
|
247
|
+
export ZOHO_DC="US"
|
|
248
|
+
export ZOHO_ENVIRONMENT="production"
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from zoho import Zoho
|
|
253
|
+
|
|
254
|
+
async with Zoho.from_env() as client:
|
|
255
|
+
org = await client.crm.org.get()
|
|
256
|
+
print(org)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Live Credential Validation (Admin)
|
|
260
|
+
|
|
261
|
+
Use the read-only validator before production rollout:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
export ZOHO_CREDENTIALS_FILE=refs/notes/zoho-live.env
|
|
265
|
+
uv sync --group dev
|
|
266
|
+
uv run python tools/admin_validate_live.py
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The script only runs read-oriented product checks and prints non-sensitive summaries
|
|
270
|
+
(counts/status only). See `docs/admin-live-validation.md` for required/optional vars.
|
|
271
|
+
|
|
272
|
+
## Development
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
uv sync --group dev
|
|
276
|
+
uv run ruff format .
|
|
277
|
+
uv run ruff check .
|
|
278
|
+
uv run mypy
|
|
279
|
+
uv run pytest
|
|
280
|
+
uv run mkdocs build --strict
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Codegen Workflows
|
|
284
|
+
|
|
285
|
+
### CRM summary
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
uv run python tools/codegen/main.py \
|
|
289
|
+
--json-details tests/fixtures/json_details_minimal.json \
|
|
290
|
+
--openapi tests/fixtures/openapi_minimal.json \
|
|
291
|
+
--output /tmp/zoho_ir_summary.json
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Creator summary
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
uv run python tools/codegen/creator_summary.py \
|
|
298
|
+
--openapi tests/fixtures/creator_openapi_minimal.json \
|
|
299
|
+
--output /tmp/creator_summary.json
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Projects extraction
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
uv run python tools/codegen/projects_extract.py \
|
|
306
|
+
--html tests/fixtures/projects/api_docs_sample.html \
|
|
307
|
+
--output /tmp/projects_mvp.json
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Curated product specs summary (People/Sheet/WorkDrive)
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
uv run python tools/codegen/curated_summary.py \
|
|
314
|
+
--spec tools/specs/people_v1_curated.json \
|
|
315
|
+
--spec tools/specs/sheet_v2_curated.json \
|
|
316
|
+
--spec tools/specs/workdrive_v1_curated.json \
|
|
317
|
+
--output /tmp/curated_summary.json
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Repository Docs
|
|
321
|
+
|
|
322
|
+
- Product docs: `docs/`
|
|
323
|
+
- API research notes: `refs/apis/`
|
|
324
|
+
- Design specs: `refs/docs/specs/`
|
|
325
|
+
- Contributor guide: `AGENTS.md`
|
zoho-0.1.1/README.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# zoho
|
|
2
|
+
|
|
3
|
+
Async-first Python SDK for Zoho, designed for developer experience and performance.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- Async-first transport built on `httpx`
|
|
8
|
+
- Explicit credential-first initialization (`from_credentials`)
|
|
9
|
+
- Strong typing with `pydantic` / `pydantic-settings`
|
|
10
|
+
- Pluggable token stores (memory, SQLite, Redis)
|
|
11
|
+
- Structlog-powered logging (`pretty` or `json`)
|
|
12
|
+
- Multi-account connection manager (`client.connections`)
|
|
13
|
+
- Product clients:
|
|
14
|
+
- CRM (`records`, `modules`, `org`, `users`, `dynamic`)
|
|
15
|
+
- Creator (`meta`, `data`, `publish`)
|
|
16
|
+
- Projects V3 (`portals`, `projects`, `tasks`)
|
|
17
|
+
- People (`forms`, `employees`, `files`)
|
|
18
|
+
- Sheet (`workbooks`, `worksheets`, `tabular`)
|
|
19
|
+
- WorkDrive (`files`, `folders`, `search`, `changes`, `admin`)
|
|
20
|
+
- Ingestion iterators for connector workloads (`zoho.ingestion`)
|
|
21
|
+
- Codegen tooling + golden tests for spec drift
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv add zoho
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Optional extras:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv add "zoho[redis]" # Redis token store
|
|
33
|
+
uv add "zoho[orjson]" # Faster JSON usage patterns
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start (Explicit Credentials)
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from zoho import Zoho
|
|
40
|
+
|
|
41
|
+
async def main() -> None:
|
|
42
|
+
async with Zoho.from_credentials(
|
|
43
|
+
client_id="your_client_id",
|
|
44
|
+
client_secret="your_client_secret",
|
|
45
|
+
refresh_token="your_refresh_token",
|
|
46
|
+
dc="US",
|
|
47
|
+
environment="production",
|
|
48
|
+
) as client:
|
|
49
|
+
lead = await client.crm.records.get(module="Leads", record_id="123456789")
|
|
50
|
+
print(lead.id)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Client Lifecycle: Context Manager vs Singleton
|
|
54
|
+
|
|
55
|
+
Both patterns are supported.
|
|
56
|
+
|
|
57
|
+
Use `async with` for one-shot scripts/jobs:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
async with Zoho.from_credentials(
|
|
61
|
+
client_id="...",
|
|
62
|
+
client_secret="...",
|
|
63
|
+
refresh_token="...",
|
|
64
|
+
) as client:
|
|
65
|
+
org = await client.crm.org.get()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use a long-lived singleton for web apps/workers and close on shutdown:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
zoho_client = Zoho.from_credentials(
|
|
72
|
+
client_id="...",
|
|
73
|
+
client_secret="...",
|
|
74
|
+
refresh_token="...",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
project_rows = await zoho_client.projects.projects.list(portal_id="12345678")
|
|
78
|
+
|
|
79
|
+
# shutdown hook
|
|
80
|
+
await zoho_client.close()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
After `close()`, `zoho_client.closed` is `True` and that instance must not be reused.
|
|
84
|
+
|
|
85
|
+
## Multi-Account Connections
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from zoho import Zoho, ZohoConnectionProfile
|
|
89
|
+
|
|
90
|
+
client = Zoho.from_credentials(
|
|
91
|
+
client_id="primary_client_id",
|
|
92
|
+
client_secret="primary_client_secret",
|
|
93
|
+
refresh_token="primary_refresh_token",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
client.register_connection(
|
|
97
|
+
ZohoConnectionProfile(
|
|
98
|
+
name="tenant_b",
|
|
99
|
+
client_id="tenant_b_client_id",
|
|
100
|
+
client_secret="tenant_b_client_secret",
|
|
101
|
+
refresh_token="tenant_b_refresh_token",
|
|
102
|
+
dc="EU",
|
|
103
|
+
token_store_backend="sqlite",
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
tenant_b = client.for_connection("tenant_b")
|
|
108
|
+
forms = await tenant_b.people.forms.list_forms()
|
|
109
|
+
print(forms.result_rows)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Product Usage Examples
|
|
113
|
+
|
|
114
|
+
### People
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
records = await client.people.forms.list_records(
|
|
118
|
+
form_link_name="employee",
|
|
119
|
+
limit=200,
|
|
120
|
+
)
|
|
121
|
+
print(records.result_rows)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Sheet
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
rows = await client.sheet.tabular.fetch_worksheet_records(
|
|
128
|
+
workbook_id="workbook_123",
|
|
129
|
+
worksheet_name="Data",
|
|
130
|
+
limit=500,
|
|
131
|
+
)
|
|
132
|
+
print(rows.records)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### WorkDrive
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
changes = await client.workdrive.changes.list_recent(
|
|
139
|
+
folder_id="folder_123",
|
|
140
|
+
limit=200,
|
|
141
|
+
)
|
|
142
|
+
print(changes.resources)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### CRM Dynamic Discovery
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
if await client.crm.dynamic.has_module("Leads"):
|
|
149
|
+
leads = client.crm.dynamic.Leads
|
|
150
|
+
rows = await leads.list(page=1, per_page=200)
|
|
151
|
+
print(rows.data)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Ingestion Helpers (`pipeshub-ai`-friendly)
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from zoho.ingestion import iter_people_form_documents
|
|
158
|
+
|
|
159
|
+
async for batch in iter_people_form_documents(
|
|
160
|
+
client,
|
|
161
|
+
form_link_name="employee",
|
|
162
|
+
connection_name="tenant_b",
|
|
163
|
+
page_size=200,
|
|
164
|
+
):
|
|
165
|
+
for doc in batch.documents:
|
|
166
|
+
print(doc.id, doc.title)
|
|
167
|
+
print(batch.checkpoint)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Additional iterators:
|
|
171
|
+
- `iter_crm_module_documents(...)`
|
|
172
|
+
- `iter_crm_documents(...)`
|
|
173
|
+
- `iter_sheet_worksheet_documents(...)`
|
|
174
|
+
- `iter_workdrive_recent_documents(...)`
|
|
175
|
+
|
|
176
|
+
## Getting OAuth Credentials
|
|
177
|
+
|
|
178
|
+
If you still need OAuth credentials, follow:
|
|
179
|
+
- `docs/auth-credentials.md`
|
|
180
|
+
- `docs/scopes.md`
|
|
181
|
+
|
|
182
|
+
At a high level:
|
|
183
|
+
1. Create a client in Zoho API Console.
|
|
184
|
+
2. Generate grant code(s) with required product scopes.
|
|
185
|
+
3. Exchange grant code for access/refresh tokens.
|
|
186
|
+
4. Use matching `dc` and accounts domain.
|
|
187
|
+
|
|
188
|
+
## Auth Helper CLI
|
|
189
|
+
|
|
190
|
+
Use the helper command for token exchange and self-client payload generation:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
export ZOHO_CREDENTIALS_FILE=refs/notes/zoho-live.env
|
|
194
|
+
uv run zoho-auth exchange-token --grant-code "<grant-code>"
|
|
195
|
+
|
|
196
|
+
uv run zoho-auth grant-code \
|
|
197
|
+
--self-client-id "1000..." \
|
|
198
|
+
--scopes "ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.users.ALL,ZohoCRM.org.ALL"
|
|
199
|
+
|
|
200
|
+
uv run zoho-auth scope-builder \
|
|
201
|
+
--product CRM \
|
|
202
|
+
--product WorkDrive \
|
|
203
|
+
--access read \
|
|
204
|
+
--format env
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
See `docs/auth-cli.md` for execute mode and header/cookie options.
|
|
208
|
+
|
|
209
|
+
## Environment-Based Setup (Convenience)
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
export ZOHO_CLIENT_ID="..."
|
|
213
|
+
export ZOHO_CLIENT_SECRET="..."
|
|
214
|
+
export ZOHO_REFRESH_TOKEN="..."
|
|
215
|
+
export ZOHO_DC="US"
|
|
216
|
+
export ZOHO_ENVIRONMENT="production"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from zoho import Zoho
|
|
221
|
+
|
|
222
|
+
async with Zoho.from_env() as client:
|
|
223
|
+
org = await client.crm.org.get()
|
|
224
|
+
print(org)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Live Credential Validation (Admin)
|
|
228
|
+
|
|
229
|
+
Use the read-only validator before production rollout:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
export ZOHO_CREDENTIALS_FILE=refs/notes/zoho-live.env
|
|
233
|
+
uv sync --group dev
|
|
234
|
+
uv run python tools/admin_validate_live.py
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The script only runs read-oriented product checks and prints non-sensitive summaries
|
|
238
|
+
(counts/status only). See `docs/admin-live-validation.md` for required/optional vars.
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
uv sync --group dev
|
|
244
|
+
uv run ruff format .
|
|
245
|
+
uv run ruff check .
|
|
246
|
+
uv run mypy
|
|
247
|
+
uv run pytest
|
|
248
|
+
uv run mkdocs build --strict
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Codegen Workflows
|
|
252
|
+
|
|
253
|
+
### CRM summary
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
uv run python tools/codegen/main.py \
|
|
257
|
+
--json-details tests/fixtures/json_details_minimal.json \
|
|
258
|
+
--openapi tests/fixtures/openapi_minimal.json \
|
|
259
|
+
--output /tmp/zoho_ir_summary.json
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Creator summary
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
uv run python tools/codegen/creator_summary.py \
|
|
266
|
+
--openapi tests/fixtures/creator_openapi_minimal.json \
|
|
267
|
+
--output /tmp/creator_summary.json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Projects extraction
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
uv run python tools/codegen/projects_extract.py \
|
|
274
|
+
--html tests/fixtures/projects/api_docs_sample.html \
|
|
275
|
+
--output /tmp/projects_mvp.json
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Curated product specs summary (People/Sheet/WorkDrive)
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
uv run python tools/codegen/curated_summary.py \
|
|
282
|
+
--spec tools/specs/people_v1_curated.json \
|
|
283
|
+
--spec tools/specs/sheet_v2_curated.json \
|
|
284
|
+
--spec tools/specs/workdrive_v1_curated.json \
|
|
285
|
+
--output /tmp/curated_summary.json
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Repository Docs
|
|
289
|
+
|
|
290
|
+
- Product docs: `docs/`
|
|
291
|
+
- API research notes: `refs/apis/`
|
|
292
|
+
- Design specs: `refs/docs/specs/`
|
|
293
|
+
- Contributor guide: `AGENTS.md`
|