@friggframework/api-module-zoho-crm 2.0.0-next.1 → 2.0.0-next.3
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.
- package/README.md +16 -1
- package/dist/api.d.ts +85 -1
- package/dist/api.js +317 -0
- package/dist/types.d.ts +178 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ If you've already done this, skip to the next section.
|
|
|
52
52
|
```shell
|
|
53
53
|
ZOHO_CRM_CLIENT_ID=your_client_id
|
|
54
54
|
ZOHO_CRM_CLIENT_SECRET=your_client_secret
|
|
55
|
-
ZOHO_CRM_SCOPE=ZohoCRM.users.ALL
|
|
55
|
+
ZOHO_CRM_SCOPE=ZohoCRM.users.ALL ZohoCRM.org.ALL ZohoCRM.settings.roles.ALL ZohoCRM.settings.profiles.ALL ZohoCRM.modules.contacts.ALL ZohoCRM.modules.leads.ALL ZohoCRM.modules.accounts.ALL ZohoCRM.modules.calls.ALL
|
|
56
56
|
REDIRECT_URI=http://localhost:3000/redirect
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -90,6 +90,21 @@ If you've already done this, skip to the next section.
|
|
|
90
90
|
- `getAccount(accountId)` - Get a specific account by ID
|
|
91
91
|
- `searchAccounts(searchParams)` - Search accounts by phone, criteria, or word
|
|
92
92
|
|
|
93
|
+
### Calls
|
|
94
|
+
- `logCall(callData)` - Log a call in the Zoho CRM Calls module
|
|
95
|
+
- `updateCall(callId, callData)` - Update an existing call record
|
|
96
|
+
|
|
97
|
+
**Call Data Fields:**
|
|
98
|
+
| Field | Type | Required | Description |
|
|
99
|
+
|-------|------|----------|-------------|
|
|
100
|
+
| `Subject` | string | Yes | Call subject/title |
|
|
101
|
+
| `Call_Type` | string | Yes | `Inbound`, `Outbound`, or `Missed` |
|
|
102
|
+
| `Call_Start_Time` | string | Yes | ISO 8601 datetime |
|
|
103
|
+
| `Call_Duration` | string | Yes* | `mm:ss` format (*required for Inbound/Outbound, cannot be zero) |
|
|
104
|
+
| `Description` | string | No | Call notes |
|
|
105
|
+
| `Who_Id` | string | No | Contact/Lead ID to associate |
|
|
106
|
+
| `$se_module` | string | No | Module for Who_Id: `Contacts` or `Leads` |
|
|
107
|
+
|
|
93
108
|
## Using the API Module from the Terminal
|
|
94
109
|
|
|
95
110
|
With your `.env` in place, you can test the API from a Node terminal.
|
package/dist/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OAuth2Requester } from '@friggframework/core';
|
|
2
|
-
import { ZohoConfig, QueryParams, SearchParams, UsersResponse, RolesResponse, ProfilesResponse, ContactsResponse, ContactResponse, LeadsResponse, LeadResponse, AccountsResponse, AccountResponse, TokenResponse } from './types';
|
|
2
|
+
import { ZohoConfig, QueryParams, SearchParams, UsersResponse, RolesResponse, ProfilesResponse, ContactsResponse, ContactResponse, LeadsResponse, LeadResponse, AccountsResponse, AccountResponse, TokenResponse, CreateNoteData, NotesResponse, NoteListResponse, NotificationWatchConfig, NotificationResponse, NotificationDetailsResponse, ZohoCallData, CallsResponse } from './types';
|
|
3
3
|
export declare class Api extends OAuth2Requester {
|
|
4
4
|
URLs: Record<string, string | ((id: string) => string)>;
|
|
5
5
|
private static readonly CONTACTS_DEFAULT_FIELDS;
|
|
@@ -9,6 +9,14 @@ export declare class Api extends OAuth2Requester {
|
|
|
9
9
|
getAuthUri(): string;
|
|
10
10
|
getTokenFromCode(code: string): Promise<TokenResponse>;
|
|
11
11
|
_delete(options: any): Promise<any>;
|
|
12
|
+
/**
|
|
13
|
+
* Build URL for notes endpoints with proper encoding
|
|
14
|
+
* @param module - Module API name (e.g., 'Contacts', 'Leads')
|
|
15
|
+
* @param recordId - Record ID
|
|
16
|
+
* @param noteId - Optional note ID for specific note operations
|
|
17
|
+
* @returns Encoded URL path for notes endpoint
|
|
18
|
+
*/
|
|
19
|
+
private buildNotesUrl;
|
|
12
20
|
listUsers(queryParams?: QueryParams): Promise<UsersResponse>;
|
|
13
21
|
getUser(userId: string): Promise<UsersResponse>;
|
|
14
22
|
createUser(body?: any): Promise<any>;
|
|
@@ -29,4 +37,80 @@ export declare class Api extends OAuth2Requester {
|
|
|
29
37
|
listAccounts(queryParams?: QueryParams): Promise<AccountsResponse>;
|
|
30
38
|
getAccount(accountId: string): Promise<AccountResponse>;
|
|
31
39
|
searchAccounts(searchParams?: SearchParams): Promise<AccountsResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* List all notes for a specific record
|
|
42
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
43
|
+
* @param recordId - Record ID to get notes for
|
|
44
|
+
* @param queryParams - Optional query parameters (per_page, page, etc.)
|
|
45
|
+
* @returns Promise<NoteListResponse> Notes list response
|
|
46
|
+
*/
|
|
47
|
+
listNotes(module: string, recordId: string, queryParams?: QueryParams): Promise<NoteListResponse>;
|
|
48
|
+
/**
|
|
49
|
+
* Get a specific note by ID
|
|
50
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
51
|
+
* @param recordId - Record ID the note is attached to
|
|
52
|
+
* @param noteId - Note ID to retrieve
|
|
53
|
+
* @returns Promise<NotesResponse> Note details
|
|
54
|
+
*/
|
|
55
|
+
getNote(module: string, recordId: string, noteId: string): Promise<NotesResponse>;
|
|
56
|
+
/**
|
|
57
|
+
* Create a note for a specific record
|
|
58
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
59
|
+
* @param recordId - Record ID to attach note to
|
|
60
|
+
* @param noteData - Note data with Note_Content (required) and optional Note_Title
|
|
61
|
+
* @returns Promise<NotesResponse> Created note response
|
|
62
|
+
*/
|
|
63
|
+
createNote(module: string, recordId: string, noteData: CreateNoteData): Promise<NotesResponse>;
|
|
64
|
+
/**
|
|
65
|
+
* Update an existing note
|
|
66
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
67
|
+
* @param recordId - Record ID the note is attached to
|
|
68
|
+
* @param noteId - Note ID to update
|
|
69
|
+
* @param noteData - Updated note data. At least one of Note_Content or Note_Title must be provided.
|
|
70
|
+
* @returns Promise<NotesResponse> Updated note response
|
|
71
|
+
*/
|
|
72
|
+
updateNote(module: string, recordId: string, noteId: string, noteData: Partial<CreateNoteData>): Promise<NotesResponse>;
|
|
73
|
+
/**
|
|
74
|
+
* Delete a note
|
|
75
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
76
|
+
* @param recordId - Record ID the note is attached to
|
|
77
|
+
* @param noteId - Note ID to delete
|
|
78
|
+
* @returns Promise<NotesResponse> Deletion response
|
|
79
|
+
*/
|
|
80
|
+
deleteNote(module: string, recordId: string, noteId: string): Promise<NotesResponse>;
|
|
81
|
+
/**
|
|
82
|
+
* Enable notification channel to receive real-time events
|
|
83
|
+
* @param body - Notification configuration with watch items
|
|
84
|
+
* @returns Promise<NotificationResponse> Response with channel details
|
|
85
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/enable.html
|
|
86
|
+
*/
|
|
87
|
+
enableNotification(body: NotificationWatchConfig): Promise<NotificationResponse>;
|
|
88
|
+
/**
|
|
89
|
+
* Disable notification channels
|
|
90
|
+
* @param channelIds - Array of channel IDs to disable
|
|
91
|
+
* @returns Promise<NotificationResponse> Response confirming deletion
|
|
92
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/disable.html
|
|
93
|
+
*/
|
|
94
|
+
disableNotification(channelIds: Array<string | number>): Promise<NotificationResponse>;
|
|
95
|
+
/**
|
|
96
|
+
* Get details of all active notification channels
|
|
97
|
+
* @returns Promise<NotificationDetailsResponse> Details of all active channels
|
|
98
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/get-details.html
|
|
99
|
+
*/
|
|
100
|
+
getNotificationDetails(): Promise<NotificationDetailsResponse>;
|
|
101
|
+
/**
|
|
102
|
+
* Log a call in Zoho CRM Calls module
|
|
103
|
+
* @param callData - Call record data with Subject, Call_Type, Call_Start_Time, Call_Duration
|
|
104
|
+
* @returns Promise<CallsResponse> Created call response
|
|
105
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/insert-records.html
|
|
106
|
+
*/
|
|
107
|
+
logCall(callData: ZohoCallData): Promise<CallsResponse>;
|
|
108
|
+
/**
|
|
109
|
+
* Update an existing call record in Zoho CRM Calls module
|
|
110
|
+
* @param callId - Zoho Call ID to update
|
|
111
|
+
* @param callData - Partial call data to update (e.g., Description, Subject)
|
|
112
|
+
* @returns Promise<CallsResponse> Updated call response
|
|
113
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/update-records.html
|
|
114
|
+
*/
|
|
115
|
+
updateCall(callId: string, callData: Partial<ZohoCallData>): Promise<CallsResponse>;
|
|
32
116
|
}
|
package/dist/api.js
CHANGED
|
@@ -26,6 +26,9 @@ class Api extends core_1.OAuth2Requester {
|
|
|
26
26
|
accounts: '/Accounts',
|
|
27
27
|
account: (accountId) => `/Accounts/${accountId}`,
|
|
28
28
|
accountSearch: '/Accounts/search',
|
|
29
|
+
calls: '/Calls',
|
|
30
|
+
call: (callId) => `/Calls/${callId}`,
|
|
31
|
+
notificationsWatch: '/actions/watch',
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
34
|
getAuthUri() {
|
|
@@ -52,6 +55,20 @@ class Api extends core_1.OAuth2Requester {
|
|
|
52
55
|
const response = await super._delete(options);
|
|
53
56
|
return await this.parsedBody(response);
|
|
54
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Build URL for notes endpoints with proper encoding
|
|
60
|
+
* @param module - Module API name (e.g., 'Contacts', 'Leads')
|
|
61
|
+
* @param recordId - Record ID
|
|
62
|
+
* @param noteId - Optional note ID for specific note operations
|
|
63
|
+
* @returns Encoded URL path for notes endpoint
|
|
64
|
+
*/
|
|
65
|
+
buildNotesUrl(module, recordId, noteId) {
|
|
66
|
+
const segments = [module, recordId, 'Notes'];
|
|
67
|
+
if (noteId)
|
|
68
|
+
segments.push(noteId);
|
|
69
|
+
const encodedPath = segments.map(encodeURIComponent).join('/');
|
|
70
|
+
return `${this.baseUrl}/${encodedPath}`;
|
|
71
|
+
}
|
|
55
72
|
async listUsers(queryParams = {}) {
|
|
56
73
|
return this._get({
|
|
57
74
|
url: this.baseUrl + this.URLs.users,
|
|
@@ -186,6 +203,12 @@ class Api extends core_1.OAuth2Requester {
|
|
|
186
203
|
});
|
|
187
204
|
}
|
|
188
205
|
catch (error) {
|
|
206
|
+
// Zoho returns 204 No Content with empty body when no results found
|
|
207
|
+
// This causes JSON parsing to fail with "Unexpected end of JSON input"
|
|
208
|
+
if (error?.message?.includes('Unexpected end of JSON input') ||
|
|
209
|
+
error?.message?.includes('invalid json response body')) {
|
|
210
|
+
return { data: [], info: undefined };
|
|
211
|
+
}
|
|
189
212
|
throw error;
|
|
190
213
|
}
|
|
191
214
|
}
|
|
@@ -232,6 +255,11 @@ class Api extends core_1.OAuth2Requester {
|
|
|
232
255
|
});
|
|
233
256
|
}
|
|
234
257
|
catch (error) {
|
|
258
|
+
// Zoho returns 204 No Content with empty body when no results found
|
|
259
|
+
if (error?.message?.includes('Unexpected end of JSON input') ||
|
|
260
|
+
error?.message?.includes('invalid json response body')) {
|
|
261
|
+
return { data: [], info: undefined };
|
|
262
|
+
}
|
|
235
263
|
throw error;
|
|
236
264
|
}
|
|
237
265
|
}
|
|
@@ -277,6 +305,295 @@ class Api extends core_1.OAuth2Requester {
|
|
|
277
305
|
query: params,
|
|
278
306
|
});
|
|
279
307
|
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
// Zoho returns 204 No Content with empty body when no results found
|
|
310
|
+
if (error?.message?.includes('Unexpected end of JSON input') ||
|
|
311
|
+
error?.message?.includes('invalid json response body')) {
|
|
312
|
+
return { data: [], info: undefined };
|
|
313
|
+
}
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* List all notes for a specific record
|
|
319
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
320
|
+
* @param recordId - Record ID to get notes for
|
|
321
|
+
* @param queryParams - Optional query parameters (per_page, page, etc.)
|
|
322
|
+
* @returns Promise<NoteListResponse> Notes list response
|
|
323
|
+
*/
|
|
324
|
+
async listNotes(module, recordId, queryParams = {}) {
|
|
325
|
+
if (!module) {
|
|
326
|
+
throw new Error('module is required');
|
|
327
|
+
}
|
|
328
|
+
if (!recordId) {
|
|
329
|
+
throw new Error('recordId is required');
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
return await this._get({
|
|
333
|
+
url: this.buildNotesUrl(module, recordId),
|
|
334
|
+
query: queryParams,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get a specific note by ID
|
|
343
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
344
|
+
* @param recordId - Record ID the note is attached to
|
|
345
|
+
* @param noteId - Note ID to retrieve
|
|
346
|
+
* @returns Promise<NotesResponse> Note details
|
|
347
|
+
*/
|
|
348
|
+
async getNote(module, recordId, noteId) {
|
|
349
|
+
if (!module) {
|
|
350
|
+
throw new Error('module is required');
|
|
351
|
+
}
|
|
352
|
+
if (!recordId) {
|
|
353
|
+
throw new Error('recordId is required');
|
|
354
|
+
}
|
|
355
|
+
if (!noteId) {
|
|
356
|
+
throw new Error('noteId is required');
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
return await this._get({
|
|
360
|
+
url: this.buildNotesUrl(module, recordId, noteId),
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Create a note for a specific record
|
|
369
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
370
|
+
* @param recordId - Record ID to attach note to
|
|
371
|
+
* @param noteData - Note data with Note_Content (required) and optional Note_Title
|
|
372
|
+
* @returns Promise<NotesResponse> Created note response
|
|
373
|
+
*/
|
|
374
|
+
async createNote(module, recordId, noteData) {
|
|
375
|
+
if (!module) {
|
|
376
|
+
throw new Error('module is required');
|
|
377
|
+
}
|
|
378
|
+
if (!recordId) {
|
|
379
|
+
throw new Error('recordId is required');
|
|
380
|
+
}
|
|
381
|
+
if (!noteData || !noteData.Note_Content) {
|
|
382
|
+
throw new Error('noteData.Note_Content is required');
|
|
383
|
+
}
|
|
384
|
+
const body = {
|
|
385
|
+
data: [{
|
|
386
|
+
Note_Content: noteData.Note_Content,
|
|
387
|
+
...(noteData.Note_Title && { Note_Title: noteData.Note_Title }),
|
|
388
|
+
}]
|
|
389
|
+
};
|
|
390
|
+
try {
|
|
391
|
+
return await this._post({
|
|
392
|
+
url: this.buildNotesUrl(module, recordId),
|
|
393
|
+
body: body,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Update an existing note
|
|
402
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
403
|
+
* @param recordId - Record ID the note is attached to
|
|
404
|
+
* @param noteId - Note ID to update
|
|
405
|
+
* @param noteData - Updated note data. At least one of Note_Content or Note_Title must be provided.
|
|
406
|
+
* @returns Promise<NotesResponse> Updated note response
|
|
407
|
+
*/
|
|
408
|
+
async updateNote(module, recordId, noteId, noteData) {
|
|
409
|
+
if (!module) {
|
|
410
|
+
throw new Error('module is required');
|
|
411
|
+
}
|
|
412
|
+
if (!recordId) {
|
|
413
|
+
throw new Error('recordId is required');
|
|
414
|
+
}
|
|
415
|
+
if (!noteId) {
|
|
416
|
+
throw new Error('noteId is required');
|
|
417
|
+
}
|
|
418
|
+
if (!noteData || (!noteData.Note_Content && !noteData.Note_Title)) {
|
|
419
|
+
throw new Error('noteData must contain Note_Content or Note_Title');
|
|
420
|
+
}
|
|
421
|
+
const body = {
|
|
422
|
+
data: [{
|
|
423
|
+
...(noteData.Note_Content && { Note_Content: noteData.Note_Content }),
|
|
424
|
+
...(noteData.Note_Title && { Note_Title: noteData.Note_Title }),
|
|
425
|
+
}]
|
|
426
|
+
};
|
|
427
|
+
try {
|
|
428
|
+
return await this._put({
|
|
429
|
+
url: this.buildNotesUrl(module, recordId, noteId),
|
|
430
|
+
body: body,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Delete a note
|
|
439
|
+
* @param module - Module API name (Contacts, Leads, Accounts, etc.)
|
|
440
|
+
* @param recordId - Record ID the note is attached to
|
|
441
|
+
* @param noteId - Note ID to delete
|
|
442
|
+
* @returns Promise<NotesResponse> Deletion response
|
|
443
|
+
*/
|
|
444
|
+
async deleteNote(module, recordId, noteId) {
|
|
445
|
+
if (!module) {
|
|
446
|
+
throw new Error('module is required');
|
|
447
|
+
}
|
|
448
|
+
if (!recordId) {
|
|
449
|
+
throw new Error('recordId is required');
|
|
450
|
+
}
|
|
451
|
+
if (!noteId) {
|
|
452
|
+
throw new Error('noteId is required');
|
|
453
|
+
}
|
|
454
|
+
try {
|
|
455
|
+
return await this._delete({
|
|
456
|
+
url: this.buildNotesUrl(module, recordId, noteId),
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Enable notification channel to receive real-time events
|
|
465
|
+
* @param body - Notification configuration with watch items
|
|
466
|
+
* @returns Promise<NotificationResponse> Response with channel details
|
|
467
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/enable.html
|
|
468
|
+
*/
|
|
469
|
+
async enableNotification(body) {
|
|
470
|
+
if (!body || !body.watch || !Array.isArray(body.watch)) {
|
|
471
|
+
throw new Error('Body must contain watch array');
|
|
472
|
+
}
|
|
473
|
+
// Validate each watch item
|
|
474
|
+
body.watch.forEach((item, index) => {
|
|
475
|
+
if (!item.channel_id) {
|
|
476
|
+
throw new Error(`watch[${index}].channel_id is required`);
|
|
477
|
+
}
|
|
478
|
+
if (!item.events || !Array.isArray(item.events) || item.events.length === 0) {
|
|
479
|
+
throw new Error(`watch[${index}].events must be a non-empty array`);
|
|
480
|
+
}
|
|
481
|
+
if (!item.notify_url) {
|
|
482
|
+
throw new Error(`watch[${index}].notify_url is required`);
|
|
483
|
+
}
|
|
484
|
+
if (item.token && item.token.length > 50) {
|
|
485
|
+
throw new Error(`watch[${index}].token must be 50 characters or less`);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
try {
|
|
489
|
+
return await this._post({
|
|
490
|
+
url: this.baseUrl + this.URLs.notificationsWatch,
|
|
491
|
+
body: body,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
throw error;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Disable notification channels
|
|
500
|
+
* @param channelIds - Array of channel IDs to disable
|
|
501
|
+
* @returns Promise<NotificationResponse> Response confirming deletion
|
|
502
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/disable.html
|
|
503
|
+
*/
|
|
504
|
+
async disableNotification(channelIds) {
|
|
505
|
+
if (!channelIds || !Array.isArray(channelIds) || channelIds.length === 0) {
|
|
506
|
+
throw new Error('channelIds must be a non-empty array');
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
return await this._delete({
|
|
510
|
+
url: this.baseUrl + this.URLs.notificationsWatch,
|
|
511
|
+
query: {
|
|
512
|
+
channel_ids: channelIds.join(',')
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Get details of all active notification channels
|
|
522
|
+
* @returns Promise<NotificationDetailsResponse> Details of all active channels
|
|
523
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/get-details.html
|
|
524
|
+
*/
|
|
525
|
+
async getNotificationDetails() {
|
|
526
|
+
try {
|
|
527
|
+
return await this._get({
|
|
528
|
+
url: this.baseUrl + this.URLs.notificationsWatch,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Log a call in Zoho CRM Calls module
|
|
537
|
+
* @param callData - Call record data with Subject, Call_Type, Call_Start_Time, Call_Duration
|
|
538
|
+
* @returns Promise<CallsResponse> Created call response
|
|
539
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/insert-records.html
|
|
540
|
+
*/
|
|
541
|
+
async logCall(callData) {
|
|
542
|
+
if (!callData.Subject) {
|
|
543
|
+
throw new Error('callData.Subject is required');
|
|
544
|
+
}
|
|
545
|
+
if (!callData.Call_Type) {
|
|
546
|
+
throw new Error('callData.Call_Type is required');
|
|
547
|
+
}
|
|
548
|
+
if (!callData.Call_Start_Time) {
|
|
549
|
+
throw new Error('callData.Call_Start_Time is required');
|
|
550
|
+
}
|
|
551
|
+
// Duration is mandatory for Inbound/Outbound calls and cannot be zero
|
|
552
|
+
if ((callData.Call_Type === 'Inbound' || callData.Call_Type === 'Outbound') && !callData.Call_Duration) {
|
|
553
|
+
throw new Error('callData.Call_Duration is required for Inbound/Outbound calls');
|
|
554
|
+
}
|
|
555
|
+
const body = {
|
|
556
|
+
data: [callData]
|
|
557
|
+
};
|
|
558
|
+
try {
|
|
559
|
+
return await this._post({
|
|
560
|
+
url: this.baseUrl + this.URLs.calls,
|
|
561
|
+
body: body,
|
|
562
|
+
headers: {
|
|
563
|
+
'Content-Type': 'application/json',
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
throw error;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Update an existing call record in Zoho CRM Calls module
|
|
573
|
+
* @param callId - Zoho Call ID to update
|
|
574
|
+
* @param callData - Partial call data to update (e.g., Description, Subject)
|
|
575
|
+
* @returns Promise<CallsResponse> Updated call response
|
|
576
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/update-records.html
|
|
577
|
+
*/
|
|
578
|
+
async updateCall(callId, callData) {
|
|
579
|
+
if (!callId) {
|
|
580
|
+
throw new Error('callId is required');
|
|
581
|
+
}
|
|
582
|
+
if (!callData || Object.keys(callData).length === 0) {
|
|
583
|
+
throw new Error('callData must contain at least one field to update');
|
|
584
|
+
}
|
|
585
|
+
const body = {
|
|
586
|
+
data: [callData]
|
|
587
|
+
};
|
|
588
|
+
try {
|
|
589
|
+
return await this._put({
|
|
590
|
+
url: this.baseUrl + this.URLs.call(callId),
|
|
591
|
+
body: body,
|
|
592
|
+
headers: {
|
|
593
|
+
'Content-Type': 'application/json',
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
}
|
|
280
597
|
catch (error) {
|
|
281
598
|
throw error;
|
|
282
599
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -195,6 +195,61 @@ export interface DeleteResponse {
|
|
|
195
195
|
message: string;
|
|
196
196
|
status: string;
|
|
197
197
|
}
|
|
198
|
+
export interface ZohoNote {
|
|
199
|
+
id: string;
|
|
200
|
+
Note_Title?: string;
|
|
201
|
+
Note_Content: string;
|
|
202
|
+
Parent_Id?: {
|
|
203
|
+
module: {
|
|
204
|
+
api_name: string;
|
|
205
|
+
id: string;
|
|
206
|
+
};
|
|
207
|
+
id: string;
|
|
208
|
+
};
|
|
209
|
+
Owner?: {
|
|
210
|
+
name: string;
|
|
211
|
+
id: string;
|
|
212
|
+
};
|
|
213
|
+
Created_Time?: string;
|
|
214
|
+
Modified_Time?: string;
|
|
215
|
+
Created_By?: {
|
|
216
|
+
name: string;
|
|
217
|
+
id: string;
|
|
218
|
+
};
|
|
219
|
+
Modified_By?: {
|
|
220
|
+
name: string;
|
|
221
|
+
id: string;
|
|
222
|
+
};
|
|
223
|
+
[key: string]: any;
|
|
224
|
+
}
|
|
225
|
+
export interface CreateNoteData {
|
|
226
|
+
Note_Content: string;
|
|
227
|
+
Note_Title?: string;
|
|
228
|
+
}
|
|
229
|
+
export interface NotesResponse {
|
|
230
|
+
data: Array<{
|
|
231
|
+
code: string;
|
|
232
|
+
details: {
|
|
233
|
+
id: string;
|
|
234
|
+
Created_Time: string;
|
|
235
|
+
Modified_Time: string;
|
|
236
|
+
Created_By?: {
|
|
237
|
+
name: string;
|
|
238
|
+
id: string;
|
|
239
|
+
};
|
|
240
|
+
Modified_By?: {
|
|
241
|
+
name: string;
|
|
242
|
+
id: string;
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
message: string;
|
|
246
|
+
status: string;
|
|
247
|
+
}>;
|
|
248
|
+
}
|
|
249
|
+
export interface NoteListResponse {
|
|
250
|
+
data: ZohoNote[];
|
|
251
|
+
info?: PaginationInfo;
|
|
252
|
+
}
|
|
198
253
|
export interface QueryParams {
|
|
199
254
|
fields?: string;
|
|
200
255
|
per_page?: number;
|
|
@@ -220,3 +275,126 @@ export interface TokenResponse {
|
|
|
220
275
|
token_type: string;
|
|
221
276
|
expires_in: number;
|
|
222
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Configuration for a single notification watch item
|
|
280
|
+
*/
|
|
281
|
+
export interface NotificationWatchItem {
|
|
282
|
+
/** Unique channel ID (use timestamp or UUID) */
|
|
283
|
+
channel_id: number | string;
|
|
284
|
+
/** Events to watch in format: "Module.operation" (e.g., "Contacts.all", "Contacts.create", "Accounts.edit") */
|
|
285
|
+
events: string[];
|
|
286
|
+
/** Callback URL to receive notifications */
|
|
287
|
+
notify_url: string;
|
|
288
|
+
/** Optional verification token (max 50 characters) */
|
|
289
|
+
token?: string;
|
|
290
|
+
/** Channel expiry time (ISO 8601 format, max 1 week from now) */
|
|
291
|
+
channel_expiry?: string;
|
|
292
|
+
/** Include field changes in callback payload */
|
|
293
|
+
return_affected_field_values?: boolean;
|
|
294
|
+
/** Trigger notifications on related record actions */
|
|
295
|
+
notify_on_related_action?: boolean;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Request body for enabling notifications
|
|
299
|
+
*/
|
|
300
|
+
export interface NotificationWatchConfig {
|
|
301
|
+
watch: NotificationWatchItem[];
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Response from notification API operations
|
|
305
|
+
*/
|
|
306
|
+
export interface NotificationResponse {
|
|
307
|
+
watch: Array<{
|
|
308
|
+
code: string;
|
|
309
|
+
details: {
|
|
310
|
+
channel_id: number | string;
|
|
311
|
+
events: string[];
|
|
312
|
+
channel_expiry: string;
|
|
313
|
+
resource_uri: string;
|
|
314
|
+
resource_id: string;
|
|
315
|
+
resource_name: string;
|
|
316
|
+
};
|
|
317
|
+
message: string;
|
|
318
|
+
status: string;
|
|
319
|
+
}>;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Response from getting notification details
|
|
323
|
+
*/
|
|
324
|
+
export interface NotificationDetailsResponse {
|
|
325
|
+
watch: Array<{
|
|
326
|
+
channel_id: number | string;
|
|
327
|
+
events: string[];
|
|
328
|
+
channel_expiry: string;
|
|
329
|
+
notify_url: string;
|
|
330
|
+
resource_uri: string;
|
|
331
|
+
resource_id: string;
|
|
332
|
+
resource_name: string;
|
|
333
|
+
token?: string;
|
|
334
|
+
}>;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Payload received in notification callback
|
|
338
|
+
*/
|
|
339
|
+
export interface NotificationCallbackPayload {
|
|
340
|
+
/** Server timestamp */
|
|
341
|
+
server_time: number;
|
|
342
|
+
/** Module name (e.g., "Contacts", "Accounts") */
|
|
343
|
+
module: string;
|
|
344
|
+
/** Resource URI */
|
|
345
|
+
resource_uri: string;
|
|
346
|
+
/** Array of affected record IDs */
|
|
347
|
+
ids: string[];
|
|
348
|
+
/** Fields that were affected (if return_affected_field_values enabled) */
|
|
349
|
+
affected_fields?: string[];
|
|
350
|
+
/** Operation type: "insert", "update", or "delete" */
|
|
351
|
+
operation: 'insert' | 'update' | 'delete';
|
|
352
|
+
/** Channel ID that triggered this notification */
|
|
353
|
+
channel_id: number | string;
|
|
354
|
+
/** Verification token (if provided during setup) */
|
|
355
|
+
token?: string;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Data for creating/updating a call record in Zoho CRM Calls module
|
|
359
|
+
*/
|
|
360
|
+
export interface ZohoCallData {
|
|
361
|
+
/** Call subject/title (required) */
|
|
362
|
+
Subject: string;
|
|
363
|
+
/** Call type: Inbound, Outbound, or Missed (required) */
|
|
364
|
+
Call_Type: 'Inbound' | 'Outbound' | 'Missed';
|
|
365
|
+
/** Call start time in ISO 8601 format (required) */
|
|
366
|
+
Call_Start_Time: string;
|
|
367
|
+
/** Call duration in "HH:mm" or "mm:ss" format (required for Inbound/Outbound, cannot be zero) */
|
|
368
|
+
Call_Duration: string;
|
|
369
|
+
/** Call notes/description */
|
|
370
|
+
Description?: string;
|
|
371
|
+
/** Contact or Lead ID to associate the call with */
|
|
372
|
+
Who_Id?: string;
|
|
373
|
+
/** Module name for the Who_Id association: "Contacts" or "Leads" */
|
|
374
|
+
$se_module?: string;
|
|
375
|
+
/** Additional custom fields */
|
|
376
|
+
[key: string]: any;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Response from Calls module operations
|
|
380
|
+
*/
|
|
381
|
+
export interface CallsResponse {
|
|
382
|
+
data: Array<{
|
|
383
|
+
code: string;
|
|
384
|
+
details: {
|
|
385
|
+
id: string;
|
|
386
|
+
Created_Time: string;
|
|
387
|
+
Modified_Time: string;
|
|
388
|
+
Created_By?: {
|
|
389
|
+
name: string;
|
|
390
|
+
id: string;
|
|
391
|
+
};
|
|
392
|
+
Modified_By?: {
|
|
393
|
+
name: string;
|
|
394
|
+
id: string;
|
|
395
|
+
};
|
|
396
|
+
};
|
|
397
|
+
message: string;
|
|
398
|
+
status: string;
|
|
399
|
+
}>;
|
|
400
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/api-module-zoho-crm",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.3",
|
|
4
4
|
"prettier": "@friggframework/prettier-config",
|
|
5
5
|
"description": "Zoho CRM API module that lets the Frigg Framework interact with Zoho CRM",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "fd8e7d7ad197886b7bd94a68fd26c179358f4b67"
|
|
40
40
|
}
|