@friggframework/api-module-zoho-crm 2.0.0-next.1 → 2.0.0-next.2

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/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 } 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,65 @@ 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>;
32
101
  }
package/dist/api.js CHANGED
@@ -26,6 +26,7 @@ class Api extends core_1.OAuth2Requester {
26
26
  accounts: '/Accounts',
27
27
  account: (accountId) => `/Accounts/${accountId}`,
28
28
  accountSearch: '/Accounts/search',
29
+ notificationsWatch: '/actions/watch',
29
30
  };
30
31
  }
31
32
  getAuthUri() {
@@ -52,6 +53,20 @@ class Api extends core_1.OAuth2Requester {
52
53
  const response = await super._delete(options);
53
54
  return await this.parsedBody(response);
54
55
  }
56
+ /**
57
+ * Build URL for notes endpoints with proper encoding
58
+ * @param module - Module API name (e.g., 'Contacts', 'Leads')
59
+ * @param recordId - Record ID
60
+ * @param noteId - Optional note ID for specific note operations
61
+ * @returns Encoded URL path for notes endpoint
62
+ */
63
+ buildNotesUrl(module, recordId, noteId) {
64
+ const segments = [module, recordId, 'Notes'];
65
+ if (noteId)
66
+ segments.push(noteId);
67
+ const encodedPath = segments.map(encodeURIComponent).join('/');
68
+ return `${this.baseUrl}/${encodedPath}`;
69
+ }
55
70
  async listUsers(queryParams = {}) {
56
71
  return this._get({
57
72
  url: this.baseUrl + this.URLs.users,
@@ -281,6 +296,224 @@ class Api extends core_1.OAuth2Requester {
281
296
  throw error;
282
297
  }
283
298
  }
299
+ /**
300
+ * List all notes for a specific record
301
+ * @param module - Module API name (Contacts, Leads, Accounts, etc.)
302
+ * @param recordId - Record ID to get notes for
303
+ * @param queryParams - Optional query parameters (per_page, page, etc.)
304
+ * @returns Promise<NoteListResponse> Notes list response
305
+ */
306
+ async listNotes(module, recordId, queryParams = {}) {
307
+ if (!module) {
308
+ throw new Error('module is required');
309
+ }
310
+ if (!recordId) {
311
+ throw new Error('recordId is required');
312
+ }
313
+ try {
314
+ return await this._get({
315
+ url: this.buildNotesUrl(module, recordId),
316
+ query: queryParams,
317
+ });
318
+ }
319
+ catch (error) {
320
+ throw error;
321
+ }
322
+ }
323
+ /**
324
+ * Get a specific note by ID
325
+ * @param module - Module API name (Contacts, Leads, Accounts, etc.)
326
+ * @param recordId - Record ID the note is attached to
327
+ * @param noteId - Note ID to retrieve
328
+ * @returns Promise<NotesResponse> Note details
329
+ */
330
+ async getNote(module, recordId, noteId) {
331
+ if (!module) {
332
+ throw new Error('module is required');
333
+ }
334
+ if (!recordId) {
335
+ throw new Error('recordId is required');
336
+ }
337
+ if (!noteId) {
338
+ throw new Error('noteId is required');
339
+ }
340
+ try {
341
+ return await this._get({
342
+ url: this.buildNotesUrl(module, recordId, noteId),
343
+ });
344
+ }
345
+ catch (error) {
346
+ throw error;
347
+ }
348
+ }
349
+ /**
350
+ * Create a note for a specific record
351
+ * @param module - Module API name (Contacts, Leads, Accounts, etc.)
352
+ * @param recordId - Record ID to attach note to
353
+ * @param noteData - Note data with Note_Content (required) and optional Note_Title
354
+ * @returns Promise<NotesResponse> Created note response
355
+ */
356
+ async createNote(module, recordId, noteData) {
357
+ if (!module) {
358
+ throw new Error('module is required');
359
+ }
360
+ if (!recordId) {
361
+ throw new Error('recordId is required');
362
+ }
363
+ if (!noteData || !noteData.Note_Content) {
364
+ throw new Error('noteData.Note_Content is required');
365
+ }
366
+ const body = {
367
+ data: [{
368
+ Note_Content: noteData.Note_Content,
369
+ ...(noteData.Note_Title && { Note_Title: noteData.Note_Title }),
370
+ }]
371
+ };
372
+ try {
373
+ return await this._post({
374
+ url: this.buildNotesUrl(module, recordId),
375
+ body: body,
376
+ });
377
+ }
378
+ catch (error) {
379
+ throw error;
380
+ }
381
+ }
382
+ /**
383
+ * Update an existing note
384
+ * @param module - Module API name (Contacts, Leads, Accounts, etc.)
385
+ * @param recordId - Record ID the note is attached to
386
+ * @param noteId - Note ID to update
387
+ * @param noteData - Updated note data. At least one of Note_Content or Note_Title must be provided.
388
+ * @returns Promise<NotesResponse> Updated note response
389
+ */
390
+ async updateNote(module, recordId, noteId, noteData) {
391
+ if (!module) {
392
+ throw new Error('module is required');
393
+ }
394
+ if (!recordId) {
395
+ throw new Error('recordId is required');
396
+ }
397
+ if (!noteId) {
398
+ throw new Error('noteId is required');
399
+ }
400
+ if (!noteData || (!noteData.Note_Content && !noteData.Note_Title)) {
401
+ throw new Error('noteData must contain Note_Content or Note_Title');
402
+ }
403
+ const body = {
404
+ data: [{
405
+ ...(noteData.Note_Content && { Note_Content: noteData.Note_Content }),
406
+ ...(noteData.Note_Title && { Note_Title: noteData.Note_Title }),
407
+ }]
408
+ };
409
+ try {
410
+ return await this._put({
411
+ url: this.buildNotesUrl(module, recordId, noteId),
412
+ body: body,
413
+ });
414
+ }
415
+ catch (error) {
416
+ throw error;
417
+ }
418
+ }
419
+ /**
420
+ * Delete a note
421
+ * @param module - Module API name (Contacts, Leads, Accounts, etc.)
422
+ * @param recordId - Record ID the note is attached to
423
+ * @param noteId - Note ID to delete
424
+ * @returns Promise<NotesResponse> Deletion response
425
+ */
426
+ async deleteNote(module, recordId, noteId) {
427
+ if (!module) {
428
+ throw new Error('module is required');
429
+ }
430
+ if (!recordId) {
431
+ throw new Error('recordId is required');
432
+ }
433
+ if (!noteId) {
434
+ throw new Error('noteId is required');
435
+ }
436
+ try {
437
+ return await this._delete({
438
+ url: this.buildNotesUrl(module, recordId, noteId),
439
+ });
440
+ }
441
+ catch (error) {
442
+ throw error;
443
+ }
444
+ }
445
+ /**
446
+ * Enable notification channel to receive real-time events
447
+ * @param body - Notification configuration with watch items
448
+ * @returns Promise<NotificationResponse> Response with channel details
449
+ * @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/enable.html
450
+ */
451
+ async enableNotification(body) {
452
+ if (!body || !body.watch || !Array.isArray(body.watch)) {
453
+ throw new Error('Body must contain watch array');
454
+ }
455
+ // Validate each watch item
456
+ body.watch.forEach((item, index) => {
457
+ if (!item.channel_id) {
458
+ throw new Error(`watch[${index}].channel_id is required`);
459
+ }
460
+ if (!item.events || !Array.isArray(item.events) || item.events.length === 0) {
461
+ throw new Error(`watch[${index}].events must be a non-empty array`);
462
+ }
463
+ if (!item.notify_url) {
464
+ throw new Error(`watch[${index}].notify_url is required`);
465
+ }
466
+ if (item.token && item.token.length > 50) {
467
+ throw new Error(`watch[${index}].token must be 50 characters or less`);
468
+ }
469
+ });
470
+ try {
471
+ return await this._post({
472
+ url: this.baseUrl + this.URLs.notificationsWatch,
473
+ body: body,
474
+ });
475
+ }
476
+ catch (error) {
477
+ throw error;
478
+ }
479
+ }
480
+ /**
481
+ * Disable notification channels
482
+ * @param channelIds - Array of channel IDs to disable
483
+ * @returns Promise<NotificationResponse> Response confirming deletion
484
+ * @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/disable.html
485
+ */
486
+ async disableNotification(channelIds) {
487
+ if (!channelIds || !Array.isArray(channelIds) || channelIds.length === 0) {
488
+ throw new Error('channelIds must be a non-empty array');
489
+ }
490
+ try {
491
+ return await this._delete({
492
+ url: this.baseUrl + this.URLs.notificationsWatch,
493
+ query: {
494
+ channel_ids: channelIds.join(',')
495
+ },
496
+ });
497
+ }
498
+ catch (error) {
499
+ throw error;
500
+ }
501
+ }
502
+ /**
503
+ * Get details of all active notification channels
504
+ * @returns Promise<NotificationDetailsResponse> Details of all active channels
505
+ * @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/get-details.html
506
+ */
507
+ async getNotificationDetails() {
508
+ try {
509
+ return await this._get({
510
+ url: this.baseUrl + this.URLs.notificationsWatch,
511
+ });
512
+ }
513
+ catch (error) {
514
+ throw error;
515
+ }
516
+ }
284
517
  }
285
518
  exports.Api = Api;
286
519
  Api.CONTACTS_DEFAULT_FIELDS = 'id,First_Name,Last_Name,Email,Phone,Mobile,Account_Name,Company,Owner,Lead_Source,Created_Time,Modified_Time';
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,82 @@ 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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@friggframework/api-module-zoho-crm",
3
- "version": "2.0.0-next.1",
3
+ "version": "2.0.0-next.2",
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": "5b22500d5eb6b9032389753acad2279ddd70a8dd"
39
+ "gitHead": "fa3f5ec40cbb9300511849233bddb0c463bfab2a"
40
40
  }