From 85da76ab361d005653541a48cdb316da2f2ef718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Quereng=C3=A4sser?= <168923949+timdev-ger@users.noreply.github.com> Date: Tue, 13 Jan 2026 12:41:51 +0100 Subject: [PATCH] Add WhatsApp, custom object, and report resources Introduces WhatsAppMessage activity support, custom object and custom object type resources, and expands the report resource with new endpoints and methods. Updates TypeScript definitions, implements corresponding API methods, and adds comprehensive tests and manual test coverage for the new features. Bumps package version to 1.0.3. --- lib/close.com.d.ts | 157 +++++++++++++++++++++++++++- lib/close.com.js | 70 ++++++++++++- package.json | 2 +- test/closecom.test.js | 230 +++++++++++++++++++++++++++++++++++++++++- test/manual-test.js | 71 ++++++++++++- 5 files changed, 520 insertions(+), 10 deletions(-) diff --git a/lib/close.com.d.ts b/lib/close.com.d.ts index 4d1595a..59b3901 100644 --- a/lib/close.com.d.ts +++ b/lib/close.com.d.ts @@ -196,6 +196,24 @@ declare module 'closecrm-node' { [key: string]: any; } + export interface WhatsAppMessage extends Activity { + _type: 'WhatsAppMessage'; + external_whatsapp_message_id: string; + message_markdown: string; + message_html?: string; + direction?: 'incoming' | 'outgoing'; + status?: string; + response_to_id?: string; + integration_link?: string; + attachments?: Array<{ + url: string; + filename: string; + content_type: string; + size?: number; + }>; + [key: string]: any; + } + export interface Task { id: string; lead_id?: string; @@ -221,6 +239,7 @@ declare module 'closecrm-node' { accepts_multiple_values?: boolean; editable_with_roles?: string[]; required?: boolean; + custom_object_type_id?: string; organization_id: string; created_by?: string; date_created: string; @@ -228,6 +247,33 @@ declare module 'closecrm-node' { [key: string]: any; } + export interface CustomObjectType { + id: string; + name: string; + name_plural: string; + description?: string; + api_create_only?: boolean; + editable_with_roles?: string[]; + fields?: CustomField[]; + back_reference_fields?: CustomField[]; + organization_id: string; + date_created: string; + date_updated: string; + [key: string]: any; + } + + export interface CustomObject { + id: string; + custom_object_type_id: string; + lead_id: string; + name: string; + custom?: Record; + date_created: string; + date_updated: string; + organization_id: string; + [key: string]: any; + } + export interface User { id: string; email: string; @@ -438,6 +484,14 @@ declare module 'closecrm-node' { delete(id: string): Promise; } + export interface WhatsAppMessageResource { + search(options?: SearchOptions): Promise>; + create(data: Partial, queryParams?: { send_to_inbox?: boolean }): Promise; + read(id: string): Promise; + update(id: string, data: Partial): Promise; + delete(id: string): Promise; + } + export interface ActivityResource { search(options?: SearchOptions): Promise>; note: NoteResource; @@ -445,6 +499,7 @@ declare module 'closecrm-node' { call: CallResource; sms: SMSResource; meeting: MeetingResource; + whatsapp_message: WhatsAppMessageResource; } export interface OpportunityResource { @@ -476,6 +531,7 @@ declare module 'closecrm-node' { contact: CustomFieldSubResource; opportunity: CustomFieldSubResource; activity: CustomFieldSubResource; + custom_object_type: CustomFieldSubResource; } export interface CustomActivityResource { @@ -486,6 +542,22 @@ declare module 'closecrm-node' { delete(type: string, id: string): Promise; } + export interface CustomObjectTypeResource { + list(options?: SearchOptions): Promise>; + create(data: Partial): Promise; + read(id: string): Promise; + update(id: string, data: Partial): Promise; + delete(id: string): Promise; + } + + export interface CustomObjectResource { + list(options?: SearchOptions): Promise>; + create(data: Partial): Promise; + read(id: string): Promise; + update(id: string, data: Partial): Promise; + delete(id: string): Promise; + } + export interface UserResource { me(): Promise; list(options?: SearchOptions): Promise>; @@ -553,8 +625,87 @@ declare module 'closecrm-node' { } export interface ReportResource { - list(options?: SearchOptions): Promise>; - read(id: string, options?: SearchOptions): Promise; + // List predefined metrics used in activity reports + activity_metrics(): Promise>; + + // Get an activity report + activity(data: { + datetime_range?: { start: string; end: string }; + relative_range?: string; + query?: any; + users?: string[]; + type: 'overview' | 'comparison'; + metrics: string[]; + }): Promise; + + // Get sent emails report grouped by template + sent_emails(organizationId: string, options?: { + user_id?: string; + date_start?: string; + date_end?: string; + }): Promise; + + // Get lead status change report + lead_statuses(organizationId: string, options?: { + date_start?: string; + date_end?: string; + query?: string; + smart_view_id?: string; + }): Promise; + + // Get opportunity status change report + opportunity_statuses(organizationId: string, options?: { + user_id?: string; + date_start?: string; + date_end?: string; + query?: string; + smart_view_id?: string; + }): Promise; + + // Get custom report + custom(organizationId: string, options?: { + query?: string; + x?: string; + y?: string; + interval?: string; + transform_y?: 'sum' | 'avg' | 'min' | 'max'; + group_by?: string; + start?: string; + end?: string; + }): Promise; + + // Get custom report fields + custom_fields(): Promise; + + // Get opportunity funnel report (totals) + funnel_totals(data: { + pipeline: string; + type: 'created-cohort' | 'active-stage-cohort'; + report_relative_range?: string; + report_datetime_range?: { start: string; end: string }; + cohort_relative_range?: string; + cohort_datetime_range?: { start: string; end: string }; + compared_relative_range?: string; + compared_datetime_range?: string; + compared_custom_range?: { start: string; end: string }; + query?: any; + users?: string[]; + }): Promise; + + // Get opportunity funnel report (stages) + funnel_stages(data: { + pipeline: string; + type: 'created-cohort' | 'active-stage-cohort'; + report_relative_range?: string; + report_datetime_range?: { start: string; end: string }; + cohort_relative_range?: string; + cohort_datetime_range?: { start: string; end: string }; + compared_relative_range?: string; + compared_datetime_range?: string; + compared_custom_range?: { start: string; end: string }; + query?: any; + users?: string[]; + }): Promise; } export interface EventResource { @@ -618,6 +769,8 @@ declare module 'closecrm-node' { task: TaskResource; custom_field: CustomFieldResource; custom_activity: CustomActivityResource; + custom_object_type: CustomObjectTypeResource; + custom_object: CustomObjectResource; user: UserResource; organization: OrganizationResource; pipeline: PipelineResource; diff --git a/lib/close.com.js b/lib/close.com.js index 851b320..7f9a3b6 100644 --- a/lib/close.com.js +++ b/lib/close.com.js @@ -14,6 +14,7 @@ class Closecom { this._initTasks(); this._initCustomFields(); this._initCustomActivities(); + this._initCustomObjects(); this._initUsers(); this._initOrganizations(); this._initPipelines(); @@ -138,6 +139,17 @@ class Closecom { read: (id) => this._get(`/activity/meeting/${id}/`), update: (id, data) => this._put(`/activity/meeting/${id}/`, data), delete: (id) => this._delete(`/activity/meeting/${id}/`) + }, + + whatsapp_message: { + search: (options = {}) => this._get('/activity/whatsapp_message/', this._normalizeSearchParams({ ...options })), + create: (data, queryParams = {}) => { + const path = '/activity/whatsapp_message/'; + return this._request({ method: 'POST', path, body: data, params: queryParams }); + }, + read: (id) => this._get(`/activity/whatsapp_message/${id}/`), + update: (id, data) => this._put(`/activity/whatsapp_message/${id}/`, data), + delete: (id) => this._delete(`/activity/whatsapp_message/${id}/`) } }; } @@ -194,6 +206,14 @@ class Closecom { read: (id) => this._get(`/custom_field/activity/${id}/`), update: (id, data) => this._put(`/custom_field/activity/${id}/`, data), delete: (id) => this._delete(`/custom_field/activity/${id}/`) + }, + + custom_object_type: { + list: () => this._get('/custom_field/custom_object_type/'), + create: (data) => this._post('/custom_field/custom_object_type/', data), + read: (id) => this._get(`/custom_field/custom_object_type/${id}/`), + update: (id, data) => this._put(`/custom_field/custom_object_type/${id}/`, data), + delete: (id) => this._delete(`/custom_field/custom_object_type/${id}/`) } }; } @@ -208,6 +228,24 @@ class Closecom { }; } + _initCustomObjects() { + this.custom_object_type = { + list: (options = {}) => this._get('/custom_object_type/', this._normalizeSearchParams({ ...options })), + create: (data) => this._post('/custom_object_type/', data), + read: (id) => this._get(`/custom_object_type/${id}/`), + update: (id, data) => this._put(`/custom_object_type/${id}/`, data), + delete: (id) => this._delete(`/custom_object_type/${id}/`) + }; + + this.custom_object = { + list: (options = {}) => this._get('/custom_object/', this._normalizeSearchParams({ ...options })), + create: (data) => this._post('/custom_object/', data), + read: (id) => this._get(`/custom_object/${id}/`), + update: (id, data) => this._put(`/custom_object/${id}/`, data), + delete: (id) => this._delete(`/custom_object/${id}/`) + }; + } + _initUsers() { this.user = { me: () => this._get('/me/'), @@ -299,8 +337,36 @@ class Closecom { _initReports() { this.report = { - list: (options = {}) => this._get('/report/', this._normalizeSearchParams({ ...options })), - read: (id, options = {}) => this._get(`/report/${id}/`, this._normalizeSearchParams({ ...options })) + // List predefined metrics used in activity reports + activity_metrics: () => this._get('/report/activity/metrics/'), + + // Get an activity report (POST) + activity: (data) => this._post('/report/activity/', data), + + // Get sent emails report grouped by template + sent_emails: (organizationId, options = {}) => + this._get(`/report/sent_emails/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get lead status change report + lead_statuses: (organizationId, options = {}) => + this._get(`/report/statuses/lead/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get opportunity status change report + opportunity_statuses: (organizationId, options = {}) => + this._get(`/report/statuses/opportunity/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get custom report + custom: (organizationId, options = {}) => + this._get(`/report/custom/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get custom report fields + custom_fields: () => this._get('/report/custom/fields/'), + + // Get opportunity funnel report (totals) + funnel_totals: (data) => this._post('/report/funnel/opportunity/totals/', data), + + // Get opportunity funnel report (stages) + funnel_stages: (data) => this._post('/report/funnel/opportunity/stages/', data) }; } diff --git a/package.json b/package.json index 07dc317..9ae5395 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "closecrm-node", - "version": "1.0.2", + "version": "1.0.3", "description": "Unofficial Close.com API client for Node.js with TypeScript support", "main": "lib/close.com.js", "types": "lib/close.com.d.ts", diff --git a/test/closecom.test.js b/test/closecom.test.js index b2f7ba6..090659d 100644 --- a/test/closecom.test.js +++ b/test/closecom.test.js @@ -71,6 +71,7 @@ describe('Closecom', () => { expect(api.activity.call).to.be.an('object'); expect(api.activity.sms).to.be.an('object'); expect(api.activity.meeting).to.be.an('object'); + expect(api.activity.whatsapp_message).to.be.an('object'); }); it('should initialize opportunity resource', () => { @@ -89,6 +90,34 @@ describe('Closecom', () => { expect(api.custom_field.contact).to.be.an('object'); expect(api.custom_field.opportunity).to.be.an('object'); expect(api.custom_field.activity).to.be.an('object'); + expect(api.custom_field.custom_object_type).to.be.an('object'); + }); + + it('should initialize custom_activity resource', () => { + expect(api.custom_activity).to.be.an('object'); + expect(api.custom_activity.search).to.be.a('function'); + expect(api.custom_activity.create).to.be.a('function'); + expect(api.custom_activity.read).to.be.a('function'); + expect(api.custom_activity.update).to.be.a('function'); + expect(api.custom_activity.delete).to.be.a('function'); + }); + + it('should initialize custom_object_type resource', () => { + expect(api.custom_object_type).to.be.an('object'); + expect(api.custom_object_type.list).to.be.a('function'); + expect(api.custom_object_type.create).to.be.a('function'); + expect(api.custom_object_type.read).to.be.a('function'); + expect(api.custom_object_type.update).to.be.a('function'); + expect(api.custom_object_type.delete).to.be.a('function'); + }); + + it('should initialize custom_object resource', () => { + expect(api.custom_object).to.be.an('object'); + expect(api.custom_object.list).to.be.a('function'); + expect(api.custom_object.create).to.be.a('function'); + expect(api.custom_object.read).to.be.a('function'); + expect(api.custom_object.update).to.be.a('function'); + expect(api.custom_object.delete).to.be.a('function'); }); it('should initialize user resource', () => { @@ -103,6 +132,95 @@ describe('Closecom', () => { expect(api.webhook.create).to.be.a('function'); }); + it('should initialize email_thread resource', () => { + expect(api.email_thread).to.be.an('object'); + expect(api.email_thread.list).to.be.a('function'); + expect(api.email_thread.read).to.be.a('function'); + }); + + it('should initialize connected_account resource', () => { + expect(api.connected_account).to.be.an('object'); + expect(api.connected_account.list).to.be.a('function'); + expect(api.connected_account.read).to.be.a('function'); + }); + + it('should initialize event resource', () => { + expect(api.event).to.be.an('object'); + expect(api.event.search).to.be.a('function'); + expect(api.event.read).to.be.a('function'); + }); + + it('should initialize report resource', () => { + expect(api.report).to.be.an('object'); + expect(api.report.activity_metrics).to.be.a('function'); + expect(api.report.activity).to.be.a('function'); + expect(api.report.sent_emails).to.be.a('function'); + expect(api.report.lead_statuses).to.be.a('function'); + expect(api.report.opportunity_statuses).to.be.a('function'); + expect(api.report.custom).to.be.a('function'); + expect(api.report.custom_fields).to.be.a('function'); + expect(api.report.funnel_totals).to.be.a('function'); + expect(api.report.funnel_stages).to.be.a('function'); + }); + + it('should initialize sequence resource', () => { + expect(api.sequence).to.be.an('object'); + expect(api.sequence.search).to.be.a('function'); + expect(api.sequence.create).to.be.a('function'); + expect(api.sequence.read).to.be.a('function'); + expect(api.sequence.update).to.be.a('function'); + expect(api.sequence.delete).to.be.a('function'); + }); + + it('should initialize sequence_subscription resource', () => { + expect(api.sequence_subscription).to.be.an('object'); + expect(api.sequence_subscription.list).to.be.a('function'); + expect(api.sequence_subscription.create).to.be.a('function'); + expect(api.sequence_subscription.read).to.be.a('function'); + expect(api.sequence_subscription.update).to.be.a('function'); + expect(api.sequence_subscription.delete).to.be.a('function'); + }); + + it('should initialize saved_search resource', () => { + expect(api.saved_search).to.be.an('object'); + expect(api.saved_search.list).to.be.a('function'); + expect(api.saved_search.create).to.be.a('function'); + expect(api.saved_search.read).to.be.a('function'); + expect(api.saved_search.update).to.be.a('function'); + expect(api.saved_search.delete).to.be.a('function'); + }); + + it('should initialize email_template resource', () => { + expect(api.email_template).to.be.an('object'); + expect(api.email_template.search).to.be.a('function'); + expect(api.email_template.create).to.be.a('function'); + expect(api.email_template.read).to.be.a('function'); + expect(api.email_template.update).to.be.a('function'); + expect(api.email_template.delete).to.be.a('function'); + }); + + it('should initialize status resource', () => { + expect(api.status).to.be.an('object'); + expect(api.status.lead).to.be.an('object'); + expect(api.status.opportunity).to.be.an('object'); + }); + + it('should initialize pipeline resource', () => { + expect(api.pipeline).to.be.an('object'); + expect(api.pipeline.list).to.be.a('function'); + expect(api.pipeline.create).to.be.a('function'); + expect(api.pipeline.read).to.be.a('function'); + expect(api.pipeline.update).to.be.a('function'); + expect(api.pipeline.delete).to.be.a('function'); + }); + + it('should initialize organization resource', () => { + expect(api.organization).to.be.an('object'); + expect(api.organization.list).to.be.a('function'); + expect(api.organization.read).to.be.a('function'); + expect(api.organization.update).to.be.a('function'); + }); + it('should initialize bulk resource', () => { expect(api.bulk).to.be.an('object'); expect(api.bulk.delete).to.be.a('function'); @@ -328,4 +446,114 @@ describe('Closecom', () => { } }); }); -}); \ No newline at end of file + + + describe('Activity Sub-Resources', () => { + it('should have all CRUD methods for note', () => { + expect(api.activity.note.search).to.be.a('function'); + expect(api.activity.note.create).to.be.a('function'); + expect(api.activity.note.read).to.be.a('function'); + expect(api.activity.note.update).to.be.a('function'); + expect(api.activity.note.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for email', () => { + expect(api.activity.email.search).to.be.a('function'); + expect(api.activity.email.create).to.be.a('function'); + expect(api.activity.email.read).to.be.a('function'); + expect(api.activity.email.update).to.be.a('function'); + expect(api.activity.email.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for call', () => { + expect(api.activity.call.search).to.be.a('function'); + expect(api.activity.call.create).to.be.a('function'); + expect(api.activity.call.read).to.be.a('function'); + expect(api.activity.call.update).to.be.a('function'); + expect(api.activity.call.delete).to.be.a('function'); + }); + + it('should have all methods for sms (no update)', () => { + expect(api.activity.sms.search).to.be.a('function'); + expect(api.activity.sms.create).to.be.a('function'); + expect(api.activity.sms.read).to.be.a('function'); + expect(api.activity.sms.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for meeting', () => { + expect(api.activity.meeting.search).to.be.a('function'); + expect(api.activity.meeting.create).to.be.a('function'); + expect(api.activity.meeting.read).to.be.a('function'); + expect(api.activity.meeting.update).to.be.a('function'); + expect(api.activity.meeting.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for whatsapp_message', () => { + expect(api.activity.whatsapp_message.search).to.be.a('function'); + expect(api.activity.whatsapp_message.create).to.be.a('function'); + expect(api.activity.whatsapp_message.read).to.be.a('function'); + expect(api.activity.whatsapp_message.update).to.be.a('function'); + expect(api.activity.whatsapp_message.delete).to.be.a('function'); + }); + }); + + describe('Custom Field Sub-Resources', () => { + it('should have all methods for lead custom fields', () => { + expect(api.custom_field.lead.list).to.be.a('function'); + expect(api.custom_field.lead.create).to.be.a('function'); + expect(api.custom_field.lead.read).to.be.a('function'); + expect(api.custom_field.lead.update).to.be.a('function'); + expect(api.custom_field.lead.delete).to.be.a('function'); + }); + + it('should have all methods for contact custom fields', () => { + expect(api.custom_field.contact.list).to.be.a('function'); + expect(api.custom_field.contact.create).to.be.a('function'); + expect(api.custom_field.contact.read).to.be.a('function'); + expect(api.custom_field.contact.update).to.be.a('function'); + expect(api.custom_field.contact.delete).to.be.a('function'); + }); + + it('should have all methods for opportunity custom fields', () => { + expect(api.custom_field.opportunity.list).to.be.a('function'); + expect(api.custom_field.opportunity.create).to.be.a('function'); + expect(api.custom_field.opportunity.read).to.be.a('function'); + expect(api.custom_field.opportunity.update).to.be.a('function'); + expect(api.custom_field.opportunity.delete).to.be.a('function'); + }); + + it('should have all methods for activity custom fields', () => { + expect(api.custom_field.activity.list).to.be.a('function'); + expect(api.custom_field.activity.create).to.be.a('function'); + expect(api.custom_field.activity.read).to.be.a('function'); + expect(api.custom_field.activity.update).to.be.a('function'); + expect(api.custom_field.activity.delete).to.be.a('function'); + }); + + it('should have all methods for custom_object_type custom fields', () => { + expect(api.custom_field.custom_object_type.list).to.be.a('function'); + expect(api.custom_field.custom_object_type.create).to.be.a('function'); + expect(api.custom_field.custom_object_type.read).to.be.a('function'); + expect(api.custom_field.custom_object_type.update).to.be.a('function'); + expect(api.custom_field.custom_object_type.delete).to.be.a('function'); + }); + }); + + describe('Status Sub-Resources', () => { + it('should have all methods for lead status', () => { + expect(api.status.lead.list).to.be.a('function'); + expect(api.status.lead.create).to.be.a('function'); + expect(api.status.lead.read).to.be.a('function'); + expect(api.status.lead.update).to.be.a('function'); + expect(api.status.lead.delete).to.be.a('function'); + }); + + it('should have all methods for opportunity status', () => { + expect(api.status.opportunity.list).to.be.a('function'); + expect(api.status.opportunity.create).to.be.a('function'); + expect(api.status.opportunity.read).to.be.a('function'); + expect(api.status.opportunity.update).to.be.a('function'); + expect(api.status.opportunity.delete).to.be.a('function'); + }); + }); +}); diff --git a/test/manual-test.js b/test/manual-test.js index d290e9e..88ed23c 100644 --- a/test/manual-test.js +++ b/test/manual-test.js @@ -119,8 +119,71 @@ async function runTests() { console.log('✅ Streamed leads:', streamCount); console.log(); - // Test 11: Error Handling - console.log('1️⃣1️⃣ Testing: Error Handling'); + // Test 11: WhatsApp Message Activity + console.log('1️⃣1️⃣ Testing: api.activity.whatsapp_message.search()'); + const whatsappResult = await api.activity.whatsapp_message.search({ _limit: 5 }); + console.log('✅ WhatsApp messages found:', whatsappResult.data.length); + console.log(); + + // Test 12: Custom Objects + console.log('1️⃣2️⃣ Testing: api.custom_object_type.list()'); + const customObjectTypes = await api.custom_object_type.list(); + console.log('✅ Custom Object Types found:', customObjectTypes.data.length); + if (customObjectTypes.data.length > 0) { + console.log(' First type:', customObjectTypes.data[0].name); + } + console.log(); + + // Test 13: Email Threads + console.log('1️⃣3️⃣ Testing: api.email_thread.list()'); + const emailThreads = await api.email_thread.list({ _limit: 5 }); + console.log('✅ Email threads found:', emailThreads.data.length); + console.log(); + + // Test 14: Connected Accounts + console.log('1️⃣4️⃣ Testing: api.connected_account.list()'); + const connectedAccounts = await api.connected_account.list(); + console.log('✅ Connected accounts found:', connectedAccounts.data.length); + console.log(); + + // Test 15: Events + console.log('1️⃣5️⃣ Testing: api.event.search()'); + const events = await api.event.search({ _limit: 5 }); + console.log('✅ Events found:', events.data.length); + console.log(); + + // Test 16: Reports (Activity Metrics) + console.log('1️⃣6️⃣ Testing: api.report.activity_metrics()'); + const activityMetrics = await api.report.activity_metrics(); + console.log('✅ Activity metrics available:', Array.isArray(activityMetrics) ? activityMetrics.length : 'Response received'); + console.log(); + + // Test 17: Sequences + console.log('1️⃣7️⃣ Testing: api.sequence.search()'); + const sequences = await api.sequence.search({ _limit: 5 }); + console.log('✅ Sequences found:', sequences.data.length); + console.log(); + + // Test 18: Email Templates + console.log('1️⃣8️⃣ Testing: api.email_template.search()'); + const templates = await api.email_template.search({ _limit: 5 }); + console.log('✅ Email templates found:', templates.data.length); + console.log(); + + // Test 19: Pipelines + console.log('1️⃣9️⃣ Testing: api.pipeline.list()'); + const pipelines = await api.pipeline.list(); + console.log('✅ Pipelines found:', pipelines.data.length); + console.log(); + + // Test 20: Lead Status + console.log('2️⃣0️⃣ Testing: api.status.lead.list()'); + const leadStatuses = await api.status.lead.list(); + console.log('✅ Lead statuses found:', leadStatuses.data.length); + console.log(); + + // Test 21: Error Handling + console.log('2️⃣1️⃣ Testing: Error Handling'); try { await api.lead.read('invalid_id_12345'); } catch (error) { @@ -130,8 +193,8 @@ async function runTests() { } console.log(); - // Test 12: Validation - console.log('1️⃣2️⃣ Testing: Input Validation'); + // Test 22: Validation + console.log('2️⃣2️⃣ Testing: Input Validation'); try { await api.lead.create({}); } catch (error) {