@dereekb/zoho 13.0.6 → 13.0.7

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 (56) hide show
  1. package/index.cjs.js +2164 -231
  2. package/index.esm.js +2143 -232
  3. package/nestjs/LICENSE +21 -0
  4. package/nestjs/README.md +11 -0
  5. package/nestjs/docs/configuration.md +165 -0
  6. package/nestjs/docs/crm-getting-started.md +296 -0
  7. package/nestjs/index.cjs.js +653 -5
  8. package/nestjs/index.esm.js +647 -7
  9. package/nestjs/package.json +3 -3
  10. package/nestjs/src/lib/crm/crm.api.d.ts +46 -0
  11. package/nestjs/src/lib/crm/crm.config.d.ts +16 -1
  12. package/nestjs/src/lib/crm/crm.module.d.ts +77 -8
  13. package/nestjs/src/lib/index.d.ts +2 -0
  14. package/nestjs/src/lib/recruit/recruit.api.d.ts +50 -0
  15. package/nestjs/src/lib/recruit/recruit.config.d.ts +16 -1
  16. package/nestjs/src/lib/recruit/recruit.module.d.ts +77 -8
  17. package/nestjs/src/lib/sign/index.d.ts +3 -0
  18. package/nestjs/src/lib/sign/sign.api.d.ts +54 -0
  19. package/nestjs/src/lib/sign/sign.config.d.ts +10 -0
  20. package/nestjs/src/lib/sign/sign.module.d.ts +94 -0
  21. package/package.json +2 -2
  22. package/src/lib/accounts/accounts.api.d.ts +149 -3
  23. package/src/lib/accounts/accounts.factory.d.ts +73 -6
  24. package/src/lib/crm/crm.api.d.ts +599 -62
  25. package/src/lib/crm/crm.api.notes.d.ts +46 -3
  26. package/src/lib/crm/crm.api.tags.d.ts +65 -2
  27. package/src/lib/crm/crm.config.d.ts +30 -0
  28. package/src/lib/crm/crm.criteria.d.ts +60 -3
  29. package/src/lib/crm/crm.error.api.d.ts +42 -0
  30. package/src/lib/crm/crm.factory.d.ts +39 -3
  31. package/src/lib/crm/crm.notes.d.ts +36 -0
  32. package/src/lib/crm/crm.tags.d.ts +3 -0
  33. package/src/lib/index.d.ts +1 -0
  34. package/src/lib/recruit/recruit.api.candidates.d.ts +44 -3
  35. package/src/lib/recruit/recruit.api.d.ts +719 -57
  36. package/src/lib/recruit/recruit.api.notes.d.ts +140 -0
  37. package/src/lib/recruit/recruit.api.tags.d.ts +122 -14
  38. package/src/lib/recruit/recruit.config.d.ts +30 -0
  39. package/src/lib/recruit/recruit.criteria.d.ts +55 -3
  40. package/src/lib/recruit/recruit.error.api.d.ts +39 -0
  41. package/src/lib/recruit/recruit.factory.d.ts +39 -3
  42. package/src/lib/recruit/recruit.notes.d.ts +21 -0
  43. package/src/lib/recruit/recruit.tags.d.ts +3 -0
  44. package/src/lib/shared/criteria.d.ts +95 -11
  45. package/src/lib/shared/criteria.util.d.ts +19 -4
  46. package/src/lib/sign/index.d.ts +6 -0
  47. package/src/lib/sign/sign.api.d.ts +397 -0
  48. package/src/lib/sign/sign.api.page.d.ts +109 -0
  49. package/src/lib/sign/sign.config.d.ts +24 -0
  50. package/src/lib/sign/sign.d.ts +225 -0
  51. package/src/lib/sign/sign.error.api.d.ts +7 -0
  52. package/src/lib/sign/sign.factory.d.ts +58 -0
  53. package/src/lib/zoho.api.page.d.ts +41 -10
  54. package/src/lib/zoho.config.d.ts +24 -9
  55. package/src/lib/zoho.limit.d.ts +41 -9
  56. package/src/lib/zoho.type.d.ts +24 -8
package/nestjs/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hapier Creative LLC.
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,11 @@
1
+ # zoho-nestjs
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build zoho-nestjs` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test zoho-nestjs` to execute the unit tests via [Jest](https://jestjs.io).
@@ -0,0 +1,165 @@
1
+ # Zoho NestJS Configuration
2
+
3
+ This document describes the environment variables used by `@dereekb/zoho/nestjs` and shows how to wire up the Zoho CRM and Zoho Recruit modules in a NestJS application.
4
+
5
+ ---
6
+
7
+ ## Environment Variables
8
+
9
+ Configuration is read from a NestJS `ConfigService` (typically backed by process environment variables). Two naming conventions are supported for every credential: a **shared** variable that applies to all services, and a **service-specific** variable that takes precedence when present.
10
+
11
+ ### Variable Naming Convention
12
+
13
+ The service-specific prefix is `ZOHO_<SERVICE>_`, where `<SERVICE>` is the uppercase `serviceAccessTokenKey` (e.g. `CRM` or `RECRUIT`). For each credential the lookup order is:
14
+
15
+ 1. `ZOHO_<SERVICE>_<KEY>` – service-specific (highest priority)
16
+ 2. `ZOHO_<KEY>` – shared fallback
17
+
18
+ ### Zoho Accounts (OAuth) Variables
19
+
20
+ These credentials are shared across all Zoho services and are used to obtain access tokens via the Zoho Accounts API.
21
+
22
+ | Variable | Service-specific (CRM) | Service-specific (Recruit) | Description |
23
+ |---|---|---|---|
24
+ | `ZOHO_ACCOUNTS_URL` | `ZOHO_CRM_ACCOUNTS_URL` | `ZOHO_RECRUIT_ACCOUNTS_URL` | Base URL of the Zoho Accounts API (e.g. `https://accounts.zoho.com`) |
25
+ | `ZOHO_ACCOUNTS_REFRESH_TOKEN` | `ZOHO_CRM_ACCOUNTS_REFRESH_TOKEN` | `ZOHO_RECRUIT_ACCOUNTS_REFRESH_TOKEN` | OAuth refresh token for the service client |
26
+ | `ZOHO_ACCOUNTS_CLIENT_ID` | `ZOHO_CRM_ACCOUNTS_CLIENT_ID` | `ZOHO_RECRUIT_ACCOUNTS_CLIENT_ID` | OAuth client ID for the service client |
27
+ | `ZOHO_ACCOUNTS_CLIENT_SECRET` | `ZOHO_CRM_ACCOUNTS_CLIENT_SECRET` | `ZOHO_RECRUIT_ACCOUNTS_CLIENT_SECRET` | OAuth client secret for the service client |
28
+
29
+ ### Zoho API URL Variables
30
+
31
+ Each service also needs to know the base URL of its own REST API.
32
+
33
+ | Variable | Description |
34
+ |---|---|
35
+ | `ZOHO_CRM_API_URL` | Base URL of the Zoho CRM API (e.g. `https://www.zohoapis.com/crm/v2`) |
36
+ | `ZOHO_RECRUIT_API_URL` | Base URL of the Zoho Recruit API (e.g. `https://recruit.zoho.com/recruit/v2`) |
37
+
38
+ > **Note:** The shared fallback `ZOHO_API_URL` can also be used if both services share the same base URL.
39
+
40
+ ---
41
+
42
+ ## Setting Up Zoho CRM
43
+
44
+ `appZohoCrmModuleMetadata()` generates the NestJS `ModuleMetadata` needed to bootstrap the Zoho CRM integration. It registers `ZohoCrmApi` and `ZohoAccountsApi` as providers and reads credentials from the environment automatically.
45
+
46
+ ### Required environment variables
47
+
48
+ ```
49
+ ZOHO_ACCOUNTS_URL=https://accounts.zoho.com
50
+ ZOHO_ACCOUNTS_REFRESH_TOKEN=<your-refresh-token>
51
+ ZOHO_ACCOUNTS_CLIENT_ID=<your-client-id>
52
+ ZOHO_ACCOUNTS_CLIENT_SECRET=<your-client-secret>
53
+ ZOHO_CRM_API_URL=https://www.zohoapis.com/crm/v2
54
+ ```
55
+
56
+ ### Example
57
+
58
+ ```typescript
59
+ import {
60
+ ZohoAccountsAccessTokenCacheService,
61
+ appZohoCrmModuleMetadata
62
+ } from '@dereekb/zoho/nestjs';
63
+ import { Module } from '@nestjs/common';
64
+
65
+ // 1. Create a dependency module that provides ZohoAccountsAccessTokenCacheService.
66
+ // Here we use a simple in-memory cache. For production, consider also layering
67
+ // in a file-based or Redis-backed cache.
68
+ @Module({
69
+ providers: [
70
+ {
71
+ provide: ZohoAccountsAccessTokenCacheService,
72
+ useFactory: () => memoryZohoAccountsAccessTokenCacheService()
73
+ }
74
+ ],
75
+ exports: [ZohoAccountsAccessTokenCacheService]
76
+ })
77
+ export class AppZohoCrmDependencyModule {}
78
+
79
+ // 2. Declare the CRM module using the helper. Pass the dependency module so that
80
+ // ZohoAccountsAccessTokenCacheService is available to the internal providers.
81
+ @Module(appZohoCrmModuleMetadata({ dependencyModule: AppZohoCrmDependencyModule }))
82
+ export class AppZohoCrmModule {}
83
+ ```
84
+
85
+ `ZohoCrmApi` is exported by `AppZohoCrmModule` and can be injected into any service that imports it.
86
+
87
+ ---
88
+
89
+ ## Setting Up Zoho Recruit
90
+
91
+ `appZohoRecruitModuleMetadata()` mirrors the CRM helper for the Zoho Recruit API.
92
+
93
+ ### Required environment variables
94
+
95
+ ```
96
+ ZOHO_ACCOUNTS_URL=https://accounts.zoho.com
97
+ ZOHO_ACCOUNTS_REFRESH_TOKEN=<your-refresh-token>
98
+ ZOHO_ACCOUNTS_CLIENT_ID=<your-client-id>
99
+ ZOHO_ACCOUNTS_CLIENT_SECRET=<your-client-secret>
100
+ ZOHO_RECRUIT_API_URL=https://recruit.zoho.com/recruit/v2
101
+ ```
102
+
103
+ Or, if Recruit uses its own dedicated OAuth client, use the service-specific variables:
104
+
105
+ ```
106
+ ZOHO_RECRUIT_ACCOUNTS_URL=https://accounts.zoho.com
107
+ ZOHO_RECRUIT_ACCOUNTS_REFRESH_TOKEN=<recruit-refresh-token>
108
+ ZOHO_RECRUIT_ACCOUNTS_CLIENT_ID=<recruit-client-id>
109
+ ZOHO_RECRUIT_ACCOUNTS_CLIENT_SECRET=<recruit-client-secret>
110
+ ZOHO_RECRUIT_API_URL=https://recruit.zoho.com/recruit/v2
111
+ ```
112
+
113
+ ### Example
114
+
115
+ ```typescript
116
+ import {
117
+ ZohoAccountsAccessTokenCacheService,
118
+ appZohoRecruitModuleMetadata
119
+ } from '@dereekb/zoho/nestjs';
120
+ import { Module } from '@nestjs/common';
121
+
122
+ @Module({
123
+ providers: [
124
+ {
125
+ provide: ZohoAccountsAccessTokenCacheService,
126
+ useFactory: () => memoryZohoAccountsAccessTokenCacheService()
127
+ }
128
+ ],
129
+ exports: [ZohoAccountsAccessTokenCacheService]
130
+ })
131
+ export class AppZohoRecruitDependencyModule {}
132
+
133
+ @Module(appZohoRecruitModuleMetadata({ dependencyModule: AppZohoRecruitDependencyModule }))
134
+ export class AppZohoRecruitModule {}
135
+ ```
136
+
137
+ `ZohoRecruitApi` is exported by `AppZohoRecruitModule` and can be injected into any service that imports it.
138
+
139
+ ---
140
+
141
+ ## Using Both CRM and Recruit Together
142
+
143
+ If your application uses both APIs, you can share or separate the token cache depending on whether the same OAuth client is used:
144
+
145
+ ```typescript
146
+ // Shared dependency module (single OAuth client for both services)
147
+ @Module({
148
+ providers: [
149
+ {
150
+ provide: ZohoAccountsAccessTokenCacheService,
151
+ useFactory: () => memoryZohoAccountsAccessTokenCacheService()
152
+ }
153
+ ],
154
+ exports: [ZohoAccountsAccessTokenCacheService]
155
+ })
156
+ export class AppZohoDependencyModule {}
157
+
158
+ @Module(appZohoCrmModuleMetadata({ dependencyModule: AppZohoDependencyModule }))
159
+ export class AppZohoCrmModule {}
160
+
161
+ @Module(appZohoRecruitModuleMetadata({ dependencyModule: AppZohoDependencyModule }))
162
+ export class AppZohoRecruitModule {}
163
+ ```
164
+
165
+ When different OAuth clients are used for CRM and Recruit, set the service-specific environment variables (e.g. `ZOHO_CRM_ACCOUNTS_CLIENT_ID` vs `ZOHO_RECRUIT_ACCOUNTS_CLIENT_ID`) and use separate dependency modules with their own cache instances.
@@ -0,0 +1,296 @@
1
+ # Zoho CRM - Getting Started with NestJS
2
+
3
+ This guide walks through setting up Zoho CRM in a new NestJS project using `@dereekb/zoho` and `@dereekb/zoho-nestjs`.
4
+
5
+ ## 1. Install Dependencies
6
+
7
+ ```bash
8
+ npm install @dereekb/zoho @dereekb/zoho-nestjs
9
+ ```
10
+
11
+ ## 2. Module & Authentication Setup
12
+
13
+ The NestJS integration wires up OAuth authentication, token caching, rate limiting, and the CRM client in a single module declaration. All you need to provide is:
14
+
15
+ - Environment variables for OAuth credentials and API URLs
16
+ - A `ZohoAccountsAccessTokenCacheService` implementation for token persistence
17
+
18
+ ### Environment Variables
19
+
20
+ ```env
21
+ # OAuth credentials (shared across all Zoho services)
22
+ ZOHO_ACCOUNTS_URL=https://accounts.zoho.com
23
+ ZOHO_ACCOUNTS_REFRESH_TOKEN=<your-refresh-token>
24
+ ZOHO_ACCOUNTS_CLIENT_ID=<your-client-id>
25
+ ZOHO_ACCOUNTS_CLIENT_SECRET=<your-client-secret>
26
+
27
+ # CRM API endpoint
28
+ ZOHO_API_URL=https://www.zohoapis.com
29
+ ```
30
+
31
+ > You can also use CRM-specific env vars (e.g. `ZOHO_CRM_ACCOUNTS_REFRESH_TOKEN`) to override the shared values. This is useful when your app integrates multiple Zoho services (CRM, Recruit, Sign) with different credentials.
32
+
33
+ ### Token Cache
34
+
35
+ You must provide a `ZohoAccountsAccessTokenCacheService` that persists OAuth access tokens. The library includes three built-in implementations:
36
+
37
+ ```typescript
38
+ import {
39
+ memoryZohoAccountsAccessTokenCacheService,
40
+ fileZohoAccountsAccessTokenCacheService,
41
+ mergeZohoAccountsAccessTokenCacheServices
42
+ } from '@dereekb/zoho-nestjs';
43
+
44
+ // In-memory only (good for testing)
45
+ const cacheService = memoryZohoAccountsAccessTokenCacheService();
46
+
47
+ // File-based (good for local development, persists across restarts)
48
+ const cacheService = fileZohoAccountsAccessTokenCacheService('.tmp/zoho-tokens.json');
49
+
50
+ // Merged (fault-tolerant, tries multiple sources in sequence)
51
+ const cacheService = mergeZohoAccountsAccessTokenCacheServices([cacheA, cacheB]);
52
+ ```
53
+
54
+ ### Module Declaration
55
+
56
+ Create a dependency module that provides the token cache, then use `appZohoCrmModuleMetadata()` to wire everything together:
57
+
58
+ ```typescript
59
+ import { Module } from '@nestjs/common';
60
+ import {
61
+ ZohoAccountsAccessTokenCacheService,
62
+ ZohoCrmApi,
63
+ appZohoCrmModuleMetadata,
64
+ fileZohoAccountsAccessTokenCacheService
65
+ } from '@dereekb/zoho-nestjs';
66
+
67
+ @Module({
68
+ providers: [
69
+ {
70
+ provide: ZohoAccountsAccessTokenCacheService,
71
+ useValue: fileZohoAccountsAccessTokenCacheService()
72
+ }
73
+ ],
74
+ exports: [ZohoAccountsAccessTokenCacheService]
75
+ })
76
+ export class ZohoCrmDependencyModule {}
77
+
78
+ @Module(appZohoCrmModuleMetadata({ dependencyModule: ZohoCrmDependencyModule }))
79
+ export class AppZohoCrmModule {}
80
+ ```
81
+
82
+ `appZohoCrmModuleMetadata()` automatically registers:
83
+ - `ZohoAccountsServiceConfig` (reads OAuth env vars via `ConfigService`)
84
+ - `ZohoAccountsApi` (manages token refresh and caching)
85
+ - `ZohoCrmServiceConfig` (reads CRM API URL from env vars)
86
+ - `ZohoCrmApi` (the injectable service you'll use everywhere)
87
+
88
+ Import `AppZohoCrmModule` in your app module and inject `ZohoCrmApi` wherever needed.
89
+
90
+ ### Non-NestJS Setup
91
+
92
+ If you're not using NestJS, create the accounts context and CRM client manually:
93
+
94
+ ```typescript
95
+ import { zohoAccountsFactory, zohoCrmFactory } from '@dereekb/zoho';
96
+
97
+ const accounts = zohoAccountsFactory({})({
98
+ apiUrl: 'us',
99
+ clientId: 'your-client-id',
100
+ clientSecret: 'your-client-secret',
101
+ refreshToken: 'your-refresh-token',
102
+ accessTokenCache: myOptionalCache
103
+ });
104
+
105
+ const crm = zohoCrmFactory({
106
+ accountsContext: accounts.accountsContext
107
+ })({
108
+ apiUrl: 'production' // or 'sandbox'
109
+ });
110
+
111
+ const { crmContext } = crm;
112
+ // Pass crmContext to all zohoCrmXxx() functions
113
+ ```
114
+
115
+ ## 3. CRUD Operations
116
+
117
+ `ZohoCrmApi` exposes all CRM operations as getter properties. Each returns a pre-bound async function.
118
+
119
+ ```typescript
120
+ import { Injectable } from '@nestjs/common';
121
+ import { ZohoCrmApi } from '@dereekb/zoho-nestjs';
122
+
123
+ @Injectable()
124
+ export class ContactsService {
125
+ constructor(private readonly zohoCrmApi: ZohoCrmApi) {}
126
+
127
+ async createContact() {
128
+ return this.zohoCrmApi.insertRecord({
129
+ module: 'Contacts',
130
+ data: { First_Name: 'Jane', Last_Name: 'Doe', Email: 'jane@example.com' }
131
+ });
132
+ }
133
+
134
+ async getContact(id: string) {
135
+ return this.zohoCrmApi.getRecordById({ module: 'Contacts', id });
136
+ }
137
+
138
+ async updateContact(id: string, lastName: string) {
139
+ return this.zohoCrmApi.updateRecord({
140
+ module: 'Contacts',
141
+ data: { id, Last_Name: lastName }
142
+ });
143
+ }
144
+
145
+ async deleteContact(id: string) {
146
+ return this.zohoCrmApi.deleteRecord({ module: 'Contacts', ids: id });
147
+ }
148
+ }
149
+ ```
150
+
151
+ When passing a **single record**, the function returns the result directly or throws on error. When passing an **array** (up to 100 records), it returns `{ successItems, errorItems }`.
152
+
153
+ ## 4. Search with Criteria
154
+
155
+ ```typescript
156
+ // Simple criteria (all AND-joined):
157
+ const results = await this.zohoCrmApi.searchRecords({
158
+ module: 'Contacts',
159
+ criteria: [
160
+ { field: 'Last_Name', filter: 'starts_with', value: 'Sm' },
161
+ { field: 'Email', filter: 'contains', value: 'example.com' }
162
+ ]
163
+ });
164
+
165
+ // OR logic using a criteria tree:
166
+ const results = await this.zohoCrmApi.searchRecords({
167
+ module: 'Contacts',
168
+ criteria: {
169
+ or: [
170
+ [{ field: 'Status', filter: 'equals', value: 'Active' }],
171
+ [{ field: 'Status', filter: 'equals', value: 'Pending' }]
172
+ ]
173
+ }
174
+ });
175
+ ```
176
+
177
+ Filter operators: `equals`, `starts_with`, `contains`. Maximum 10 criteria entries per search.
178
+
179
+ ## 5. Pagination
180
+
181
+ All list/search results return `{ data, info: { more_records, page, per_page } }`. Use page factories for automatic chaining:
182
+
183
+ ```typescript
184
+ const fetchPage = this.zohoCrmApi.searchRecordsPageFactory({
185
+ module: 'Contacts',
186
+ criteria: [{ field: 'Last_Name', filter: 'starts_with', value: 'A' }]
187
+ });
188
+
189
+ const page1 = await fetchPage.fetchNext();
190
+ const page2 = await page1.fetchNext(); // auto-follows pagination
191
+ ```
192
+
193
+ ## 6. Notes, Tags, and Attachments
194
+
195
+ ### Notes
196
+
197
+ ```typescript
198
+ // Create notes for a record
199
+ await this.zohoCrmApi.createNotesForRecord({
200
+ module: 'Contacts',
201
+ id: contactId,
202
+ notes: [{ Note_Title: 'Call Log', Note_Content: 'Left voicemail' }]
203
+ });
204
+
205
+ // Get notes (paginated)
206
+ const notes = await this.zohoCrmApi.getNotesForRecord({
207
+ module: 'Contacts',
208
+ id: contactId,
209
+ fields: 'Note_Title,Note_Content'
210
+ });
211
+
212
+ // Delete notes
213
+ await this.zohoCrmApi.deleteNotes({ ids: [noteId1, noteId2] });
214
+ ```
215
+
216
+ ### Tags
217
+
218
+ ```typescript
219
+ // Create tags for a module
220
+ await this.zohoCrmApi.createTagsForModule({
221
+ module: 'Contacts',
222
+ tags: [{ name: 'VIP' }, { name: 'Hot Lead', color_code: '#FF0000' }]
223
+ });
224
+
225
+ // Add/remove tags on records
226
+ await this.zohoCrmApi.addTagsToRecords({
227
+ module: 'Contacts',
228
+ ids: [contactId],
229
+ tag_names: ['VIP']
230
+ });
231
+
232
+ await this.zohoCrmApi.removeTagsFromRecords({
233
+ module: 'Contacts',
234
+ ids: [contactId],
235
+ tag_names: ['VIP']
236
+ });
237
+ ```
238
+
239
+ ### Attachments
240
+
241
+ ```typescript
242
+ // Upload (max 20MB)
243
+ await this.zohoCrmApi.uploadAttachmentForRecord({
244
+ module: 'Contacts',
245
+ id: contactId,
246
+ file: myFile,
247
+ attachmentCategoryName: 'Resume'
248
+ });
249
+
250
+ // List attachments
251
+ const attachments = await this.zohoCrmApi.getAttachmentsForRecord({
252
+ module: 'Contacts',
253
+ id: contactId
254
+ });
255
+
256
+ // Download
257
+ const fileResponse = await this.zohoCrmApi.downloadAttachmentForRecord({
258
+ module: 'Contacts',
259
+ id: contactId,
260
+ attachment_id: attachmentId
261
+ });
262
+
263
+ // Delete
264
+ await this.zohoCrmApi.deleteAttachmentFromRecord({
265
+ module: 'Contacts',
266
+ id: contactId,
267
+ attachment_id: attachmentId
268
+ });
269
+ ```
270
+
271
+ ## 7. Serverless Functions
272
+
273
+ Call Zoho CRM serverless functions via REST API:
274
+
275
+ ```typescript
276
+ // OAuth-authenticated call
277
+ const result = await this.zohoCrmApi.executeRestApiFunction({
278
+ functionName: 'my_custom_function',
279
+ params: { contact_id: '123' }
280
+ });
281
+
282
+ // API key-authenticated call (cross-environment)
283
+ const result = await this.zohoCrmApi.executeRestApiFunction({
284
+ functionName: 'my_custom_function',
285
+ apiKey: 'your-zapikey',
286
+ apiUrl: 'production'
287
+ });
288
+ ```
289
+
290
+ ## Key Design Details
291
+
292
+ - **Rate limiting** is built-in (default 100 req/min), auto-adjusts from `X-RATELIMIT-LIMIT` response headers, and retries on HTTP 429.
293
+ - **Token refresh** is automatic. On `ZohoInvalidTokenError`, the token is cleared and re-fetched transparently.
294
+ - **Error classes** are specific and catchable: `ZohoCrmRecordNoContentError`, `ZohoCrmRecordCrudDuplicateDataError`, `ZohoCrmRecordCrudMandatoryFieldNotFoundError`, etc.
295
+ - **Module constants** are provided for common modules: `ZOHO_CRM_LEADS_MODULE`, `ZOHO_CRM_CONTACTS_MODULE`, `ZOHO_CRM_TASKS_MODULE`, etc.
296
+ - Uses the **Zoho CRM v8 API**.