@ecodrix/erix-api 1.1.6 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecodrix/erix-api",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "author": "ECODrIx Team <contact@ecodrix.com>",
5
5
  "license": "MIT",
6
6
  "description": "Official Isomorphic SDK for the ECODrIx platform. Native support for WhatsApp, CRM, Storage, and Meetings across TS, JS, Python, and Java.",
package/src/core.ts CHANGED
@@ -17,8 +17,14 @@ import { WhatsApp } from "./resources/whatsapp";
17
17
 
18
18
  declare const process: any;
19
19
 
20
+ /** @internal The canonical ECODrIx backend URL — not exposed to SDK consumers. */
21
+ const ECOD_API_BASE = "https://api.ecodrix.com";
22
+
20
23
  /**
21
24
  * Configuration options for the Ecodrix client.
25
+ *
26
+ * Minimum required: `apiKey` and `clientCode`.
27
+ * The backend URL is managed internally and should not need to be changed.
22
28
  */
23
29
  export interface EcodrixOptions {
24
30
  /**
@@ -31,24 +37,21 @@ export interface EcodrixOptions {
31
37
  /**
32
38
  * Your tenant ID (Client Code).
33
39
  * This scopes all API requests to your specific organisation.
34
- * Required for most operations.
35
40
  * @example "ERIX_CLNT_JHBJHF"
36
41
  */
37
42
  clientCode?: string;
38
43
 
39
44
  /**
40
- * Override the base URL of the ECODrIx API.
41
- * Useful for pointing to a local development or staging server.
42
- * @default "https://api.ecodrix.com"
45
+ * @internal Override Socket.io URL for testing/staging.
46
+ * Not documented internal use only.
43
47
  */
44
- baseUrl?: string;
48
+ socketUrl?: string;
45
49
 
46
50
  /**
47
- * Override the Socket.io server URL for real-time events.
48
- * Defaults to the same value as `baseUrl`.
49
- * @default "https://api.ecodrix.com"
51
+ * @internal Override the backend API URL. Used by the CLI and internal tests only.
52
+ * Host projects must never set this — the production URL is hardcoded internally.
50
53
  */
51
- socketUrl?: string;
54
+ baseUrl?: string;
52
55
  }
53
56
 
54
57
  /**
@@ -115,7 +118,9 @@ export class Ecodrix {
115
118
  throw new AuthenticationError("API Key is required");
116
119
  }
117
120
 
118
- const baseUrl = options.baseUrl || "https://api.ecodrix.com";
121
+ // @internal: options.baseUrl is available for CLI/test use only.
122
+ // Host projects hardcode to prod — they never set baseUrl.
123
+ const baseUrl = options.baseUrl ?? ECOD_API_BASE;
119
124
  const socketUrl = options.socketUrl || baseUrl;
120
125
 
121
126
  const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
@@ -183,7 +188,7 @@ export class Ecodrix {
183
188
 
184
189
  /**
185
190
  * Join a specific real-time room (e.g., a conversation or a lead).
186
- *
191
+ *
187
192
  * @param roomId - The unique identifier for the room.
188
193
  */
189
194
  public joinRoom(roomId: string) {
@@ -192,7 +197,7 @@ export class Ecodrix {
192
197
 
193
198
  /**
194
199
  * Leave a previously joined real-time room.
195
- *
200
+ *
196
201
  * @param roomId - The unique identifier for the room.
197
202
  */
198
203
  public leaveRoom(roomId: string) {
@@ -84,10 +84,7 @@ export class Activities extends APIResource {
84
84
  * const timeline = await erixClient.crm.activities.timeline("lead_123", { limit: 10 });
85
85
  * ```
86
86
  */
87
- async timeline<T = any>(
88
- leadId: string,
89
- params?: { page?: number; limit?: number },
90
- ) {
87
+ async timeline<T = any>(leadId: string, params?: { page?: number; limit?: number }) {
91
88
  return this.get<T>(`/api/crm/leads/${leadId}/timeline`, { params } as any);
92
89
  }
93
90
 
@@ -123,4 +120,3 @@ export class Activities extends APIResource {
123
120
  return this.post<T>(`/api/crm/leads/${leadId}/calls`, params);
124
121
  }
125
122
  }
126
-
@@ -39,5 +39,4 @@ export class AutomationDashboard extends APIResource {
39
39
  async retryFailedEvent<T = any>(logId: string) {
40
40
  return this.post<T>(`/api/crm/automation/logs/${logId}/retry`, {});
41
41
  }
42
-
43
42
  }
@@ -62,7 +62,6 @@ export class Automations extends APIResource {
62
62
  return this.deleteRequest(`/api/crm/automations/${ruleId}`);
63
63
  }
64
64
 
65
-
66
65
  /**
67
66
  * Bulk delete rules.
68
67
  */
@@ -118,20 +117,14 @@ export class Automations extends APIResource {
118
117
  * Pause an enrollment.
119
118
  */
120
119
  async pauseEnrollment<T = any>(ruleId: string, enrollmentId: string) {
121
- return this.post<T>(
122
- `/api/crm/automations/${ruleId}/enrollments/${enrollmentId}/pause`,
123
- {},
124
- );
120
+ return this.post<T>(`/api/crm/automations/${ruleId}/enrollments/${enrollmentId}/pause`, {});
125
121
  }
126
122
 
127
123
  /**
128
124
  * Resume an enrollment.
129
125
  */
130
126
  async resumeEnrollment<T = any>(ruleId: string, enrollmentId: string) {
131
- return this.post<T>(
132
- `/api/crm/automations/${ruleId}/enrollments/${enrollmentId}/resume`,
133
- {},
134
- );
127
+ return this.post<T>(`/api/crm/automations/${ruleId}/enrollments/${enrollmentId}/resume`, {});
135
128
  }
136
129
 
137
130
  /**
@@ -174,5 +167,4 @@ export class Automations extends APIResource {
174
167
  async webhookEvent<T = any>(ruleId: string, eventName: string, payload?: any) {
175
168
  return this.post<T>("/api/crm/webhook-event", { ruleId, eventName, payload });
176
169
  }
177
-
178
170
  }
@@ -3,23 +3,12 @@ import { APIResource } from "../../resource";
3
3
  /**
4
4
  * Valid statuses for a CRM Lead.
5
5
  */
6
- export type LeadStatus =
7
- | "new"
8
- | "contacted"
9
- | "qualified"
10
- | "won"
11
- | "lost"
12
- | "archived";
6
+ export type LeadStatus = "new" | "contacted" | "qualified" | "won" | "lost" | "archived";
13
7
 
14
8
  /**
15
9
  * Common lead acquisition sources. Additional string values are allowed.
16
10
  */
17
- export type LeadSource =
18
- | "website"
19
- | "whatsapp"
20
- | "direct"
21
- | "referral"
22
- | string;
11
+ export type LeadSource = "website" | "whatsapp" | "direct" | "referral" | string;
23
12
 
24
13
  /**
25
14
  * Parameters for creating a new CRM Lead.
@@ -201,9 +190,7 @@ export class Leads extends APIResource {
201
190
  * }
202
191
  * ```
203
192
  */
204
- async *listAutoPaging<T = any>(
205
- params?: ListLeadsParams,
206
- ): AsyncGenerator<T, void, unknown> {
193
+ async *listAutoPaging<T = any>(params?: ListLeadsParams): AsyncGenerator<T, void, unknown> {
207
194
  let currentPage = params?.page || 1;
208
195
  let hasMore = true;
209
196
 
@@ -213,9 +200,7 @@ export class Leads extends APIResource {
213
200
  page: currentPage,
214
201
  });
215
202
 
216
- const items = Array.isArray(response.data)
217
- ? response.data
218
- : response || [];
203
+ const items = Array.isArray(response.data) ? response.data : response || [];
219
204
 
220
205
  if (items.length === 0) {
221
206
  hasMore = false;
@@ -308,11 +293,7 @@ export class Leads extends APIResource {
308
293
  * @param outcome - "won" | "lost"
309
294
  * @param reason - Reason for the outcome.
310
295
  */
311
- async convert<T = any>(
312
- leadId: string,
313
- outcome: "won" | "lost",
314
- reason?: string,
315
- ) {
296
+ async convert<T = any>(leadId: string, outcome: "won" | "lost", reason?: string) {
316
297
  return this.post<T>(`/api/crm/leads/${leadId}/convert`, {
317
298
  outcome,
318
299
  reason,
@@ -325,10 +306,7 @@ export class Leads extends APIResource {
325
306
  * @param leadId - ID of the lead.
326
307
  * @param options - Tags to add or remove.
327
308
  */
328
- async tags<T = any>(
329
- leadId: string,
330
- options: { add?: string[]; remove?: string[] },
331
- ) {
309
+ async tags<T = any>(leadId: string, options: { add?: string[]; remove?: string[] }) {
332
310
  return this.patch<T>(`/api/crm/leads/${leadId}/tags`, options);
333
311
  }
334
312
 
@@ -371,10 +349,7 @@ export class Leads extends APIResource {
371
349
  /**
372
350
  * Retrieve the complete chronological timeline for a lead.
373
351
  */
374
- async activities<T = any>(
375
- leadId: string,
376
- params?: { page?: number; limit?: number },
377
- ) {
352
+ async activities<T = any>(leadId: string, params?: { page?: number; limit?: number }) {
378
353
  return this.get<T>(`/api/crm/leads/${leadId}/timeline`, { params } as any);
379
354
  }
380
355
 
@@ -388,11 +363,7 @@ export class Leads extends APIResource {
388
363
  /**
389
364
  * Update an existing note.
390
365
  */
391
- async updateNote<T = any>(
392
- leadId: string,
393
- noteId: string,
394
- params: { content: string },
395
- ) {
366
+ async updateNote<T = any>(leadId: string, noteId: string, params: { content: string }) {
396
367
  return this.patch<T>(`/api/crm/notes/${noteId}`, params);
397
368
  }
398
369
 
@@ -24,5 +24,4 @@ export class Payments extends APIResource {
24
24
  }) {
25
25
  return this.post<T>("/api/crm/payments/capture", payload);
26
26
  }
27
-
28
27
  }
@@ -43,5 +43,4 @@ export class Scoring extends APIResource {
43
43
  async recalculate<T = any>(leadId: string) {
44
44
  return this.post<T>(`/api/crm/scoring/${leadId}/recalculate`, {});
45
45
  }
46
-
47
46
  }
@@ -38,9 +38,7 @@ export class EmailResource extends APIResource {
38
38
  * });
39
39
  * ```
40
40
  */
41
- async sendEmailCampaign(
42
- payload: SendCampaignPayload,
43
- ): Promise<CampaignResult> {
41
+ async sendEmailCampaign(payload: SendCampaignPayload): Promise<CampaignResult> {
44
42
  return this.post<CampaignResult>("/api/saas/emails/campaign", payload);
45
43
  }
46
44
 
@@ -103,9 +103,7 @@ export class EventsResource extends APIResource {
103
103
  * ```
104
104
  */
105
105
  async list(): Promise<{ success: boolean; data: EventDefinition[] }> {
106
- return this.get<{ success: boolean; data: EventDefinition[] }>(
107
- "/api/saas/events",
108
- );
106
+ return this.get<{ success: boolean; data: EventDefinition[] }>("/api/saas/events");
109
107
  }
110
108
 
111
109
  /**
@@ -75,9 +75,7 @@ export class Health extends APIResource {
75
75
  * ```
76
76
  */
77
77
  async jobStatus(jobId: string): Promise<JobStatus> {
78
- const res = await this.get<{ data: JobStatus }>(
79
- `/api/saas/jobs/status/${jobId}`,
80
- );
78
+ const res = await this.get<{ data: JobStatus }>(`/api/saas/jobs/status/${jobId}`);
81
79
  return res.data;
82
80
  }
83
81
  }
@@ -117,10 +117,7 @@ export class Campaigns extends APIResource {
117
117
  * ```
118
118
  */
119
119
  async send<T = any>(campaignId: string, payload?: { scheduledAt?: string }) {
120
- return this.post<T>(
121
- `/api/saas/marketing/campaigns/${campaignId}/send`,
122
- payload || {},
123
- );
120
+ return this.post<T>(`/api/saas/marketing/campaigns/${campaignId}/send`, payload || {});
124
121
  }
125
122
 
126
123
  /**
@@ -33,10 +33,7 @@ export class Files extends APIResource {
33
33
  * const files = await erixClient.storage.files.list("Invoices", { year: "2026" });
34
34
  * ```
35
35
  */
36
- async list<T = any>(
37
- folder: string,
38
- params?: { year?: string; month?: string },
39
- ) {
36
+ async list<T = any>(folder: string, params?: { year?: string; month?: string }) {
40
37
  return this.get<T>(`/api/saas/storage/files/${folder}`, { params } as any);
41
38
  }
42
39
 
@@ -64,12 +64,9 @@ export class Conversations extends APIResource {
64
64
  * ```
65
65
  */
66
66
  async messages<T = any>(conversationId: string, params?: ListParams) {
67
- return this.get<T>(
68
- `/api/saas/chat/conversations/${conversationId}/messages`,
69
- {
70
- params,
71
- } as any,
72
- );
67
+ return this.get<T>(`/api/saas/chat/conversations/${conversationId}/messages`, {
68
+ params,
69
+ } as any);
73
70
  }
74
71
 
75
72
  /**
@@ -83,18 +80,11 @@ export class Conversations extends APIResource {
83
80
  * await erixClient.whatsapp.conversations.linkLead("conv_123", "lead_456");
84
81
  * ```
85
82
  */
86
- async linkLead<T = any>(
87
- conversationId: string,
88
- leadId: string,
89
- leadData?: any,
90
- ) {
91
- return this.post<T>(
92
- `/api/saas/chat/conversations/${conversationId}/link-lead`,
93
- {
94
- leadId,
95
- leadData,
96
- },
97
- );
83
+ async linkLead<T = any>(conversationId: string, leadId: string, leadData?: any) {
84
+ return this.post<T>(`/api/saas/chat/conversations/${conversationId}/link-lead`, {
85
+ leadId,
86
+ leadData,
87
+ });
98
88
  }
99
89
 
100
90
  /**
@@ -107,10 +97,7 @@ export class Conversations extends APIResource {
107
97
  * ```
108
98
  */
109
99
  async markRead<T = any>(conversationId: string) {
110
- return this.post<T>(
111
- `/api/saas/chat/conversations/${conversationId}/read`,
112
- {},
113
- );
100
+ return this.post<T>(`/api/saas/chat/conversations/${conversationId}/read`, {});
114
101
  }
115
102
 
116
103
  /**
@@ -85,6 +85,32 @@ export interface SendTemplateParams {
85
85
  * });
86
86
  * ```
87
87
  */
88
+ /**
89
+ * Resolved media metadata returned by any upload endpoint.
90
+ * `variants` is only present for raster images (JPEG, PNG, WebP, AVIF, GIF).
91
+ */
92
+ export interface ChatMediaMeta {
93
+ /** Raw CDN URL — always present, all media types */
94
+ url: string;
95
+ /** Categorical type — "image" | "video" | "audio" | "document" */
96
+ type: "image" | "video" | "audio" | "document";
97
+ /** Cloudflare-optimised variant URLs. Only present for raster images. */
98
+ variants?: {
99
+ /** 150px WebP — use for list thumbnails and chat bubbles */
100
+ thumb: string;
101
+ /** 600px WebP — use for modal previews */
102
+ medium: string;
103
+ /** 1200px WebP — use for hero or full-screen views */
104
+ full: string;
105
+ /** Original CDN URL, no transformation */
106
+ raw: string;
107
+ };
108
+ /** Storage key within the tenant bucket */
109
+ key?: string;
110
+ /** Original filename */
111
+ fileName?: string;
112
+ }
113
+
88
114
  export class Messages extends APIResource {
89
115
  /**
90
116
  * Send a free-text or media message to a WhatsApp number.
@@ -185,25 +211,26 @@ export class Messages extends APIResource {
185
211
  /**
186
212
  * Upload a media file for WhatsApp chat.
187
213
  *
188
- * This is the recommended way to upload media for WhatsApp chatting as it
189
- * allows the backend to perform WhatsApp-specific optimizations (resizing,
190
- * format conversion, and thumbnail generation).
214
+ * Works with any file type image, video, audio, or document.
215
+ * For raster images the response includes Cloudflare-optimised `variants`
216
+ * (thumb 150px, medium 600px, full 1200px). Non-image types return only `url`.
191
217
  *
192
218
  * @param file - The file to upload (File, Blob, or Buffer).
193
- * @returns The uploaded media details `{ url, type }`.
219
+ * @returns Full media metadata `{ url, type, variants?, key, fileName }`.
194
220
  *
195
221
  * @example
196
222
  * ```typescript
197
223
  * const file = input.files[0];
198
224
  * const { data } = await ecod.whatsapp.messages.upload(file);
199
- * console.log(data.url); // -> https://cdn.ecodrix.com/tenants/ABC/chat/chat_123.jpg
225
+ * // For images: data.variants.thumb 150px WebP from Cloudflare
226
+ * // For video/audio: use data.url directly in <video> or <audio>
200
227
  * ```
201
228
  */
202
- async upload<T = { url: string; type: string }>(file: any): Promise<{ success: boolean; data: T }> {
229
+ async upload(file: any): Promise<{ success: boolean; data: ChatMediaMeta }> {
203
230
  const formData = new FormData();
204
231
  formData.append("file", file);
205
232
 
206
- return this.post<{ success: boolean; data: T }>("/api/saas/chat/upload", formData, {
233
+ return this.post<{ success: boolean; data: ChatMediaMeta }>("/api/saas/chat/upload", formData, {
207
234
  headers: {
208
235
  "Content-Type": "multipart/form-data",
209
236
  },
@@ -49,9 +49,7 @@ export class Templates extends APIResource {
49
49
  * ```
50
50
  */
51
51
  async retrieve<T = any>(templateIdentifier: string) {
52
- return this.get<T>(
53
- `/api/saas/chat/templates/${encodeURIComponent(templateIdentifier)}`,
54
- );
52
+ return this.get<T>(`/api/saas/chat/templates/${encodeURIComponent(templateIdentifier)}`);
55
53
  }
56
54
 
57
55
  /**
@@ -145,9 +143,7 @@ export class Templates extends APIResource {
145
143
  * Validate if a template is ready for automated sending based on its mappings.
146
144
  */
147
145
  async validate<T = any>(templateName: string) {
148
- return this.get<T>(
149
- `/api/saas/chat/templates/${encodeURIComponent(templateName)}/validate`,
150
- );
146
+ return this.get<T>(`/api/saas/chat/templates/${encodeURIComponent(templateName)}/validate`);
151
147
  }
152
148
 
153
149
  /**
@@ -162,22 +158,16 @@ export class Templates extends APIResource {
162
158
  * });
163
159
  * ```
164
160
  */
165
- async preview<T = any>(
166
- templateName: string,
167
- context: { lead?: any; vars?: any },
168
- ) {
169
- return this.post<T>(
170
- `/api/saas/chat/templates/${encodeURIComponent(templateName)}/preview`,
171
- { context },
172
- );
161
+ async preview<T = any>(templateName: string, context: { lead?: any; vars?: any }) {
162
+ return this.post<T>(`/api/saas/chat/templates/${encodeURIComponent(templateName)}/preview`, {
163
+ context,
164
+ });
173
165
  }
174
166
 
175
167
  /**
176
168
  * Check which automations or sequences are currently using this template.
177
169
  */
178
170
  async checkUsage<T = any>(templateName: string) {
179
- return this.get<T>(
180
- `/api/saas/chat/templates/${encodeURIComponent(templateName)}/usage`,
181
- );
171
+ return this.get<T>(`/api/saas/chat/templates/${encodeURIComponent(templateName)}/usage`);
182
172
  }
183
173
  }