@clianta/sdk 1.7.1 → 1.7.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/dist/angular.cjs.js +54 -48
- package/dist/angular.cjs.js.map +1 -1
- package/dist/angular.d.ts +12 -9
- package/dist/angular.esm.js +54 -48
- package/dist/angular.esm.js.map +1 -1
- package/dist/clianta.cjs.js +1238 -48
- package/dist/clianta.cjs.js.map +1 -1
- package/dist/clianta.esm.js +1238 -49
- package/dist/clianta.esm.js.map +1 -1
- package/dist/clianta.umd.js +1238 -48
- package/dist/clianta.umd.js.map +1 -1
- package/dist/clianta.umd.min.js +2 -2
- package/dist/clianta.umd.min.js.map +1 -1
- package/dist/index.d.ts +832 -12
- package/dist/react.cjs.js +54 -48
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.d.ts +12 -9
- package/dist/react.esm.js +54 -48
- package/dist/react.esm.js.map +1 -1
- package/dist/svelte.cjs.js +54 -48
- package/dist/svelte.cjs.js.map +1 -1
- package/dist/svelte.d.ts +12 -9
- package/dist/svelte.esm.js +54 -48
- package/dist/svelte.esm.js.map +1 -1
- package/dist/vue.cjs.js +54 -48
- package/dist/vue.cjs.js.map +1 -1
- package/dist/vue.d.ts +12 -9
- package/dist/vue.esm.js +54 -48
- package/dist/vue.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/clianta.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Clianta SDK v1.7.
|
|
2
|
+
* Clianta SDK v1.7.3
|
|
3
3
|
* (c) 2026 Clianta
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -13,7 +13,7 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
|
|
|
13
13
|
* @see SDK_VERSION in core/config.ts
|
|
14
14
|
*/
|
|
15
15
|
/** SDK Version */
|
|
16
|
-
const SDK_VERSION = '1.7.
|
|
16
|
+
const SDK_VERSION = '1.7.2';
|
|
17
17
|
/** Default API endpoint — reads from env or falls back to localhost */
|
|
18
18
|
const getDefaultApiEndpoint = () => {
|
|
19
19
|
// Next.js (process.env)
|
|
@@ -224,8 +224,9 @@ class Transport {
|
|
|
224
224
|
/**
|
|
225
225
|
* Send identify request.
|
|
226
226
|
* Returns contactId from the server response so the Tracker can store it.
|
|
227
|
+
* Retries on 5xx with exponential backoff (same policy as sendEvents).
|
|
227
228
|
*/
|
|
228
|
-
async sendIdentify(data) {
|
|
229
|
+
async sendIdentify(data, attempt = 1) {
|
|
229
230
|
const url = `${this.config.apiEndpoint}/api/public/track/identify`;
|
|
230
231
|
try {
|
|
231
232
|
const response = await this.fetchWithTimeout(url, {
|
|
@@ -243,16 +244,26 @@ class Transport {
|
|
|
243
244
|
contactId: body.contactId ?? undefined,
|
|
244
245
|
};
|
|
245
246
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
// Server error — retry with exponential backoff
|
|
248
|
+
if (response.status >= 500 && attempt < this.config.maxRetries) {
|
|
249
|
+
const backoff = this.config.retryDelay * Math.pow(2, attempt - 1);
|
|
250
|
+
logger.warn(`Identify server error (${response.status}), retrying in ${backoff}ms...`);
|
|
251
|
+
await this.delay(backoff);
|
|
252
|
+
return this.sendIdentify(data, attempt + 1);
|
|
251
253
|
}
|
|
254
|
+
logger.error(`Identify failed with status ${response.status}:`, body.message);
|
|
252
255
|
return { success: false, status: response.status };
|
|
253
256
|
}
|
|
254
257
|
catch (error) {
|
|
255
|
-
|
|
258
|
+
// Network error — retry if still online
|
|
259
|
+
const isOnline = typeof navigator === 'undefined' || navigator.onLine;
|
|
260
|
+
if (isOnline && attempt < this.config.maxRetries) {
|
|
261
|
+
const backoff = this.config.retryDelay * Math.pow(2, attempt - 1);
|
|
262
|
+
logger.warn(`Identify network error, retrying in ${backoff}ms (${attempt}/${this.config.maxRetries})...`);
|
|
263
|
+
await this.delay(backoff);
|
|
264
|
+
return this.sendIdentify(data, attempt + 1);
|
|
265
|
+
}
|
|
266
|
+
logger.error('Identify request failed after retries:', error);
|
|
256
267
|
return { success: false, error: error };
|
|
257
268
|
}
|
|
258
269
|
}
|
|
@@ -570,9 +581,6 @@ function resetIds(useCookies = false) {
|
|
|
570
581
|
// Ignore
|
|
571
582
|
}
|
|
572
583
|
}
|
|
573
|
-
// ============================================
|
|
574
|
-
// URL UTILITIES
|
|
575
|
-
// ============================================
|
|
576
584
|
/**
|
|
577
585
|
* Extract UTM parameters from URL
|
|
578
586
|
*/
|
|
@@ -729,8 +737,9 @@ class EventQueue {
|
|
|
729
737
|
flushInterval: config.flushInterval ?? 5000,
|
|
730
738
|
maxQueueSize: config.maxQueueSize ?? MAX_QUEUE_SIZE,
|
|
731
739
|
storageKey: config.storageKey ?? STORAGE_KEYS.EVENT_QUEUE,
|
|
740
|
+
persistMode: config.persistMode ?? 'session',
|
|
732
741
|
};
|
|
733
|
-
this.persistMode = config.persistMode
|
|
742
|
+
this.persistMode = this.config.persistMode;
|
|
734
743
|
this.isOnline = typeof navigator === 'undefined' || navigator.onLine;
|
|
735
744
|
// Restore persisted queue
|
|
736
745
|
this.restoreQueue();
|
|
@@ -794,9 +803,11 @@ class EventQueue {
|
|
|
794
803
|
// Send to backend
|
|
795
804
|
const result = await this.transport.sendEvents(events);
|
|
796
805
|
if (!result.success) {
|
|
797
|
-
// Re-queue events on failure (at the front)
|
|
806
|
+
// Re-queue events on failure (at the front), capped at maxQueueSize
|
|
798
807
|
logger.warn('Flush failed, re-queuing events');
|
|
799
|
-
this.queue.
|
|
808
|
+
const availableSpace = this.config.maxQueueSize - this.queue.length;
|
|
809
|
+
const eventsToRequeue = events.slice(0, Math.max(0, availableSpace));
|
|
810
|
+
this.queue.unshift(...eventsToRequeue);
|
|
800
811
|
this.persistQueue(this.queue);
|
|
801
812
|
}
|
|
802
813
|
else {
|
|
@@ -1243,7 +1254,7 @@ class FormsPlugin extends BasePlugin {
|
|
|
1243
1254
|
if (this.trackedForms.has(form))
|
|
1244
1255
|
return;
|
|
1245
1256
|
this.trackedForms.add(form);
|
|
1246
|
-
const formId = form.id || form.name || `form-${Math.random().toString(36).
|
|
1257
|
+
const formId = form.id || form.name || `form-${Math.random().toString(36).substring(2, 11)}`;
|
|
1247
1258
|
// Track form view
|
|
1248
1259
|
this.track('form_view', 'Form Viewed', {
|
|
1249
1260
|
formId,
|
|
@@ -1887,27 +1898,22 @@ class PopupFormsPlugin extends BasePlugin {
|
|
|
1887
1898
|
super.destroy();
|
|
1888
1899
|
}
|
|
1889
1900
|
loadShownForms() {
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1901
|
+
const stored = getLocalStorage('clianta_shown_forms');
|
|
1902
|
+
if (stored) {
|
|
1903
|
+
try {
|
|
1893
1904
|
const data = JSON.parse(stored);
|
|
1894
1905
|
this.shownForms = new Set(data.forms || []);
|
|
1895
1906
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1907
|
+
catch {
|
|
1908
|
+
// Ignore parse errors
|
|
1909
|
+
}
|
|
1899
1910
|
}
|
|
1900
1911
|
}
|
|
1901
1912
|
saveShownForms() {
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
}));
|
|
1907
|
-
}
|
|
1908
|
-
catch (e) {
|
|
1909
|
-
// Ignore storage errors
|
|
1910
|
-
}
|
|
1913
|
+
setLocalStorage('clianta_shown_forms', JSON.stringify({
|
|
1914
|
+
forms: Array.from(this.shownForms),
|
|
1915
|
+
timestamp: Date.now(),
|
|
1916
|
+
}));
|
|
1911
1917
|
}
|
|
1912
1918
|
async fetchForms() {
|
|
1913
1919
|
if (!this.tracker)
|
|
@@ -1926,7 +1932,7 @@ class PopupFormsPlugin extends BasePlugin {
|
|
|
1926
1932
|
}
|
|
1927
1933
|
}
|
|
1928
1934
|
catch (error) {
|
|
1929
|
-
|
|
1935
|
+
logger.error('Failed to fetch popup forms:', error);
|
|
1930
1936
|
}
|
|
1931
1937
|
}
|
|
1932
1938
|
shouldShowForm(form) {
|
|
@@ -1937,7 +1943,7 @@ class PopupFormsPlugin extends BasePlugin {
|
|
|
1937
1943
|
}
|
|
1938
1944
|
else if (form.showFrequency === 'once_per_session') {
|
|
1939
1945
|
const sessionKey = `clianta_form_${form._id}_shown`;
|
|
1940
|
-
if (
|
|
1946
|
+
if (getSessionStorage(sessionKey))
|
|
1941
1947
|
return false;
|
|
1942
1948
|
}
|
|
1943
1949
|
return true;
|
|
@@ -2009,7 +2015,7 @@ class PopupFormsPlugin extends BasePlugin {
|
|
|
2009
2015
|
// Mark as shown
|
|
2010
2016
|
this.shownForms.add(form._id);
|
|
2011
2017
|
this.saveShownForms();
|
|
2012
|
-
|
|
2018
|
+
setSessionStorage(`clianta_form_${form._id}_shown`, 'true');
|
|
2013
2019
|
// Track view
|
|
2014
2020
|
await this.trackFormView(form._id);
|
|
2015
2021
|
// Render form
|
|
@@ -2325,17 +2331,17 @@ class PopupFormsPlugin extends BasePlugin {
|
|
|
2325
2331
|
const redirect = new URL(form.redirectUrl, window.location.origin);
|
|
2326
2332
|
const isSameOrigin = redirect.origin === window.location.origin;
|
|
2327
2333
|
const isSafeProtocol = redirect.protocol === 'https:' || redirect.protocol === 'http:';
|
|
2328
|
-
if (isSameOrigin
|
|
2334
|
+
if (isSameOrigin && isSafeProtocol) {
|
|
2329
2335
|
setTimeout(() => {
|
|
2330
2336
|
window.location.href = redirect.href;
|
|
2331
2337
|
}, 1500);
|
|
2332
2338
|
}
|
|
2333
2339
|
else {
|
|
2334
|
-
|
|
2340
|
+
logger.warn('Blocked unsafe redirect URL:', form.redirectUrl);
|
|
2335
2341
|
}
|
|
2336
2342
|
}
|
|
2337
2343
|
catch {
|
|
2338
|
-
|
|
2344
|
+
logger.warn('Invalid redirect URL:', form.redirectUrl);
|
|
2339
2345
|
}
|
|
2340
2346
|
}
|
|
2341
2347
|
// Close after delay
|
|
@@ -2348,7 +2354,7 @@ class PopupFormsPlugin extends BasePlugin {
|
|
|
2348
2354
|
}
|
|
2349
2355
|
}
|
|
2350
2356
|
catch (error) {
|
|
2351
|
-
|
|
2357
|
+
logger.error('Form submit error:', error);
|
|
2352
2358
|
if (submitBtn) {
|
|
2353
2359
|
submitBtn.disabled = false;
|
|
2354
2360
|
submitBtn.textContent = form.submitButtonText || 'Subscribe';
|
|
@@ -2419,7 +2425,7 @@ const STORAGE_KEY_PATTERNS = [
|
|
|
2419
2425
|
'token', 'jwt', 'auth', 'user', 'session', 'credential', 'account',
|
|
2420
2426
|
];
|
|
2421
2427
|
/** JWT/user object fields containing email */
|
|
2422
|
-
const EMAIL_CLAIMS = ['email', '
|
|
2428
|
+
const EMAIL_CLAIMS = ['email', 'preferred_username', 'user_email', 'mail', 'emailAddress', 'e_mail'];
|
|
2423
2429
|
/** Full name fields */
|
|
2424
2430
|
const NAME_CLAIMS = ['name', 'full_name', 'display_name', 'displayName'];
|
|
2425
2431
|
/** First name fields */
|
|
@@ -3363,6 +3369,7 @@ class Tracker {
|
|
|
3363
3369
|
this.queue = new EventQueue(this.transport, {
|
|
3364
3370
|
batchSize: this.config.batchSize,
|
|
3365
3371
|
flushInterval: this.config.flushInterval,
|
|
3372
|
+
persistMode: this.config.persistMode,
|
|
3366
3373
|
});
|
|
3367
3374
|
// Get or create visitor and session IDs based on mode
|
|
3368
3375
|
this.visitorId = this.createVisitorId();
|
|
@@ -3479,10 +3486,13 @@ class Tracker {
|
|
|
3479
3486
|
logger.warn('SDK not initialized, event dropped');
|
|
3480
3487
|
return;
|
|
3481
3488
|
}
|
|
3489
|
+
const utmParams = getUTMParams();
|
|
3482
3490
|
const event = {
|
|
3483
3491
|
workspaceId: this.workspaceId,
|
|
3484
3492
|
visitorId: this.visitorId,
|
|
3485
3493
|
sessionId: this.sessionId,
|
|
3494
|
+
contactId: this.contactId ?? undefined,
|
|
3495
|
+
groupId: this.groupId ?? undefined,
|
|
3486
3496
|
eventType: eventType,
|
|
3487
3497
|
eventName,
|
|
3488
3498
|
url: typeof window !== 'undefined' ? window.location.href : '',
|
|
@@ -3493,18 +3503,14 @@ class Tracker {
|
|
|
3493
3503
|
websiteDomain: typeof window !== 'undefined' ? window.location.hostname : undefined,
|
|
3494
3504
|
},
|
|
3495
3505
|
device: getDeviceInfo(),
|
|
3496
|
-
|
|
3506
|
+
utmSource: utmParams.utmSource,
|
|
3507
|
+
utmMedium: utmParams.utmMedium,
|
|
3508
|
+
utmCampaign: utmParams.utmCampaign,
|
|
3509
|
+
utmTerm: utmParams.utmTerm,
|
|
3510
|
+
utmContent: utmParams.utmContent,
|
|
3497
3511
|
timestamp: new Date().toISOString(),
|
|
3498
3512
|
sdkVersion: SDK_VERSION,
|
|
3499
3513
|
};
|
|
3500
|
-
// Attach contactId if known (from a prior identify() call)
|
|
3501
|
-
if (this.contactId) {
|
|
3502
|
-
event.contactId = this.contactId;
|
|
3503
|
-
}
|
|
3504
|
-
// Attach groupId if known (from a prior group() call)
|
|
3505
|
-
if (this.groupId) {
|
|
3506
|
-
event.groupId = this.groupId;
|
|
3507
|
-
}
|
|
3508
3514
|
// Validate event against registered schema (debug mode only)
|
|
3509
3515
|
this.validateEventSchema(eventType, properties);
|
|
3510
3516
|
// Check consent before tracking
|
|
@@ -3965,6 +3971,1189 @@ class Tracker {
|
|
|
3965
3971
|
}
|
|
3966
3972
|
}
|
|
3967
3973
|
|
|
3974
|
+
/**
|
|
3975
|
+
* Clianta SDK - Event Triggers Manager
|
|
3976
|
+
* Manages event-driven automation and email notifications
|
|
3977
|
+
*/
|
|
3978
|
+
/**
|
|
3979
|
+
* Event Triggers Manager
|
|
3980
|
+
* Handles event-driven automation based on CRM actions
|
|
3981
|
+
*
|
|
3982
|
+
* Similar to:
|
|
3983
|
+
* - Salesforce: Process Builder, Flow Automation
|
|
3984
|
+
* - HubSpot: Workflows, Email Sequences
|
|
3985
|
+
* - Pipedrive: Workflow Automation
|
|
3986
|
+
*/
|
|
3987
|
+
class EventTriggersManager {
|
|
3988
|
+
constructor(apiEndpoint, workspaceId, authToken) {
|
|
3989
|
+
this.triggers = new Map();
|
|
3990
|
+
this.listeners = new Map();
|
|
3991
|
+
this.apiEndpoint = apiEndpoint;
|
|
3992
|
+
this.workspaceId = workspaceId;
|
|
3993
|
+
this.authToken = authToken;
|
|
3994
|
+
}
|
|
3995
|
+
/**
|
|
3996
|
+
* Set authentication token
|
|
3997
|
+
*/
|
|
3998
|
+
setAuthToken(token) {
|
|
3999
|
+
this.authToken = token;
|
|
4000
|
+
}
|
|
4001
|
+
/**
|
|
4002
|
+
* Make authenticated API request
|
|
4003
|
+
*/
|
|
4004
|
+
async request(endpoint, options = {}) {
|
|
4005
|
+
const url = `${this.apiEndpoint}${endpoint}`;
|
|
4006
|
+
const headers = {
|
|
4007
|
+
'Content-Type': 'application/json',
|
|
4008
|
+
...(options.headers || {}),
|
|
4009
|
+
};
|
|
4010
|
+
if (this.authToken) {
|
|
4011
|
+
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
4012
|
+
}
|
|
4013
|
+
try {
|
|
4014
|
+
const response = await fetch(url, {
|
|
4015
|
+
...options,
|
|
4016
|
+
headers,
|
|
4017
|
+
});
|
|
4018
|
+
const data = await response.json();
|
|
4019
|
+
if (!response.ok) {
|
|
4020
|
+
return {
|
|
4021
|
+
success: false,
|
|
4022
|
+
error: data.message || 'Request failed',
|
|
4023
|
+
status: response.status,
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
return {
|
|
4027
|
+
success: true,
|
|
4028
|
+
data: data.data || data,
|
|
4029
|
+
status: response.status,
|
|
4030
|
+
};
|
|
4031
|
+
}
|
|
4032
|
+
catch (error) {
|
|
4033
|
+
return {
|
|
4034
|
+
success: false,
|
|
4035
|
+
error: error instanceof Error ? error.message : 'Network error',
|
|
4036
|
+
status: 0,
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
// ============================================
|
|
4041
|
+
// TRIGGER MANAGEMENT
|
|
4042
|
+
// ============================================
|
|
4043
|
+
/**
|
|
4044
|
+
* Get all event triggers
|
|
4045
|
+
*/
|
|
4046
|
+
async getTriggers() {
|
|
4047
|
+
return this.request(`/api/workspaces/${this.workspaceId}/triggers`);
|
|
4048
|
+
}
|
|
4049
|
+
/**
|
|
4050
|
+
* Get a single trigger by ID
|
|
4051
|
+
*/
|
|
4052
|
+
async getTrigger(triggerId) {
|
|
4053
|
+
return this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`);
|
|
4054
|
+
}
|
|
4055
|
+
/**
|
|
4056
|
+
* Create a new event trigger
|
|
4057
|
+
*/
|
|
4058
|
+
async createTrigger(trigger) {
|
|
4059
|
+
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers`, {
|
|
4060
|
+
method: 'POST',
|
|
4061
|
+
body: JSON.stringify(trigger),
|
|
4062
|
+
});
|
|
4063
|
+
// Cache the trigger locally if successful
|
|
4064
|
+
if (result.success && result.data?._id) {
|
|
4065
|
+
this.triggers.set(result.data._id, result.data);
|
|
4066
|
+
}
|
|
4067
|
+
return result;
|
|
4068
|
+
}
|
|
4069
|
+
/**
|
|
4070
|
+
* Update an existing trigger
|
|
4071
|
+
*/
|
|
4072
|
+
async updateTrigger(triggerId, updates) {
|
|
4073
|
+
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
|
|
4074
|
+
method: 'PUT',
|
|
4075
|
+
body: JSON.stringify(updates),
|
|
4076
|
+
});
|
|
4077
|
+
// Update cache if successful
|
|
4078
|
+
if (result.success && result.data?._id) {
|
|
4079
|
+
this.triggers.set(result.data._id, result.data);
|
|
4080
|
+
}
|
|
4081
|
+
return result;
|
|
4082
|
+
}
|
|
4083
|
+
/**
|
|
4084
|
+
* Delete a trigger
|
|
4085
|
+
*/
|
|
4086
|
+
async deleteTrigger(triggerId) {
|
|
4087
|
+
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
|
|
4088
|
+
method: 'DELETE',
|
|
4089
|
+
});
|
|
4090
|
+
// Remove from cache if successful
|
|
4091
|
+
if (result.success) {
|
|
4092
|
+
this.triggers.delete(triggerId);
|
|
4093
|
+
}
|
|
4094
|
+
return result;
|
|
4095
|
+
}
|
|
4096
|
+
/**
|
|
4097
|
+
* Activate a trigger
|
|
4098
|
+
*/
|
|
4099
|
+
async activateTrigger(triggerId) {
|
|
4100
|
+
return this.updateTrigger(triggerId, { isActive: true });
|
|
4101
|
+
}
|
|
4102
|
+
/**
|
|
4103
|
+
* Deactivate a trigger
|
|
4104
|
+
*/
|
|
4105
|
+
async deactivateTrigger(triggerId) {
|
|
4106
|
+
return this.updateTrigger(triggerId, { isActive: false });
|
|
4107
|
+
}
|
|
4108
|
+
// ============================================
|
|
4109
|
+
// EVENT HANDLING (CLIENT-SIDE)
|
|
4110
|
+
// ============================================
|
|
4111
|
+
/**
|
|
4112
|
+
* Register a local event listener for client-side triggers
|
|
4113
|
+
* This allows immediate client-side reactions to events
|
|
4114
|
+
*/
|
|
4115
|
+
on(eventType, callback) {
|
|
4116
|
+
if (!this.listeners.has(eventType)) {
|
|
4117
|
+
this.listeners.set(eventType, new Set());
|
|
4118
|
+
}
|
|
4119
|
+
this.listeners.get(eventType).add(callback);
|
|
4120
|
+
logger.debug(`Event listener registered: ${eventType}`);
|
|
4121
|
+
}
|
|
4122
|
+
/**
|
|
4123
|
+
* Remove an event listener
|
|
4124
|
+
*/
|
|
4125
|
+
off(eventType, callback) {
|
|
4126
|
+
const listeners = this.listeners.get(eventType);
|
|
4127
|
+
if (listeners) {
|
|
4128
|
+
listeners.delete(callback);
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
/**
|
|
4132
|
+
* Emit an event (client-side only)
|
|
4133
|
+
* This will trigger any registered local listeners
|
|
4134
|
+
*/
|
|
4135
|
+
emit(eventType, data) {
|
|
4136
|
+
logger.debug(`Event emitted: ${eventType}`, data);
|
|
4137
|
+
const listeners = this.listeners.get(eventType);
|
|
4138
|
+
if (listeners) {
|
|
4139
|
+
listeners.forEach(callback => {
|
|
4140
|
+
try {
|
|
4141
|
+
callback(data);
|
|
4142
|
+
}
|
|
4143
|
+
catch (error) {
|
|
4144
|
+
logger.error(`Error in event listener for ${eventType}:`, error);
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
/**
|
|
4150
|
+
* Check if conditions are met for a trigger
|
|
4151
|
+
* Supports dynamic field evaluation including custom fields and nested paths
|
|
4152
|
+
*/
|
|
4153
|
+
evaluateConditions(conditions, data) {
|
|
4154
|
+
if (!conditions || conditions.length === 0) {
|
|
4155
|
+
return true; // No conditions means always fire
|
|
4156
|
+
}
|
|
4157
|
+
return conditions.every(condition => {
|
|
4158
|
+
// Support dot notation for nested fields (e.g., 'customFields.industry')
|
|
4159
|
+
const fieldValue = condition.field.includes('.')
|
|
4160
|
+
? this.getNestedValue(data, condition.field)
|
|
4161
|
+
: data[condition.field];
|
|
4162
|
+
const targetValue = condition.value;
|
|
4163
|
+
switch (condition.operator) {
|
|
4164
|
+
case 'equals':
|
|
4165
|
+
return fieldValue === targetValue;
|
|
4166
|
+
case 'not_equals':
|
|
4167
|
+
return fieldValue !== targetValue;
|
|
4168
|
+
case 'contains':
|
|
4169
|
+
return String(fieldValue).includes(String(targetValue));
|
|
4170
|
+
case 'greater_than':
|
|
4171
|
+
return Number(fieldValue) > Number(targetValue);
|
|
4172
|
+
case 'less_than':
|
|
4173
|
+
return Number(fieldValue) < Number(targetValue);
|
|
4174
|
+
case 'in':
|
|
4175
|
+
return Array.isArray(targetValue) && targetValue.includes(fieldValue);
|
|
4176
|
+
case 'not_in':
|
|
4177
|
+
return Array.isArray(targetValue) && !targetValue.includes(fieldValue);
|
|
4178
|
+
default:
|
|
4179
|
+
return false;
|
|
4180
|
+
}
|
|
4181
|
+
});
|
|
4182
|
+
}
|
|
4183
|
+
/**
|
|
4184
|
+
* Execute actions for a triggered event (client-side preview)
|
|
4185
|
+
* Note: Actual execution happens on the backend
|
|
4186
|
+
*/
|
|
4187
|
+
async executeActions(trigger, data) {
|
|
4188
|
+
logger.info(`Executing actions for trigger: ${trigger.name}`);
|
|
4189
|
+
for (const action of trigger.actions) {
|
|
4190
|
+
try {
|
|
4191
|
+
await this.executeAction(action, data);
|
|
4192
|
+
}
|
|
4193
|
+
catch (error) {
|
|
4194
|
+
logger.error(`Failed to execute action:`, error);
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
/**
|
|
4199
|
+
* Execute a single action
|
|
4200
|
+
*/
|
|
4201
|
+
async executeAction(action, data) {
|
|
4202
|
+
switch (action.type) {
|
|
4203
|
+
case 'send_email':
|
|
4204
|
+
await this.executeSendEmail(action, data);
|
|
4205
|
+
break;
|
|
4206
|
+
case 'webhook':
|
|
4207
|
+
await this.executeWebhook(action, data);
|
|
4208
|
+
break;
|
|
4209
|
+
case 'create_task':
|
|
4210
|
+
await this.executeCreateTask(action, data);
|
|
4211
|
+
break;
|
|
4212
|
+
case 'update_contact':
|
|
4213
|
+
await this.executeUpdateContact(action, data);
|
|
4214
|
+
break;
|
|
4215
|
+
default:
|
|
4216
|
+
logger.warn(`Unknown action type:`, action);
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
/**
|
|
4220
|
+
* Execute send email action (via backend API)
|
|
4221
|
+
*/
|
|
4222
|
+
async executeSendEmail(action, data) {
|
|
4223
|
+
logger.debug('Sending email:', action);
|
|
4224
|
+
const payload = {
|
|
4225
|
+
to: this.replaceVariables(action.to, data),
|
|
4226
|
+
subject: action.subject ? this.replaceVariables(action.subject, data) : undefined,
|
|
4227
|
+
body: action.body ? this.replaceVariables(action.body, data) : undefined,
|
|
4228
|
+
templateId: action.templateId,
|
|
4229
|
+
cc: action.cc,
|
|
4230
|
+
bcc: action.bcc,
|
|
4231
|
+
from: action.from,
|
|
4232
|
+
delayMinutes: action.delayMinutes,
|
|
4233
|
+
};
|
|
4234
|
+
await this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
|
|
4235
|
+
method: 'POST',
|
|
4236
|
+
body: JSON.stringify(payload),
|
|
4237
|
+
});
|
|
4238
|
+
}
|
|
4239
|
+
/**
|
|
4240
|
+
* Execute webhook action
|
|
4241
|
+
*/
|
|
4242
|
+
async executeWebhook(action, data) {
|
|
4243
|
+
logger.debug('Calling webhook:', action.url);
|
|
4244
|
+
const body = action.body ? this.replaceVariables(action.body, data) : JSON.stringify(data);
|
|
4245
|
+
await fetch(action.url, {
|
|
4246
|
+
method: action.method,
|
|
4247
|
+
headers: {
|
|
4248
|
+
'Content-Type': 'application/json',
|
|
4249
|
+
...action.headers,
|
|
4250
|
+
},
|
|
4251
|
+
body,
|
|
4252
|
+
});
|
|
4253
|
+
}
|
|
4254
|
+
/**
|
|
4255
|
+
* Execute create task action
|
|
4256
|
+
*/
|
|
4257
|
+
async executeCreateTask(action, data) {
|
|
4258
|
+
logger.debug('Creating task:', action.title);
|
|
4259
|
+
const dueDate = action.dueDays
|
|
4260
|
+
? new Date(Date.now() + action.dueDays * 24 * 60 * 60 * 1000).toISOString()
|
|
4261
|
+
: undefined;
|
|
4262
|
+
await this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
|
|
4263
|
+
method: 'POST',
|
|
4264
|
+
body: JSON.stringify({
|
|
4265
|
+
title: this.replaceVariables(action.title, data),
|
|
4266
|
+
description: action.description ? this.replaceVariables(action.description, data) : undefined,
|
|
4267
|
+
priority: action.priority,
|
|
4268
|
+
dueDate,
|
|
4269
|
+
assignedTo: action.assignedTo,
|
|
4270
|
+
relatedContactId: typeof data.contactId === 'string' ? data.contactId : undefined,
|
|
4271
|
+
}),
|
|
4272
|
+
});
|
|
4273
|
+
}
|
|
4274
|
+
/**
|
|
4275
|
+
* Execute update contact action
|
|
4276
|
+
*/
|
|
4277
|
+
async executeUpdateContact(action, data) {
|
|
4278
|
+
const contactId = data.contactId || data._id;
|
|
4279
|
+
if (!contactId) {
|
|
4280
|
+
logger.warn('Cannot update contact: no contactId in data');
|
|
4281
|
+
return;
|
|
4282
|
+
}
|
|
4283
|
+
logger.debug('Updating contact:', contactId);
|
|
4284
|
+
await this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
4285
|
+
method: 'PUT',
|
|
4286
|
+
body: JSON.stringify(action.updates),
|
|
4287
|
+
});
|
|
4288
|
+
}
|
|
4289
|
+
/**
|
|
4290
|
+
* Replace variables in a string template
|
|
4291
|
+
* Supports syntax like {{contact.email}}, {{opportunity.value}}
|
|
4292
|
+
*/
|
|
4293
|
+
replaceVariables(template, data) {
|
|
4294
|
+
return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
|
|
4295
|
+
const value = this.getNestedValue(data, path.trim());
|
|
4296
|
+
return value !== undefined ? String(value) : match;
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4299
|
+
/**
|
|
4300
|
+
* Get nested value from object using dot notation
|
|
4301
|
+
* Supports dynamic field access including custom fields
|
|
4302
|
+
*/
|
|
4303
|
+
getNestedValue(obj, path) {
|
|
4304
|
+
return path.split('.').reduce((current, key) => {
|
|
4305
|
+
return current !== null && current !== undefined && typeof current === 'object'
|
|
4306
|
+
? current[key]
|
|
4307
|
+
: undefined;
|
|
4308
|
+
}, obj);
|
|
4309
|
+
}
|
|
4310
|
+
/**
|
|
4311
|
+
* Extract all available field paths from a data object
|
|
4312
|
+
* Useful for dynamic field discovery based on platform-specific attributes
|
|
4313
|
+
* @param obj - The data object to extract fields from
|
|
4314
|
+
* @param prefix - Internal use for nested paths
|
|
4315
|
+
* @param maxDepth - Maximum depth to traverse (default: 3)
|
|
4316
|
+
* @returns Array of field paths (e.g., ['email', 'contact.firstName', 'customFields.industry'])
|
|
4317
|
+
*/
|
|
4318
|
+
extractAvailableFields(obj, prefix = '', maxDepth = 3) {
|
|
4319
|
+
if (maxDepth <= 0)
|
|
4320
|
+
return [];
|
|
4321
|
+
const fields = [];
|
|
4322
|
+
for (const key in obj) {
|
|
4323
|
+
if (!obj.hasOwnProperty(key))
|
|
4324
|
+
continue;
|
|
4325
|
+
const value = obj[key];
|
|
4326
|
+
const fieldPath = prefix ? `${prefix}.${key}` : key;
|
|
4327
|
+
fields.push(fieldPath);
|
|
4328
|
+
// Recursively traverse nested objects
|
|
4329
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
4330
|
+
const nestedFields = this.extractAvailableFields(value, fieldPath, maxDepth - 1);
|
|
4331
|
+
fields.push(...nestedFields);
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
return fields;
|
|
4335
|
+
}
|
|
4336
|
+
/**
|
|
4337
|
+
* Get available fields from sample data
|
|
4338
|
+
* Helps with dynamic field detection for platform-specific attributes
|
|
4339
|
+
* @param sampleData - Sample data object to analyze
|
|
4340
|
+
* @returns Array of available field paths
|
|
4341
|
+
*/
|
|
4342
|
+
getAvailableFields(sampleData) {
|
|
4343
|
+
return this.extractAvailableFields(sampleData);
|
|
4344
|
+
}
|
|
4345
|
+
// ============================================
|
|
4346
|
+
// HELPER METHODS FOR COMMON PATTERNS
|
|
4347
|
+
// ============================================
|
|
4348
|
+
/**
|
|
4349
|
+
* Create a simple email trigger
|
|
4350
|
+
* Helper method for common use case
|
|
4351
|
+
*/
|
|
4352
|
+
async createEmailTrigger(config) {
|
|
4353
|
+
return this.createTrigger({
|
|
4354
|
+
name: config.name,
|
|
4355
|
+
eventType: config.eventType,
|
|
4356
|
+
conditions: config.conditions,
|
|
4357
|
+
actions: [
|
|
4358
|
+
{
|
|
4359
|
+
type: 'send_email',
|
|
4360
|
+
to: config.to,
|
|
4361
|
+
subject: config.subject,
|
|
4362
|
+
body: config.body,
|
|
4363
|
+
},
|
|
4364
|
+
],
|
|
4365
|
+
isActive: true,
|
|
4366
|
+
});
|
|
4367
|
+
}
|
|
4368
|
+
/**
|
|
4369
|
+
* Create a task creation trigger
|
|
4370
|
+
*/
|
|
4371
|
+
async createTaskTrigger(config) {
|
|
4372
|
+
return this.createTrigger({
|
|
4373
|
+
name: config.name,
|
|
4374
|
+
eventType: config.eventType,
|
|
4375
|
+
conditions: config.conditions,
|
|
4376
|
+
actions: [
|
|
4377
|
+
{
|
|
4378
|
+
type: 'create_task',
|
|
4379
|
+
title: config.taskTitle,
|
|
4380
|
+
description: config.taskDescription,
|
|
4381
|
+
priority: config.priority,
|
|
4382
|
+
dueDays: config.dueDays,
|
|
4383
|
+
},
|
|
4384
|
+
],
|
|
4385
|
+
isActive: true,
|
|
4386
|
+
});
|
|
4387
|
+
}
|
|
4388
|
+
/**
|
|
4389
|
+
* Create a webhook trigger
|
|
4390
|
+
*/
|
|
4391
|
+
async createWebhookTrigger(config) {
|
|
4392
|
+
return this.createTrigger({
|
|
4393
|
+
name: config.name,
|
|
4394
|
+
eventType: config.eventType,
|
|
4395
|
+
conditions: config.conditions,
|
|
4396
|
+
actions: [
|
|
4397
|
+
{
|
|
4398
|
+
type: 'webhook',
|
|
4399
|
+
url: config.webhookUrl,
|
|
4400
|
+
method: config.method || 'POST',
|
|
4401
|
+
},
|
|
4402
|
+
],
|
|
4403
|
+
isActive: true,
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
/**
|
|
4409
|
+
* Clianta SDK - CRM API Client
|
|
4410
|
+
* @see SDK_VERSION in core/config.ts
|
|
4411
|
+
*/
|
|
4412
|
+
/**
|
|
4413
|
+
* CRM API Client for managing contacts and opportunities
|
|
4414
|
+
*/
|
|
4415
|
+
class CRMClient {
|
|
4416
|
+
constructor(apiEndpoint, workspaceId, authToken, apiKey) {
|
|
4417
|
+
this.apiEndpoint = apiEndpoint;
|
|
4418
|
+
this.workspaceId = workspaceId;
|
|
4419
|
+
this.authToken = authToken;
|
|
4420
|
+
this.apiKey = apiKey;
|
|
4421
|
+
this.triggers = new EventTriggersManager(apiEndpoint, workspaceId, authToken);
|
|
4422
|
+
}
|
|
4423
|
+
/**
|
|
4424
|
+
* Set authentication token for API requests (user JWT)
|
|
4425
|
+
*/
|
|
4426
|
+
setAuthToken(token) {
|
|
4427
|
+
this.authToken = token;
|
|
4428
|
+
this.apiKey = undefined;
|
|
4429
|
+
this.triggers.setAuthToken(token);
|
|
4430
|
+
}
|
|
4431
|
+
/**
|
|
4432
|
+
* Set workspace API key for server-to-server requests.
|
|
4433
|
+
* Use this instead of setAuthToken when integrating from an external app.
|
|
4434
|
+
*/
|
|
4435
|
+
setApiKey(key) {
|
|
4436
|
+
this.apiKey = key;
|
|
4437
|
+
this.authToken = undefined;
|
|
4438
|
+
}
|
|
4439
|
+
/**
|
|
4440
|
+
* Validate required parameter exists
|
|
4441
|
+
* @throws {Error} if value is null/undefined or empty string
|
|
4442
|
+
*/
|
|
4443
|
+
validateRequired(param, value, methodName) {
|
|
4444
|
+
if (value === null || value === undefined || value === '') {
|
|
4445
|
+
throw new Error(`[CRMClient.${methodName}] ${param} is required`);
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
/**
|
|
4449
|
+
* Make authenticated API request
|
|
4450
|
+
*/
|
|
4451
|
+
async request(endpoint, options = {}) {
|
|
4452
|
+
const url = `${this.apiEndpoint}${endpoint}`;
|
|
4453
|
+
const headers = {
|
|
4454
|
+
'Content-Type': 'application/json',
|
|
4455
|
+
...(options.headers || {}),
|
|
4456
|
+
};
|
|
4457
|
+
if (this.apiKey) {
|
|
4458
|
+
headers['X-Api-Key'] = this.apiKey;
|
|
4459
|
+
}
|
|
4460
|
+
else if (this.authToken) {
|
|
4461
|
+
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
4462
|
+
}
|
|
4463
|
+
try {
|
|
4464
|
+
const response = await fetch(url, {
|
|
4465
|
+
...options,
|
|
4466
|
+
headers,
|
|
4467
|
+
});
|
|
4468
|
+
const data = await response.json();
|
|
4469
|
+
if (!response.ok) {
|
|
4470
|
+
return {
|
|
4471
|
+
success: false,
|
|
4472
|
+
error: data.message || 'Request failed',
|
|
4473
|
+
status: response.status,
|
|
4474
|
+
};
|
|
4475
|
+
}
|
|
4476
|
+
return {
|
|
4477
|
+
success: true,
|
|
4478
|
+
data: data.data || data,
|
|
4479
|
+
status: response.status,
|
|
4480
|
+
};
|
|
4481
|
+
}
|
|
4482
|
+
catch (error) {
|
|
4483
|
+
return {
|
|
4484
|
+
success: false,
|
|
4485
|
+
error: error instanceof Error ? error.message : 'Network error',
|
|
4486
|
+
status: 0,
|
|
4487
|
+
};
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
// ============================================
|
|
4491
|
+
// INBOUND EVENTS API (API-key authenticated)
|
|
4492
|
+
// ============================================
|
|
4493
|
+
/**
|
|
4494
|
+
* Send an inbound event from an external app (e.g. user signup on client website).
|
|
4495
|
+
* Requires the client to be initialized with an API key via setApiKey() or the constructor.
|
|
4496
|
+
*
|
|
4497
|
+
* The contact is upserted in the CRM and matching workflow automations fire automatically.
|
|
4498
|
+
*
|
|
4499
|
+
* @example
|
|
4500
|
+
* const crm = new CRMClient('http://localhost:5000', 'WORKSPACE_ID');
|
|
4501
|
+
* crm.setApiKey('mm_live_...');
|
|
4502
|
+
*
|
|
4503
|
+
* await crm.sendEvent({
|
|
4504
|
+
* event: 'user.registered',
|
|
4505
|
+
* contact: { email: 'alice@example.com', firstName: 'Alice' },
|
|
4506
|
+
* data: { plan: 'free', signupSource: 'homepage' },
|
|
4507
|
+
* });
|
|
4508
|
+
*/
|
|
4509
|
+
async sendEvent(payload) {
|
|
4510
|
+
const url = `${this.apiEndpoint}/api/public/events`;
|
|
4511
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
4512
|
+
if (this.apiKey) {
|
|
4513
|
+
headers['X-Api-Key'] = this.apiKey;
|
|
4514
|
+
}
|
|
4515
|
+
else if (this.authToken) {
|
|
4516
|
+
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
4517
|
+
}
|
|
4518
|
+
try {
|
|
4519
|
+
const response = await fetch(url, {
|
|
4520
|
+
method: 'POST',
|
|
4521
|
+
headers,
|
|
4522
|
+
body: JSON.stringify(payload),
|
|
4523
|
+
});
|
|
4524
|
+
const data = await response.json();
|
|
4525
|
+
if (!response.ok) {
|
|
4526
|
+
return {
|
|
4527
|
+
success: false,
|
|
4528
|
+
contactCreated: false,
|
|
4529
|
+
event: payload.event,
|
|
4530
|
+
error: data.error || 'Request failed',
|
|
4531
|
+
};
|
|
4532
|
+
}
|
|
4533
|
+
return {
|
|
4534
|
+
success: data.success,
|
|
4535
|
+
contactCreated: data.contactCreated,
|
|
4536
|
+
contactId: data.contactId,
|
|
4537
|
+
event: data.event,
|
|
4538
|
+
};
|
|
4539
|
+
}
|
|
4540
|
+
catch (error) {
|
|
4541
|
+
return {
|
|
4542
|
+
success: false,
|
|
4543
|
+
contactCreated: false,
|
|
4544
|
+
event: payload.event,
|
|
4545
|
+
error: error instanceof Error ? error.message : 'Network error',
|
|
4546
|
+
};
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
// ============================================
|
|
4550
|
+
// CONTACTS API
|
|
4551
|
+
// ============================================
|
|
4552
|
+
/**
|
|
4553
|
+
* Get all contacts with pagination
|
|
4554
|
+
*/
|
|
4555
|
+
async getContacts(params) {
|
|
4556
|
+
const queryParams = new URLSearchParams();
|
|
4557
|
+
if (params?.page)
|
|
4558
|
+
queryParams.set('page', params.page.toString());
|
|
4559
|
+
if (params?.limit)
|
|
4560
|
+
queryParams.set('limit', params.limit.toString());
|
|
4561
|
+
if (params?.search)
|
|
4562
|
+
queryParams.set('search', params.search);
|
|
4563
|
+
if (params?.status)
|
|
4564
|
+
queryParams.set('status', params.status);
|
|
4565
|
+
const query = queryParams.toString();
|
|
4566
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/contacts${query ? `?${query}` : ''}`;
|
|
4567
|
+
return this.request(endpoint);
|
|
4568
|
+
}
|
|
4569
|
+
/**
|
|
4570
|
+
* Get a single contact by ID
|
|
4571
|
+
*/
|
|
4572
|
+
async getContact(contactId) {
|
|
4573
|
+
this.validateRequired('contactId', contactId, 'getContact');
|
|
4574
|
+
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`);
|
|
4575
|
+
}
|
|
4576
|
+
/**
|
|
4577
|
+
* Create a new contact
|
|
4578
|
+
*/
|
|
4579
|
+
async createContact(contact) {
|
|
4580
|
+
return this.request(`/api/workspaces/${this.workspaceId}/contacts`, {
|
|
4581
|
+
method: 'POST',
|
|
4582
|
+
body: JSON.stringify(contact),
|
|
4583
|
+
});
|
|
4584
|
+
}
|
|
4585
|
+
/**
|
|
4586
|
+
* Update an existing contact
|
|
4587
|
+
*/
|
|
4588
|
+
async updateContact(contactId, updates) {
|
|
4589
|
+
this.validateRequired('contactId', contactId, 'updateContact');
|
|
4590
|
+
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
4591
|
+
method: 'PUT',
|
|
4592
|
+
body: JSON.stringify(updates),
|
|
4593
|
+
});
|
|
4594
|
+
}
|
|
4595
|
+
/**
|
|
4596
|
+
* Delete a contact
|
|
4597
|
+
*/
|
|
4598
|
+
async deleteContact(contactId) {
|
|
4599
|
+
this.validateRequired('contactId', contactId, 'deleteContact');
|
|
4600
|
+
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
4601
|
+
method: 'DELETE',
|
|
4602
|
+
});
|
|
4603
|
+
}
|
|
4604
|
+
// ============================================
|
|
4605
|
+
// OPPORTUNITIES API
|
|
4606
|
+
// ============================================
|
|
4607
|
+
/**
|
|
4608
|
+
* Get all opportunities with pagination
|
|
4609
|
+
*/
|
|
4610
|
+
async getOpportunities(params) {
|
|
4611
|
+
const queryParams = new URLSearchParams();
|
|
4612
|
+
if (params?.page)
|
|
4613
|
+
queryParams.set('page', params.page.toString());
|
|
4614
|
+
if (params?.limit)
|
|
4615
|
+
queryParams.set('limit', params.limit.toString());
|
|
4616
|
+
if (params?.pipelineId)
|
|
4617
|
+
queryParams.set('pipelineId', params.pipelineId);
|
|
4618
|
+
if (params?.stageId)
|
|
4619
|
+
queryParams.set('stageId', params.stageId);
|
|
4620
|
+
const query = queryParams.toString();
|
|
4621
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/opportunities${query ? `?${query}` : ''}`;
|
|
4622
|
+
return this.request(endpoint);
|
|
4623
|
+
}
|
|
4624
|
+
/**
|
|
4625
|
+
* Get a single opportunity by ID
|
|
4626
|
+
*/
|
|
4627
|
+
async getOpportunity(opportunityId) {
|
|
4628
|
+
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`);
|
|
4629
|
+
}
|
|
4630
|
+
/**
|
|
4631
|
+
* Create a new opportunity
|
|
4632
|
+
*/
|
|
4633
|
+
async createOpportunity(opportunity) {
|
|
4634
|
+
return this.request(`/api/workspaces/${this.workspaceId}/opportunities`, {
|
|
4635
|
+
method: 'POST',
|
|
4636
|
+
body: JSON.stringify(opportunity),
|
|
4637
|
+
});
|
|
4638
|
+
}
|
|
4639
|
+
/**
|
|
4640
|
+
* Update an existing opportunity
|
|
4641
|
+
*/
|
|
4642
|
+
async updateOpportunity(opportunityId, updates) {
|
|
4643
|
+
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
|
|
4644
|
+
method: 'PUT',
|
|
4645
|
+
body: JSON.stringify(updates),
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
/**
|
|
4649
|
+
* Delete an opportunity
|
|
4650
|
+
*/
|
|
4651
|
+
async deleteOpportunity(opportunityId) {
|
|
4652
|
+
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
|
|
4653
|
+
method: 'DELETE',
|
|
4654
|
+
});
|
|
4655
|
+
}
|
|
4656
|
+
/**
|
|
4657
|
+
* Move opportunity to a different stage
|
|
4658
|
+
*/
|
|
4659
|
+
async moveOpportunity(opportunityId, stageId) {
|
|
4660
|
+
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/move`, {
|
|
4661
|
+
method: 'POST',
|
|
4662
|
+
body: JSON.stringify({ stageId }),
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
// ============================================
|
|
4666
|
+
// COMPANIES API
|
|
4667
|
+
// ============================================
|
|
4668
|
+
/**
|
|
4669
|
+
* Get all companies with pagination
|
|
4670
|
+
*/
|
|
4671
|
+
async getCompanies(params) {
|
|
4672
|
+
const queryParams = new URLSearchParams();
|
|
4673
|
+
if (params?.page)
|
|
4674
|
+
queryParams.set('page', params.page.toString());
|
|
4675
|
+
if (params?.limit)
|
|
4676
|
+
queryParams.set('limit', params.limit.toString());
|
|
4677
|
+
if (params?.search)
|
|
4678
|
+
queryParams.set('search', params.search);
|
|
4679
|
+
if (params?.status)
|
|
4680
|
+
queryParams.set('status', params.status);
|
|
4681
|
+
if (params?.industry)
|
|
4682
|
+
queryParams.set('industry', params.industry);
|
|
4683
|
+
const query = queryParams.toString();
|
|
4684
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/companies${query ? `?${query}` : ''}`;
|
|
4685
|
+
return this.request(endpoint);
|
|
4686
|
+
}
|
|
4687
|
+
/**
|
|
4688
|
+
* Get a single company by ID
|
|
4689
|
+
*/
|
|
4690
|
+
async getCompany(companyId) {
|
|
4691
|
+
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`);
|
|
4692
|
+
}
|
|
4693
|
+
/**
|
|
4694
|
+
* Create a new company
|
|
4695
|
+
*/
|
|
4696
|
+
async createCompany(company) {
|
|
4697
|
+
return this.request(`/api/workspaces/${this.workspaceId}/companies`, {
|
|
4698
|
+
method: 'POST',
|
|
4699
|
+
body: JSON.stringify(company),
|
|
4700
|
+
});
|
|
4701
|
+
}
|
|
4702
|
+
/**
|
|
4703
|
+
* Update an existing company
|
|
4704
|
+
*/
|
|
4705
|
+
async updateCompany(companyId, updates) {
|
|
4706
|
+
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
|
|
4707
|
+
method: 'PUT',
|
|
4708
|
+
body: JSON.stringify(updates),
|
|
4709
|
+
});
|
|
4710
|
+
}
|
|
4711
|
+
/**
|
|
4712
|
+
* Delete a company
|
|
4713
|
+
*/
|
|
4714
|
+
async deleteCompany(companyId) {
|
|
4715
|
+
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
|
|
4716
|
+
method: 'DELETE',
|
|
4717
|
+
});
|
|
4718
|
+
}
|
|
4719
|
+
/**
|
|
4720
|
+
* Get contacts belonging to a company
|
|
4721
|
+
*/
|
|
4722
|
+
async getCompanyContacts(companyId, params) {
|
|
4723
|
+
const queryParams = new URLSearchParams();
|
|
4724
|
+
if (params?.page)
|
|
4725
|
+
queryParams.set('page', params.page.toString());
|
|
4726
|
+
if (params?.limit)
|
|
4727
|
+
queryParams.set('limit', params.limit.toString());
|
|
4728
|
+
const query = queryParams.toString();
|
|
4729
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/contacts${query ? `?${query}` : ''}`;
|
|
4730
|
+
return this.request(endpoint);
|
|
4731
|
+
}
|
|
4732
|
+
/**
|
|
4733
|
+
* Get deals/opportunities belonging to a company
|
|
4734
|
+
*/
|
|
4735
|
+
async getCompanyDeals(companyId, params) {
|
|
4736
|
+
const queryParams = new URLSearchParams();
|
|
4737
|
+
if (params?.page)
|
|
4738
|
+
queryParams.set('page', params.page.toString());
|
|
4739
|
+
if (params?.limit)
|
|
4740
|
+
queryParams.set('limit', params.limit.toString());
|
|
4741
|
+
const query = queryParams.toString();
|
|
4742
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/deals${query ? `?${query}` : ''}`;
|
|
4743
|
+
return this.request(endpoint);
|
|
4744
|
+
}
|
|
4745
|
+
// ============================================
|
|
4746
|
+
// PIPELINES API
|
|
4747
|
+
// ============================================
|
|
4748
|
+
/**
|
|
4749
|
+
* Get all pipelines
|
|
4750
|
+
*/
|
|
4751
|
+
async getPipelines() {
|
|
4752
|
+
return this.request(`/api/workspaces/${this.workspaceId}/pipelines`);
|
|
4753
|
+
}
|
|
4754
|
+
/**
|
|
4755
|
+
* Get a single pipeline by ID
|
|
4756
|
+
*/
|
|
4757
|
+
async getPipeline(pipelineId) {
|
|
4758
|
+
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`);
|
|
4759
|
+
}
|
|
4760
|
+
/**
|
|
4761
|
+
* Create a new pipeline
|
|
4762
|
+
*/
|
|
4763
|
+
async createPipeline(pipeline) {
|
|
4764
|
+
return this.request(`/api/workspaces/${this.workspaceId}/pipelines`, {
|
|
4765
|
+
method: 'POST',
|
|
4766
|
+
body: JSON.stringify(pipeline),
|
|
4767
|
+
});
|
|
4768
|
+
}
|
|
4769
|
+
/**
|
|
4770
|
+
* Update an existing pipeline
|
|
4771
|
+
*/
|
|
4772
|
+
async updatePipeline(pipelineId, updates) {
|
|
4773
|
+
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
|
|
4774
|
+
method: 'PUT',
|
|
4775
|
+
body: JSON.stringify(updates),
|
|
4776
|
+
});
|
|
4777
|
+
}
|
|
4778
|
+
/**
|
|
4779
|
+
* Delete a pipeline
|
|
4780
|
+
*/
|
|
4781
|
+
async deletePipeline(pipelineId) {
|
|
4782
|
+
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
|
|
4783
|
+
method: 'DELETE',
|
|
4784
|
+
});
|
|
4785
|
+
}
|
|
4786
|
+
// ============================================
|
|
4787
|
+
// TASKS API
|
|
4788
|
+
// ============================================
|
|
4789
|
+
/**
|
|
4790
|
+
* Get all tasks with pagination
|
|
4791
|
+
*/
|
|
4792
|
+
async getTasks(params) {
|
|
4793
|
+
const queryParams = new URLSearchParams();
|
|
4794
|
+
if (params?.page)
|
|
4795
|
+
queryParams.set('page', params.page.toString());
|
|
4796
|
+
if (params?.limit)
|
|
4797
|
+
queryParams.set('limit', params.limit.toString());
|
|
4798
|
+
if (params?.status)
|
|
4799
|
+
queryParams.set('status', params.status);
|
|
4800
|
+
if (params?.priority)
|
|
4801
|
+
queryParams.set('priority', params.priority);
|
|
4802
|
+
if (params?.contactId)
|
|
4803
|
+
queryParams.set('contactId', params.contactId);
|
|
4804
|
+
if (params?.companyId)
|
|
4805
|
+
queryParams.set('companyId', params.companyId);
|
|
4806
|
+
if (params?.opportunityId)
|
|
4807
|
+
queryParams.set('opportunityId', params.opportunityId);
|
|
4808
|
+
const query = queryParams.toString();
|
|
4809
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/tasks${query ? `?${query}` : ''}`;
|
|
4810
|
+
return this.request(endpoint);
|
|
4811
|
+
}
|
|
4812
|
+
/**
|
|
4813
|
+
* Get a single task by ID
|
|
4814
|
+
*/
|
|
4815
|
+
async getTask(taskId) {
|
|
4816
|
+
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`);
|
|
4817
|
+
}
|
|
4818
|
+
/**
|
|
4819
|
+
* Create a new task
|
|
4820
|
+
*/
|
|
4821
|
+
async createTask(task) {
|
|
4822
|
+
return this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
|
|
4823
|
+
method: 'POST',
|
|
4824
|
+
body: JSON.stringify(task),
|
|
4825
|
+
});
|
|
4826
|
+
}
|
|
4827
|
+
/**
|
|
4828
|
+
* Update an existing task
|
|
4829
|
+
*/
|
|
4830
|
+
async updateTask(taskId, updates) {
|
|
4831
|
+
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
|
|
4832
|
+
method: 'PUT',
|
|
4833
|
+
body: JSON.stringify(updates),
|
|
4834
|
+
});
|
|
4835
|
+
}
|
|
4836
|
+
/**
|
|
4837
|
+
* Mark a task as completed
|
|
4838
|
+
*/
|
|
4839
|
+
async completeTask(taskId) {
|
|
4840
|
+
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}/complete`, {
|
|
4841
|
+
method: 'PATCH',
|
|
4842
|
+
});
|
|
4843
|
+
}
|
|
4844
|
+
/**
|
|
4845
|
+
* Delete a task
|
|
4846
|
+
*/
|
|
4847
|
+
async deleteTask(taskId) {
|
|
4848
|
+
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
|
|
4849
|
+
method: 'DELETE',
|
|
4850
|
+
});
|
|
4851
|
+
}
|
|
4852
|
+
// ============================================
|
|
4853
|
+
// ACTIVITIES API
|
|
4854
|
+
// ============================================
|
|
4855
|
+
/**
|
|
4856
|
+
* Get activities for a contact
|
|
4857
|
+
*/
|
|
4858
|
+
async getContactActivities(contactId, params) {
|
|
4859
|
+
const queryParams = new URLSearchParams();
|
|
4860
|
+
if (params?.page)
|
|
4861
|
+
queryParams.set('page', params.page.toString());
|
|
4862
|
+
if (params?.limit)
|
|
4863
|
+
queryParams.set('limit', params.limit.toString());
|
|
4864
|
+
if (params?.type)
|
|
4865
|
+
queryParams.set('type', params.type);
|
|
4866
|
+
const query = queryParams.toString();
|
|
4867
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
|
|
4868
|
+
return this.request(endpoint);
|
|
4869
|
+
}
|
|
4870
|
+
/**
|
|
4871
|
+
* Get activities for an opportunity/deal
|
|
4872
|
+
*/
|
|
4873
|
+
async getOpportunityActivities(opportunityId, params) {
|
|
4874
|
+
const queryParams = new URLSearchParams();
|
|
4875
|
+
if (params?.page)
|
|
4876
|
+
queryParams.set('page', params.page.toString());
|
|
4877
|
+
if (params?.limit)
|
|
4878
|
+
queryParams.set('limit', params.limit.toString());
|
|
4879
|
+
if (params?.type)
|
|
4880
|
+
queryParams.set('type', params.type);
|
|
4881
|
+
const query = queryParams.toString();
|
|
4882
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/activities${query ? `?${query}` : ''}`;
|
|
4883
|
+
return this.request(endpoint);
|
|
4884
|
+
}
|
|
4885
|
+
/**
|
|
4886
|
+
* Create a new activity
|
|
4887
|
+
*/
|
|
4888
|
+
async createActivity(activity) {
|
|
4889
|
+
// Determine the correct endpoint based on related entity
|
|
4890
|
+
let endpoint;
|
|
4891
|
+
if (activity.opportunityId) {
|
|
4892
|
+
endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${activity.opportunityId}/activities`;
|
|
4893
|
+
}
|
|
4894
|
+
else if (activity.contactId) {
|
|
4895
|
+
endpoint = `/api/workspaces/${this.workspaceId}/contacts/${activity.contactId}/activities`;
|
|
4896
|
+
}
|
|
4897
|
+
else {
|
|
4898
|
+
endpoint = `/api/workspaces/${this.workspaceId}/activities`;
|
|
4899
|
+
}
|
|
4900
|
+
return this.request(endpoint, {
|
|
4901
|
+
method: 'POST',
|
|
4902
|
+
body: JSON.stringify(activity),
|
|
4903
|
+
});
|
|
4904
|
+
}
|
|
4905
|
+
/**
|
|
4906
|
+
* Update an existing activity
|
|
4907
|
+
*/
|
|
4908
|
+
async updateActivity(activityId, updates) {
|
|
4909
|
+
return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
|
|
4910
|
+
method: 'PATCH',
|
|
4911
|
+
body: JSON.stringify(updates),
|
|
4912
|
+
});
|
|
4913
|
+
}
|
|
4914
|
+
/**
|
|
4915
|
+
* Delete an activity
|
|
4916
|
+
*/
|
|
4917
|
+
async deleteActivity(activityId) {
|
|
4918
|
+
return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
|
|
4919
|
+
method: 'DELETE',
|
|
4920
|
+
});
|
|
4921
|
+
}
|
|
4922
|
+
/**
|
|
4923
|
+
* Log a call activity
|
|
4924
|
+
*/
|
|
4925
|
+
async logCall(data) {
|
|
4926
|
+
return this.createActivity({
|
|
4927
|
+
type: 'call',
|
|
4928
|
+
title: `${data.direction === 'inbound' ? 'Inbound' : 'Outbound'} Call`,
|
|
4929
|
+
direction: data.direction,
|
|
4930
|
+
duration: data.duration,
|
|
4931
|
+
outcome: data.outcome,
|
|
4932
|
+
description: data.notes,
|
|
4933
|
+
contactId: data.contactId,
|
|
4934
|
+
opportunityId: data.opportunityId,
|
|
4935
|
+
});
|
|
4936
|
+
}
|
|
4937
|
+
/**
|
|
4938
|
+
* Log a meeting activity
|
|
4939
|
+
*/
|
|
4940
|
+
async logMeeting(data) {
|
|
4941
|
+
return this.createActivity({
|
|
4942
|
+
type: 'meeting',
|
|
4943
|
+
title: data.title,
|
|
4944
|
+
duration: data.duration,
|
|
4945
|
+
outcome: data.outcome,
|
|
4946
|
+
description: data.notes,
|
|
4947
|
+
contactId: data.contactId,
|
|
4948
|
+
opportunityId: data.opportunityId,
|
|
4949
|
+
});
|
|
4950
|
+
}
|
|
4951
|
+
/**
|
|
4952
|
+
* Add a note to a contact or opportunity
|
|
4953
|
+
*/
|
|
4954
|
+
async addNote(data) {
|
|
4955
|
+
return this.createActivity({
|
|
4956
|
+
type: 'note',
|
|
4957
|
+
title: 'Note',
|
|
4958
|
+
description: data.content,
|
|
4959
|
+
contactId: data.contactId,
|
|
4960
|
+
opportunityId: data.opportunityId,
|
|
4961
|
+
});
|
|
4962
|
+
}
|
|
4963
|
+
// ============================================
|
|
4964
|
+
// EMAIL TEMPLATES API
|
|
4965
|
+
// ============================================
|
|
4966
|
+
/**
|
|
4967
|
+
* Get all email templates
|
|
4968
|
+
*/
|
|
4969
|
+
async getEmailTemplates(params) {
|
|
4970
|
+
const queryParams = new URLSearchParams();
|
|
4971
|
+
if (params?.page)
|
|
4972
|
+
queryParams.set('page', params.page.toString());
|
|
4973
|
+
if (params?.limit)
|
|
4974
|
+
queryParams.set('limit', params.limit.toString());
|
|
4975
|
+
const query = queryParams.toString();
|
|
4976
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/email-templates${query ? `?${query}` : ''}`;
|
|
4977
|
+
return this.request(endpoint);
|
|
4978
|
+
}
|
|
4979
|
+
/**
|
|
4980
|
+
* Get a single email template by ID
|
|
4981
|
+
*/
|
|
4982
|
+
async getEmailTemplate(templateId) {
|
|
4983
|
+
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`);
|
|
4984
|
+
}
|
|
4985
|
+
/**
|
|
4986
|
+
* Create a new email template
|
|
4987
|
+
*/
|
|
4988
|
+
async createEmailTemplate(template) {
|
|
4989
|
+
return this.request(`/api/workspaces/${this.workspaceId}/email-templates`, {
|
|
4990
|
+
method: 'POST',
|
|
4991
|
+
body: JSON.stringify(template),
|
|
4992
|
+
});
|
|
4993
|
+
}
|
|
4994
|
+
/**
|
|
4995
|
+
* Update an email template
|
|
4996
|
+
*/
|
|
4997
|
+
async updateEmailTemplate(templateId, updates) {
|
|
4998
|
+
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
|
|
4999
|
+
method: 'PUT',
|
|
5000
|
+
body: JSON.stringify(updates),
|
|
5001
|
+
});
|
|
5002
|
+
}
|
|
5003
|
+
/**
|
|
5004
|
+
* Delete an email template
|
|
5005
|
+
*/
|
|
5006
|
+
async deleteEmailTemplate(templateId) {
|
|
5007
|
+
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
|
|
5008
|
+
method: 'DELETE',
|
|
5009
|
+
});
|
|
5010
|
+
}
|
|
5011
|
+
/**
|
|
5012
|
+
* Send an email using a template
|
|
5013
|
+
*/
|
|
5014
|
+
async sendEmail(data) {
|
|
5015
|
+
return this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
|
|
5016
|
+
method: 'POST',
|
|
5017
|
+
body: JSON.stringify(data),
|
|
5018
|
+
});
|
|
5019
|
+
}
|
|
5020
|
+
// ============================================
|
|
5021
|
+
// READ-BACK / DATA RETRIEVAL API
|
|
5022
|
+
// ============================================
|
|
5023
|
+
/**
|
|
5024
|
+
* Get a contact by email address.
|
|
5025
|
+
* Returns the first matching contact from a search query.
|
|
5026
|
+
*/
|
|
5027
|
+
async getContactByEmail(email) {
|
|
5028
|
+
this.validateRequired('email', email, 'getContactByEmail');
|
|
5029
|
+
const queryParams = new URLSearchParams({ search: email, limit: '1' });
|
|
5030
|
+
return this.request(`/api/workspaces/${this.workspaceId}/contacts?${queryParams.toString()}`);
|
|
5031
|
+
}
|
|
5032
|
+
/**
|
|
5033
|
+
* Get activity timeline for a contact
|
|
5034
|
+
*/
|
|
5035
|
+
async getContactActivity(contactId, params) {
|
|
5036
|
+
this.validateRequired('contactId', contactId, 'getContactActivity');
|
|
5037
|
+
const queryParams = new URLSearchParams();
|
|
5038
|
+
if (params?.page)
|
|
5039
|
+
queryParams.set('page', params.page.toString());
|
|
5040
|
+
if (params?.limit)
|
|
5041
|
+
queryParams.set('limit', params.limit.toString());
|
|
5042
|
+
if (params?.type)
|
|
5043
|
+
queryParams.set('type', params.type);
|
|
5044
|
+
if (params?.startDate)
|
|
5045
|
+
queryParams.set('startDate', params.startDate);
|
|
5046
|
+
if (params?.endDate)
|
|
5047
|
+
queryParams.set('endDate', params.endDate);
|
|
5048
|
+
const query = queryParams.toString();
|
|
5049
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
|
|
5050
|
+
return this.request(endpoint);
|
|
5051
|
+
}
|
|
5052
|
+
/**
|
|
5053
|
+
* Get engagement metrics for a contact (via their linked visitor data)
|
|
5054
|
+
*/
|
|
5055
|
+
async getContactEngagement(contactId) {
|
|
5056
|
+
this.validateRequired('contactId', contactId, 'getContactEngagement');
|
|
5057
|
+
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}/engagement`);
|
|
5058
|
+
}
|
|
5059
|
+
/**
|
|
5060
|
+
* Get a full timeline for a contact including events, activities, and opportunities
|
|
5061
|
+
*/
|
|
5062
|
+
async getContactTimeline(contactId, params) {
|
|
5063
|
+
this.validateRequired('contactId', contactId, 'getContactTimeline');
|
|
5064
|
+
const queryParams = new URLSearchParams();
|
|
5065
|
+
if (params?.page)
|
|
5066
|
+
queryParams.set('page', params.page.toString());
|
|
5067
|
+
if (params?.limit)
|
|
5068
|
+
queryParams.set('limit', params.limit.toString());
|
|
5069
|
+
const query = queryParams.toString();
|
|
5070
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/timeline${query ? `?${query}` : ''}`;
|
|
5071
|
+
return this.request(endpoint);
|
|
5072
|
+
}
|
|
5073
|
+
/**
|
|
5074
|
+
* Search contacts with advanced filters
|
|
5075
|
+
*/
|
|
5076
|
+
async searchContacts(query, filters) {
|
|
5077
|
+
const queryParams = new URLSearchParams();
|
|
5078
|
+
queryParams.set('search', query);
|
|
5079
|
+
if (filters?.status)
|
|
5080
|
+
queryParams.set('status', filters.status);
|
|
5081
|
+
if (filters?.lifecycleStage)
|
|
5082
|
+
queryParams.set('lifecycleStage', filters.lifecycleStage);
|
|
5083
|
+
if (filters?.source)
|
|
5084
|
+
queryParams.set('source', filters.source);
|
|
5085
|
+
if (filters?.tags)
|
|
5086
|
+
queryParams.set('tags', filters.tags.join(','));
|
|
5087
|
+
if (filters?.page)
|
|
5088
|
+
queryParams.set('page', filters.page.toString());
|
|
5089
|
+
if (filters?.limit)
|
|
5090
|
+
queryParams.set('limit', filters.limit.toString());
|
|
5091
|
+
const qs = queryParams.toString();
|
|
5092
|
+
const endpoint = `/api/workspaces/${this.workspaceId}/contacts${qs ? `?${qs}` : ''}`;
|
|
5093
|
+
return this.request(endpoint);
|
|
5094
|
+
}
|
|
5095
|
+
// ============================================
|
|
5096
|
+
// WEBHOOK MANAGEMENT API
|
|
5097
|
+
// ============================================
|
|
5098
|
+
/**
|
|
5099
|
+
* List all webhook subscriptions
|
|
5100
|
+
*/
|
|
5101
|
+
async listWebhooks(params) {
|
|
5102
|
+
const queryParams = new URLSearchParams();
|
|
5103
|
+
if (params?.page)
|
|
5104
|
+
queryParams.set('page', params.page.toString());
|
|
5105
|
+
if (params?.limit)
|
|
5106
|
+
queryParams.set('limit', params.limit.toString());
|
|
5107
|
+
const query = queryParams.toString();
|
|
5108
|
+
return this.request(`/api/workspaces/${this.workspaceId}/webhooks${query ? `?${query}` : ''}`);
|
|
5109
|
+
}
|
|
5110
|
+
/**
|
|
5111
|
+
* Create a new webhook subscription
|
|
5112
|
+
*/
|
|
5113
|
+
async createWebhook(data) {
|
|
5114
|
+
return this.request(`/api/workspaces/${this.workspaceId}/webhooks`, {
|
|
5115
|
+
method: 'POST',
|
|
5116
|
+
body: JSON.stringify(data),
|
|
5117
|
+
});
|
|
5118
|
+
}
|
|
5119
|
+
/**
|
|
5120
|
+
* Delete a webhook subscription
|
|
5121
|
+
*/
|
|
5122
|
+
async deleteWebhook(webhookId) {
|
|
5123
|
+
this.validateRequired('webhookId', webhookId, 'deleteWebhook');
|
|
5124
|
+
return this.request(`/api/workspaces/${this.workspaceId}/webhooks/${webhookId}`, {
|
|
5125
|
+
method: 'DELETE',
|
|
5126
|
+
});
|
|
5127
|
+
}
|
|
5128
|
+
// ============================================
|
|
5129
|
+
// EVENT TRIGGERS API (delegated to triggers manager)
|
|
5130
|
+
// ============================================
|
|
5131
|
+
/**
|
|
5132
|
+
* Get all event triggers
|
|
5133
|
+
*/
|
|
5134
|
+
async getEventTriggers() {
|
|
5135
|
+
return this.triggers.getTriggers();
|
|
5136
|
+
}
|
|
5137
|
+
/**
|
|
5138
|
+
* Create a new event trigger
|
|
5139
|
+
*/
|
|
5140
|
+
async createEventTrigger(trigger) {
|
|
5141
|
+
return this.triggers.createTrigger(trigger);
|
|
5142
|
+
}
|
|
5143
|
+
/**
|
|
5144
|
+
* Update an event trigger
|
|
5145
|
+
*/
|
|
5146
|
+
async updateEventTrigger(triggerId, updates) {
|
|
5147
|
+
return this.triggers.updateTrigger(triggerId, updates);
|
|
5148
|
+
}
|
|
5149
|
+
/**
|
|
5150
|
+
* Delete an event trigger
|
|
5151
|
+
*/
|
|
5152
|
+
async deleteEventTrigger(triggerId) {
|
|
5153
|
+
return this.triggers.deleteTrigger(triggerId);
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
|
|
3968
5157
|
/**
|
|
3969
5158
|
* Clianta SDK
|
|
3970
5159
|
* Client-side tracking SDK for CRM — tracks visitors, identifies contacts,
|
|
@@ -4074,6 +5263,7 @@ if (typeof window !== 'undefined') {
|
|
|
4074
5263
|
}
|
|
4075
5264
|
}
|
|
4076
5265
|
|
|
5266
|
+
exports.CRMClient = CRMClient;
|
|
4077
5267
|
exports.ConsentManager = ConsentManager;
|
|
4078
5268
|
exports.SDK_VERSION = SDK_VERSION;
|
|
4079
5269
|
exports.Tracker = Tracker;
|