@ecodrix/erix-api 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -25
- package/dist/cli.js +38 -0
- package/dist/index.d.cts +1871 -0
- package/dist/index.d.ts +902 -42
- package/dist/ts/browser/index.global.js +14 -14
- package/dist/ts/browser/index.global.js.map +1 -1
- package/dist/ts/cjs/index.cjs +1 -1
- package/dist/ts/cjs/index.cjs.map +1 -1
- package/dist/ts/cjs/index.d.cts +902 -42
- package/dist/ts/esm/index.d.ts +902 -42
- package/dist/ts/esm/index.js +1 -1
- package/dist/ts/esm/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cli.ts +318 -0
- package/src/core.ts +20 -0
- package/src/index.ts +17 -1
- package/src/resource.ts +44 -5
- package/src/resources/crm/activities.ts +89 -0
- package/src/resources/crm/analytics.ts +89 -0
- package/src/resources/crm/automationDashboard.ts +24 -0
- package/src/resources/crm/automations.ts +145 -0
- package/src/resources/crm/index.ts +24 -0
- package/src/resources/crm/leads.ts +170 -42
- package/src/resources/crm/payments.ts +10 -0
- package/src/resources/crm/pipelines.ts +117 -0
- package/src/resources/crm/scoring.ts +33 -0
- package/src/resources/crm/sequences.ts +24 -0
- package/src/resources/events.ts +41 -0
- package/src/resources/health.ts +61 -0
- package/src/resources/marketing.ts +104 -0
- package/src/resources/meet.ts +9 -2
- package/src/resources/notifications.ts +30 -0
- package/src/resources/queue.ts +39 -0
- package/src/resources/storage.ts +72 -0
- package/src/resources/whatsapp/broadcasts.ts +31 -0
- package/src/resources/whatsapp/conversations.ts +34 -9
- package/src/resources/whatsapp/index.ts +20 -0
- package/src/resources/whatsapp/messages.ts +24 -0
- package/src/resources/whatsapp/templates.ts +99 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import repl from "node:repl";
|
|
5
|
+
import { Ecodrix } from "./core";
|
|
6
|
+
|
|
7
|
+
// Setup environment
|
|
8
|
+
dotenv.config();
|
|
9
|
+
|
|
10
|
+
// Build-time constants injected by tsup
|
|
11
|
+
declare const process: any;
|
|
12
|
+
const VERSION = process.env.SDK_VERSION || "1.0.2";
|
|
13
|
+
const NAME = process.env.SDK_NAME || "@ecodrix/erix-api";
|
|
14
|
+
const DESCRIPTION = process.env.SDK_DESCRIPTION || "Official ECODrIx SDK CLI";
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
|
|
18
|
+
program.name("erix").description(DESCRIPTION).version(VERSION);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Helper to initialize the Ecodrix client from environment or flags.
|
|
22
|
+
*/
|
|
23
|
+
function getClient(options: any): Ecodrix {
|
|
24
|
+
const apiKey = options.key || process.env.ECOD_API_KEY;
|
|
25
|
+
const clientCode = options.client || process.env.ECOD_CLIENT_CODE;
|
|
26
|
+
|
|
27
|
+
if (!apiKey) {
|
|
28
|
+
console.error(pc.red("Error: API Key is missing."));
|
|
29
|
+
console.log(
|
|
30
|
+
pc.yellow("Set ECOD_API_KEY environment variable or use --key <key>"),
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return new Ecodrix({
|
|
36
|
+
apiKey,
|
|
37
|
+
clientCode,
|
|
38
|
+
baseUrl: options.baseUrl || process.env.ECOD_BASE_URL,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.option("-k, --key <key>", "ECODrIx API Key")
|
|
44
|
+
.option("-c, --client <code...", "Tenant Client Code")
|
|
45
|
+
.option("--base-url <url>", "API Base URL override");
|
|
46
|
+
|
|
47
|
+
// --- WHOAMI ---
|
|
48
|
+
program
|
|
49
|
+
.command("whoami")
|
|
50
|
+
.description("Verify current authentication and tenant status")
|
|
51
|
+
.action(async (options, command) => {
|
|
52
|
+
const globalOptions = command.parent.opts();
|
|
53
|
+
const ecod = getClient(globalOptions);
|
|
54
|
+
|
|
55
|
+
console.log(pc.cyan("Checking connection to ECODrIx Platform..."));
|
|
56
|
+
try {
|
|
57
|
+
const me = (await ecod.request("GET", "/api/saas/me/profile")) as any;
|
|
58
|
+
console.log(pc.green("✔ Authenticated successfully!"));
|
|
59
|
+
console.log(`${pc.bold("User ID:")} ${me.id}`);
|
|
60
|
+
console.log(
|
|
61
|
+
`${pc.bold("Organisation:")} ${me.organisation?.name || "N/A"}`,
|
|
62
|
+
);
|
|
63
|
+
if (globalOptions.client) {
|
|
64
|
+
console.log(
|
|
65
|
+
`${pc.bold("Tenant Code:")} ${pc.magenta(globalOptions.client)}`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
} catch (error: any) {
|
|
69
|
+
console.error(pc.red("✖ Authentication failed"));
|
|
70
|
+
console.error(pc.dim(error.message));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// --- WHATSAPP ---
|
|
76
|
+
const whatsapp = program
|
|
77
|
+
.command("whatsapp")
|
|
78
|
+
.description("WhatsApp Business API operations");
|
|
79
|
+
|
|
80
|
+
whatsapp
|
|
81
|
+
.command("send-template")
|
|
82
|
+
.description("Send a WhatsApp template message")
|
|
83
|
+
.argument("<phone>", "Recipient phone number")
|
|
84
|
+
.argument("<template>", "Template name")
|
|
85
|
+
.option("-v, --vars <json>", "Template variables as JSON string", "[]")
|
|
86
|
+
.action(async (phone, template, options, command) => {
|
|
87
|
+
const globalOptions = command.parent.parent.opts();
|
|
88
|
+
const ecod = getClient(globalOptions);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const vars = JSON.parse(options.vars);
|
|
92
|
+
console.log(pc.cyan(`Sending template '${template}' to ${phone}...`));
|
|
93
|
+
|
|
94
|
+
const result = (await ecod.whatsapp.messages.sendTemplate({
|
|
95
|
+
to: phone,
|
|
96
|
+
templateName: template,
|
|
97
|
+
language: "en_US",
|
|
98
|
+
variables: vars,
|
|
99
|
+
})) as any;
|
|
100
|
+
|
|
101
|
+
console.log(pc.green("✔ Message sent successfully!"));
|
|
102
|
+
console.log(pc.dim(`ID: ${result?.id || "N/A"}`));
|
|
103
|
+
} catch (error: any) {
|
|
104
|
+
console.error(pc.red("✖ Failed to send message"));
|
|
105
|
+
console.error(pc.dim(error.message));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// --- CRM ---
|
|
110
|
+
const crm = program.command("crm").description("CRM and Lead management");
|
|
111
|
+
|
|
112
|
+
crm
|
|
113
|
+
.command("leads")
|
|
114
|
+
.description("List recent leads")
|
|
115
|
+
.option("-l, --limit <number>", "Number of leads to fetch", "10")
|
|
116
|
+
.option("-s, --status <status>", "Filter by lead status")
|
|
117
|
+
.option("-p, --pipeline <id>", "Filter by pipeline ID")
|
|
118
|
+
.option("-q, --search <query>", "Search by name or email")
|
|
119
|
+
.action(async (options, command) => {
|
|
120
|
+
const globalOptions = command.parent.parent.opts();
|
|
121
|
+
const ecod = getClient(globalOptions);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const limit = parseInt(options.limit);
|
|
125
|
+
console.log(pc.cyan(`Fetching last ${limit} leads...`));
|
|
126
|
+
|
|
127
|
+
const queryParams: any = { limit };
|
|
128
|
+
if (options.status) queryParams.status = options.status;
|
|
129
|
+
if (options.pipeline) queryParams.pipelineId = options.pipeline;
|
|
130
|
+
if (options.search) queryParams.search = options.search;
|
|
131
|
+
|
|
132
|
+
const response: any = await ecod.crm.leads.list(queryParams);
|
|
133
|
+
const leads = Array.isArray(response.data)
|
|
134
|
+
? response.data
|
|
135
|
+
: Array.isArray(response)
|
|
136
|
+
? response
|
|
137
|
+
: [];
|
|
138
|
+
|
|
139
|
+
if (leads.length === 0) {
|
|
140
|
+
console.log(pc.yellow("No leads found."));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.table(
|
|
145
|
+
leads.map((l: any) => ({
|
|
146
|
+
ID: l.id || l._id,
|
|
147
|
+
Name: `${l.firstName} ${l.lastName || ""}`.trim(),
|
|
148
|
+
Phone: l.phone,
|
|
149
|
+
Status: l.status,
|
|
150
|
+
Score: l.score || 0,
|
|
151
|
+
Created: new Date(l.createdAt).toLocaleDateString(),
|
|
152
|
+
})),
|
|
153
|
+
);
|
|
154
|
+
} catch (error: any) {
|
|
155
|
+
console.error(pc.red("✖ Failed to fetch leads"));
|
|
156
|
+
console.error(pc.dim(error.message));
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
crm
|
|
161
|
+
.command("pipelines")
|
|
162
|
+
.description("List all CRM pipelines")
|
|
163
|
+
.action(async (options, command) => {
|
|
164
|
+
const globalOptions = command.parent.parent.opts();
|
|
165
|
+
const ecod = getClient(globalOptions);
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
console.log(pc.cyan("Fetching pipelines..."));
|
|
169
|
+
const response: any = await ecod.crm.pipelines.list();
|
|
170
|
+
const pipelines = response.data || response || [];
|
|
171
|
+
|
|
172
|
+
if (pipelines.length === 0) {
|
|
173
|
+
console.log(pc.yellow("No pipelines found."));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.table(
|
|
178
|
+
pipelines.map((p: any) => ({
|
|
179
|
+
ID: p.id || p._id,
|
|
180
|
+
Name: p.name,
|
|
181
|
+
Default: p.isDefault ? "Yes" : "No",
|
|
182
|
+
Stages: p.stages?.length || 0
|
|
183
|
+
})),
|
|
184
|
+
);
|
|
185
|
+
} catch (error: any) {
|
|
186
|
+
console.error(pc.red("✖ Failed to fetch pipelines"));
|
|
187
|
+
console.error(pc.dim(error.message));
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// --- ANALYTICS ---
|
|
192
|
+
const analytics = program.command("analytics").description("Business Intelligence Analytics");
|
|
193
|
+
|
|
194
|
+
analytics
|
|
195
|
+
.command("overview")
|
|
196
|
+
.description("Get high-level CRM KPIs")
|
|
197
|
+
.option("-r, --range <range>", "Date range (e.g., 24h, 7d, 30d, 365d)", "30d")
|
|
198
|
+
.action(async (options, command) => {
|
|
199
|
+
const globalOptions = command.parent.parent.opts();
|
|
200
|
+
const ecod = getClient(globalOptions);
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
console.log(pc.cyan(`Fetching overview metrics for last ${options.range}...`));
|
|
204
|
+
const response: any = await ecod.crm.analytics.overview({ range: options.range });
|
|
205
|
+
const data = response.data || response;
|
|
206
|
+
|
|
207
|
+
console.log("\n" + pc.bold("OVERVIEW KPIs:"));
|
|
208
|
+
console.log(`Total Leads: ${pc.green(data.totalLeads || 0)}`);
|
|
209
|
+
console.log(`Open Value: ${pc.yellow("$" + (data.openValue || 0).toLocaleString())}`);
|
|
210
|
+
console.log(`Won Revenue: ${pc.green("$" + (data.wonRevenue || 0).toLocaleString())}`);
|
|
211
|
+
console.log(`Avg Score: ${pc.blue(data.avgScore?.toFixed(1) || 0)}`);
|
|
212
|
+
console.log(`Conversion: ${pc.magenta((data.conversionRate || 0).toFixed(2) + "%")}\n`);
|
|
213
|
+
|
|
214
|
+
} catch (error: any) {
|
|
215
|
+
console.error(pc.red("✖ Failed to fetch analytics overview"));
|
|
216
|
+
console.error(pc.dim(error.message));
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// --- WEBHOOKS ---
|
|
221
|
+
const webhooks = program
|
|
222
|
+
.command("webhooks")
|
|
223
|
+
.description("Webhook utility tools");
|
|
224
|
+
|
|
225
|
+
webhooks
|
|
226
|
+
.command("verify")
|
|
227
|
+
.description("Verify a cryptographic webhook signature")
|
|
228
|
+
.argument("<payload>", "The raw request body string")
|
|
229
|
+
.argument("<signature>", "The 'x-ecodrix-signature' header value")
|
|
230
|
+
.argument("<secret>", "Your webhook signing secret")
|
|
231
|
+
.action(async (payload, signature, secret, options, command) => {
|
|
232
|
+
const globalOptions = command.parent.parent.opts();
|
|
233
|
+
const ecod = getClient(globalOptions);
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
await ecod.webhooks.constructEvent(payload, signature, secret);
|
|
237
|
+
console.log(pc.green("✔ Signature is VALID"));
|
|
238
|
+
} catch (error: any) {
|
|
239
|
+
console.error(pc.red("✖ Error during verification"));
|
|
240
|
+
console.error(pc.dim(error.message));
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// --- SHELL (REPL) ---
|
|
245
|
+
program
|
|
246
|
+
.command("shell")
|
|
247
|
+
.alias("repl")
|
|
248
|
+
.description("Start an interactive SDK shell")
|
|
249
|
+
.action(async (options, command) => {
|
|
250
|
+
const globalOptions = command.parent.opts();
|
|
251
|
+
const ecod = getClient(globalOptions);
|
|
252
|
+
|
|
253
|
+
console.log(pc.magenta(pc.bold("\nWelcome to the Erix Interactive Shell")));
|
|
254
|
+
console.log(pc.dim(`SDK Version: ${VERSION}`));
|
|
255
|
+
console.log(pc.dim("The 'ecod' client is pre-initialized and ready.\n"));
|
|
256
|
+
|
|
257
|
+
const r = repl.start({
|
|
258
|
+
prompt: pc.cyan("erix > "),
|
|
259
|
+
useColors: true,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Load resources into REPL context for immediate access
|
|
263
|
+
r.context.ecod = ecod;
|
|
264
|
+
r.context.whatsapp = ecod.whatsapp;
|
|
265
|
+
r.context.crm = ecod.crm;
|
|
266
|
+
r.context.meet = ecod.meet;
|
|
267
|
+
r.context.media = ecod.media;
|
|
268
|
+
|
|
269
|
+
r.on("exit", () => {
|
|
270
|
+
console.log(pc.yellow("\nGoodbye!"));
|
|
271
|
+
process.exit(0);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// --- COMPLETION ---
|
|
276
|
+
program
|
|
277
|
+
.command("completion")
|
|
278
|
+
.description("Generate Bash auto-completion script")
|
|
279
|
+
.action(() => {
|
|
280
|
+
const script = `
|
|
281
|
+
# Erix Bash Completion
|
|
282
|
+
_erix_completions() {
|
|
283
|
+
local cur opts
|
|
284
|
+
COMPREPLY=()
|
|
285
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
286
|
+
opts="whoami whatsapp crm analytics webhooks shell completion"
|
|
287
|
+
|
|
288
|
+
if [[ \${COMP_CWORD} -eq 1 ]] ; then
|
|
289
|
+
COMPREPLY=( \$(compgen -W "\${opts}" -- \${cur}) )
|
|
290
|
+
return 0
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# Simple sub-command completion
|
|
294
|
+
case "\${COMP_WORDS[1]}" in
|
|
295
|
+
whatsapp)
|
|
296
|
+
COMPREPLY=( \$(compgen -W "send-template" -- \${cur}) )
|
|
297
|
+
;;
|
|
298
|
+
crm)
|
|
299
|
+
COMPREPLY=( \$(compgen -W "leads pipelines" -- \${cur}) )
|
|
300
|
+
;;
|
|
301
|
+
analytics)
|
|
302
|
+
COMPREPLY=( \$(compgen -W "overview" -- \${cur}) )
|
|
303
|
+
;;
|
|
304
|
+
webhooks)
|
|
305
|
+
COMPREPLY=( \$(compgen -W "verify" -- \${cur}) )
|
|
306
|
+
;;
|
|
307
|
+
esac
|
|
308
|
+
}
|
|
309
|
+
complete -F _erix_completions erix
|
|
310
|
+
`;
|
|
311
|
+
console.log(script.trim());
|
|
312
|
+
console.error(pc.yellow('\n# To enable, run: eval "$(erix completion)"'));
|
|
313
|
+
console.error(
|
|
314
|
+
pc.dim("# Or add it to your ~/.bashrc: erix completion >> ~/.bashrc"),
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
program.parse();
|
package/src/core.ts
CHANGED
|
@@ -9,6 +9,10 @@ import { Notifications } from "./resources/notifications";
|
|
|
9
9
|
import { EmailResource } from "./resources/email";
|
|
10
10
|
import { EventsResource } from "./resources/events";
|
|
11
11
|
import { Webhooks } from "./resources/webhooks";
|
|
12
|
+
import { Storage } from "./resources/storage";
|
|
13
|
+
import { Marketing } from "./resources/marketing";
|
|
14
|
+
import { Health } from "./resources/health";
|
|
15
|
+
import { Queue } from "./resources/queue";
|
|
12
16
|
import { io, type Socket } from "socket.io-client";
|
|
13
17
|
|
|
14
18
|
declare const process: any;
|
|
@@ -94,6 +98,18 @@ export class Ecodrix {
|
|
|
94
98
|
/** Cryptographic webhook signature verification. */
|
|
95
99
|
public readonly webhooks: Webhooks;
|
|
96
100
|
|
|
101
|
+
/** Tenant Cloud Storage mapping. */
|
|
102
|
+
public readonly storage: Storage;
|
|
103
|
+
|
|
104
|
+
/** Email and SMS Marketing Campaigns. */
|
|
105
|
+
public readonly marketing: Marketing;
|
|
106
|
+
|
|
107
|
+
/** Platform and tenant health diagnostics. */
|
|
108
|
+
public readonly health: Health;
|
|
109
|
+
|
|
110
|
+
/** Background job queue management. */
|
|
111
|
+
public readonly queue: Queue;
|
|
112
|
+
|
|
97
113
|
constructor(options: EcodrixOptions) {
|
|
98
114
|
if (!options.apiKey) {
|
|
99
115
|
throw new AuthenticationError("API Key is required");
|
|
@@ -151,6 +167,10 @@ export class Ecodrix {
|
|
|
151
167
|
this.email = new EmailResource(this.client);
|
|
152
168
|
this.events = new EventsResource(this.client);
|
|
153
169
|
this.webhooks = new Webhooks();
|
|
170
|
+
this.storage = new Storage(this.client);
|
|
171
|
+
this.marketing = new Marketing(this.client);
|
|
172
|
+
this.health = new Health(this.client);
|
|
173
|
+
this.queue = new Queue(this.client);
|
|
154
174
|
|
|
155
175
|
// Establish persistent Socket.io connection
|
|
156
176
|
this.socket = io(socketUrl, {
|
package/src/index.ts
CHANGED
|
@@ -2,16 +2,32 @@ export { Ecodrix, type EcodrixOptions } from "./core";
|
|
|
2
2
|
export * from "./error";
|
|
3
3
|
|
|
4
4
|
// Resource Type Exports
|
|
5
|
+
export * from "./resources/whatsapp/index";
|
|
5
6
|
export * from "./resources/whatsapp/messages";
|
|
6
7
|
export * from "./resources/whatsapp/conversations";
|
|
7
|
-
export * from "./resources/whatsapp/
|
|
8
|
+
export * from "./resources/whatsapp/templates";
|
|
9
|
+
export * from "./resources/whatsapp/broadcasts";
|
|
10
|
+
|
|
11
|
+
export * from "./resources/crm/index";
|
|
8
12
|
export * from "./resources/crm/leads";
|
|
13
|
+
export * from "./resources/crm/automations";
|
|
14
|
+
export * from "./resources/crm/pipelines";
|
|
15
|
+
export * from "./resources/crm/activities";
|
|
16
|
+
export * from "./resources/crm/analytics";
|
|
17
|
+
export * from "./resources/crm/sequences";
|
|
18
|
+
export * from "./resources/crm/scoring";
|
|
19
|
+
export * from "./resources/crm/payments";
|
|
20
|
+
export * from "./resources/crm/automationDashboard";
|
|
9
21
|
export * from "./resources/meet";
|
|
10
22
|
export * from "./resources/media";
|
|
11
23
|
export * from "./resources/notifications";
|
|
12
24
|
export * from "./resources/email";
|
|
13
25
|
export * from "./resources/events";
|
|
14
26
|
export * from "./resources/webhooks";
|
|
27
|
+
export * from "./resources/storage";
|
|
28
|
+
export * from "./resources/marketing";
|
|
29
|
+
export * from "./resources/health";
|
|
30
|
+
export * from "./resources/queue";
|
|
15
31
|
|
|
16
32
|
// Export the main client also as default for better ergonomics
|
|
17
33
|
import { Ecodrix } from "./core";
|
package/src/resource.ts
CHANGED
|
@@ -17,7 +17,11 @@ export interface RequestOptions extends AxiosRequestConfig {
|
|
|
17
17
|
export abstract class APIResource {
|
|
18
18
|
public constructor(protected readonly client: AxiosInstance) {}
|
|
19
19
|
|
|
20
|
-
protected async post<T>(
|
|
20
|
+
protected async post<T>(
|
|
21
|
+
url: string,
|
|
22
|
+
data?: any,
|
|
23
|
+
options?: RequestOptions,
|
|
24
|
+
): Promise<T> {
|
|
21
25
|
try {
|
|
22
26
|
const config = this.buildConfig(options);
|
|
23
27
|
const response = await this.client.post(url, data, config);
|
|
@@ -37,7 +41,38 @@ export abstract class APIResource {
|
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
protected async
|
|
44
|
+
protected async patch<T>(
|
|
45
|
+
url: string,
|
|
46
|
+
data?: any,
|
|
47
|
+
options?: RequestOptions,
|
|
48
|
+
): Promise<T> {
|
|
49
|
+
try {
|
|
50
|
+
const config = this.buildConfig(options);
|
|
51
|
+
const response = await this.client.patch(url, data, config);
|
|
52
|
+
return response.data;
|
|
53
|
+
} catch (error: any) {
|
|
54
|
+
this.handleError(error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
protected async put<T>(
|
|
59
|
+
url: string,
|
|
60
|
+
data?: any,
|
|
61
|
+
options?: RequestOptions,
|
|
62
|
+
): Promise<T> {
|
|
63
|
+
try {
|
|
64
|
+
const config = this.buildConfig(options);
|
|
65
|
+
const response = await this.client.put(url, data, config);
|
|
66
|
+
return response.data;
|
|
67
|
+
} catch (error: any) {
|
|
68
|
+
this.handleError(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected async deleteRequest<T>(
|
|
73
|
+
url: string,
|
|
74
|
+
options?: RequestOptions,
|
|
75
|
+
): Promise<T> {
|
|
41
76
|
try {
|
|
42
77
|
const config = this.buildConfig(options);
|
|
43
78
|
const response = await this.client.delete(url, config);
|
|
@@ -47,7 +82,9 @@ export abstract class APIResource {
|
|
|
47
82
|
}
|
|
48
83
|
}
|
|
49
84
|
|
|
50
|
-
private buildConfig(
|
|
85
|
+
private buildConfig(
|
|
86
|
+
options?: RequestOptions,
|
|
87
|
+
): AxiosRequestConfig | undefined {
|
|
51
88
|
if (!options) return undefined;
|
|
52
89
|
|
|
53
90
|
const config: AxiosRequestConfig = { ...options };
|
|
@@ -63,9 +100,11 @@ export abstract class APIResource {
|
|
|
63
100
|
private handleError(error: any): never {
|
|
64
101
|
if (error.response) {
|
|
65
102
|
throw new APIError(
|
|
66
|
-
error.response.data?.message ||
|
|
103
|
+
error.response.data?.message ||
|
|
104
|
+
error.response.data?.error ||
|
|
105
|
+
"API Request Failed",
|
|
67
106
|
error.response.status,
|
|
68
|
-
error.response.data?.code
|
|
107
|
+
error.response.data?.code,
|
|
69
108
|
);
|
|
70
109
|
}
|
|
71
110
|
throw new APIError(error.message || "Network Error");
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { APIResource } from "../../resource";
|
|
2
|
+
|
|
3
|
+
export interface LogActivityParams {
|
|
4
|
+
leadId: string;
|
|
5
|
+
type: "note" | "call" | "email" | "meeting" | "whatsapp" | "system";
|
|
6
|
+
title: string;
|
|
7
|
+
body?: string;
|
|
8
|
+
metadata?: Record<string, any>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface LogCallParams {
|
|
12
|
+
durationMinutes: number;
|
|
13
|
+
summary: string;
|
|
14
|
+
outcome: "answered" | "no_answer" | "busy" | "voicemail" | "wrong_number";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class Notes extends APIResource {
|
|
18
|
+
/**
|
|
19
|
+
* List all notes for a specific lead.
|
|
20
|
+
*/
|
|
21
|
+
async list<T = any>(leadId: string) {
|
|
22
|
+
return this.get<T>(`/api/saas/crm/leads/${leadId}/notes`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Add a note to a lead.
|
|
27
|
+
*/
|
|
28
|
+
async create<T = any>(leadId: string, params: { content: string }) {
|
|
29
|
+
return this.post<T>(`/api/saas/crm/leads/${leadId}/notes`, params);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Update an existing note.
|
|
34
|
+
*/
|
|
35
|
+
async update<T = any>(noteId: string, content: string) {
|
|
36
|
+
return this.patch<T>(`/api/saas/crm/notes/${noteId}`, { content });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Pin or unpin a note to the top of the feed.
|
|
41
|
+
*/
|
|
42
|
+
async pin<T = any>(noteId: string, isPinned = true) {
|
|
43
|
+
return this.patch<T>(`/api/saas/crm/notes/${noteId}/pin`, { isPinned });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Delete a note.
|
|
48
|
+
*/
|
|
49
|
+
async delete(noteId: string) {
|
|
50
|
+
return this.deleteRequest(`/api/saas/crm/notes/${noteId}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export class Activities extends APIResource {
|
|
55
|
+
public notes: Notes;
|
|
56
|
+
|
|
57
|
+
constructor(client: any) {
|
|
58
|
+
super(client);
|
|
59
|
+
this.notes = new Notes(client);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Retrieve the complete chronological timeline for a lead.
|
|
64
|
+
*/
|
|
65
|
+
async timeline<T = any>(leadId: string, params?: { page?: number; limit?: number }) {
|
|
66
|
+
return this.get<T>(`/api/saas/crm/leads/${leadId}/timeline`, { params } as any);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* List specific activities (filtered by type).
|
|
71
|
+
*/
|
|
72
|
+
async list<T = any>(leadId: string, params?: { type?: string; page?: number; limit?: number }) {
|
|
73
|
+
return this.get<T>(`/api/saas/crm/activities`, { params: { leadId, ...params } } as any);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Generic method to log a business activity/event.
|
|
78
|
+
*/
|
|
79
|
+
async log<T = any>(params: LogActivityParams) {
|
|
80
|
+
return this.post<T>("/api/saas/crm/activities", params);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Specific method to log communication outcomes.
|
|
85
|
+
*/
|
|
86
|
+
async logCall<T = any>(leadId: string, params: LogCallParams) {
|
|
87
|
+
return this.post<T>(`/api/saas/crm/leads/${leadId}/calls`, params);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { APIResource } from "../../resource";
|
|
2
|
+
|
|
3
|
+
export type AnalyticsRange = "24h" | "7d" | "30d" | "60d" | "90d" | "365d";
|
|
4
|
+
|
|
5
|
+
export interface AnalyticsParams {
|
|
6
|
+
range?: AnalyticsRange;
|
|
7
|
+
from?: string;
|
|
8
|
+
to?: string;
|
|
9
|
+
pipelineId?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class Analytics extends APIResource {
|
|
13
|
+
/**
|
|
14
|
+
* KPIs: total leads, pipeline value, won revenue, avg score, conversion rate.
|
|
15
|
+
*/
|
|
16
|
+
async overview<T = any>(params?: AnalyticsParams) {
|
|
17
|
+
return this.get<T>("/api/saas/crm/analytics/overview", { params } as any);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Stage-by-stage lead counts and conversion percentages.
|
|
22
|
+
*/
|
|
23
|
+
async funnel<T = any>(pipelineId: string) {
|
|
24
|
+
return this.get<T>("/api/saas/crm/analytics/funnel", { params: { pipelineId } } as any);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Revenue forecast: deal value × stage probability.
|
|
29
|
+
*/
|
|
30
|
+
async forecast<T = any>(pipelineId?: string) {
|
|
31
|
+
return this.get<T>("/api/saas/crm/analytics/forecast", { params: { pipelineId } } as any);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lead source breakdown: count, conversion rate, total value per source.
|
|
36
|
+
*/
|
|
37
|
+
async sources<T = any>(params?: AnalyticsParams) {
|
|
38
|
+
return this.get<T>("/api/saas/crm/analytics/sources", { params } as any);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Team leaderboard: won deals, revenue, activity count, conversion rate per member.
|
|
43
|
+
*/
|
|
44
|
+
async team<T = any>(params?: AnalyticsParams) {
|
|
45
|
+
return this.get<T>("/api/saas/crm/analytics/team", { params } as any);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Daily activity counts by type. For activity calendar.
|
|
50
|
+
*/
|
|
51
|
+
async heatmap<T = any>(params?: AnalyticsParams) {
|
|
52
|
+
return this.get<T>("/api/saas/crm/analytics/heatmap", { params } as any);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Score distribution: how many leads in each score bucket.
|
|
57
|
+
*/
|
|
58
|
+
async scores<T = any>() {
|
|
59
|
+
return this.get<T>("/api/saas/crm/analytics/scores");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Avg time leads spend in each stage. Helps find bottlenecks.
|
|
64
|
+
*/
|
|
65
|
+
async stageTime<T = any>(pipelineId: string) {
|
|
66
|
+
return this.get<T>("/api/saas/crm/analytics/stage-time", { params: { pipelineId } } as any);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Tiered Growth Report matching business sophistication.
|
|
71
|
+
*/
|
|
72
|
+
async tiered<T = any>(params?: AnalyticsParams) {
|
|
73
|
+
return this.get<T>("/api/saas/crm/analytics/tiered", { params } as any);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Consolidated analytics including CRM overview and WhatsApp.
|
|
78
|
+
*/
|
|
79
|
+
async summary<T = any>(params?: AnalyticsParams) {
|
|
80
|
+
return this.get<T>("/api/saas/crm/analytics/summary", { params } as any);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* WhatsApp volume and delivery analytics.
|
|
85
|
+
*/
|
|
86
|
+
async whatsapp<T = any>(params?: AnalyticsParams) {
|
|
87
|
+
return this.get<T>("/api/saas/crm/analytics/whatsapp", { params } as any);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { APIResource } from "../../resource";
|
|
2
|
+
|
|
3
|
+
export class AutomationDashboard extends APIResource {
|
|
4
|
+
/**
|
|
5
|
+
* Retrieve summary statistics for automation health.
|
|
6
|
+
*/
|
|
7
|
+
async stats<T = any>() {
|
|
8
|
+
return this.get<T>("/api/saas/crm/automation/stats");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* List recent EventLog entries (automation logs).
|
|
13
|
+
*/
|
|
14
|
+
async logs<T = any>(params?: { limit?: number; status?: string }) {
|
|
15
|
+
return this.get<T>("/api/saas/crm/automation/logs", { params } as any);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Re-emit a failed event log to process its automations again.
|
|
20
|
+
*/
|
|
21
|
+
async retryFailedEvent<T = any>(logId: string) {
|
|
22
|
+
return this.post<T>(`/api/saas/crm/automation/logs/${logId}/retry`, {});
|
|
23
|
+
}
|
|
24
|
+
}
|