@ecodrix/erix-api
v1.3.4
Published
Official Isomorphic SDK for the ECODrIx platform. Native support for WhatsApp, CRM, Storage, and Meetings across TS, JS, Python, and Java.
Downloads
3,910
Maintainers
Readme
@ecodrix/erix-api
The official, isomorphic SDK for the ECODrIx platform.
Manage WhatsApp conversations, CRM leads, pipelines, automations, marketing campaigns, file storage, and Google Meet — all from a single, type-safe library.
Table of Contents
- Installation
- Quick Start
- Configuration
- Resources
- Enterprise Capabilities
- Real-time Events
- Error Handling
- Browser / CDN Usage
Installation
# pnpm (recommended)
pnpm add @ecodrix/erix-api
# npm
npm install @ecodrix/erix-apiRequires: Node.js >= 18
Quick Start
import { Ecodrix } from "@ecodrix/erix-api";
const ecod = new Ecodrix({
apiKey: process.env.ECOD_API_KEY!,
clientCode: process.env.ECOD_CLIENT_CODE,
});
// Send a WhatsApp message
await ecod.whatsapp.messages.send({
to: "+919876543210",
text: "Hello from ECODrIx!",
});
// Create a CRM lead
const lead = await ecod.crm.leads.create({
firstName: "Priya",
phone: "+919876543210",
source: "website",
});
// Fire an automation trigger
await ecod.events.trigger({
trigger: "trial_started",
phone: "+919876543210",
createLeadIfMissing: true,
});Configuration
Pass an options object to new Ecodrix(options):
| Option | Type | Required | Default | Description |
| ------------ | -------- | ----------- | ------------------------- | ---------------------------------------------- |
| apiKey | string | ✅ Yes | — | Your ECOD Platform API key |
| clientCode | string | Recommended | — | Your tenant ID — scopes all requests |
| baseUrl | string | No | https://api.ecodrix.com | Override the API base URL (e.g. for local dev) |
| socketUrl | string | No | Same as baseUrl | Override the Socket.io server URL |
const ecod = new Ecodrix({
apiKey: process.env.ECOD_API_KEY!,
clientCode: process.env.ECOD_CLIENT_CODE,
baseUrl: "http://localhost:4000", // For local development
});Resources
Access via ecod.whatsapp.
ecod.whatsapp.messages
Send a text message
await ecod.whatsapp.messages.send({
to: "+919876543210",
text: "Your appointment is confirmed!",
});Send a media message (image, video, document, audio)
await ecod.whatsapp.messages.send({
to: "+919876543210",
mediaUrl: "https://cdn.ecodrix.com/invoice.pdf",
mediaType: "document",
filename: "invoice.pdf",
});Send a template message via queue
await ecod.whatsapp.messages.sendTemplate({
to: "+919876543210",
templateName: "appointment_reminder",
language: "en_US",
variables: ["Priya", "Tomorrow 10AM"],
});Mark messages as read
await ecod.whatsapp.messages.markRead("message_id");ecod.whatsapp.conversations
// List conversations (cursor-based pagination)
const { data } = await ecod.whatsapp.conversations.list({
limit: 20,
status: "open",
after: "cursor_token",
});
// Get a specific conversation
const conv = await ecod.whatsapp.conversations.retrieve("conversation_id");
// Get messages in a conversation
const { data: msgs } = await ecod.whatsapp.conversations.messages("conversation_id", {
limit: 50,
});
// Create a conversation explicitly
await ecod.whatsapp.conversations.create({ phone: "+919876543210", name: "Priya" });
// Link a conversation to a CRM lead
await ecod.whatsapp.conversations.linkLead("conversation_id", "lead_id");
// Mark a conversation as read (clears unread badge)
await ecod.whatsapp.conversations.markRead("conversation_id");
// Delete / archive a conversation
await ecod.whatsapp.conversations.delete("conversation_id");
// Bulk delete conversations
await ecod.whatsapp.conversations.bulkDelete(["conv_1", "conv_2"]);ecod.whatsapp.templates
// List pre-approved Meta templates
const { data } = await ecod.whatsapp.templates.list();
// Sync templates from the Meta Business Account
await ecod.whatsapp.templates.sync();ecod.whatsapp.broadcasts
Send a personalised WhatsApp template message to multiple recipients at once.
await ecod.whatsapp.broadcasts.create({
name: "April Promo",
templateName: "discount_offer",
templateLanguage: "en_US",
recipients: [
{ phone: "+919876543210", variables: ["Priya", "20%"] },
{ phone: "+919123456789", variables: ["Ravi", "30%"] },
],
});
// List past broadcasts
const { data } = await ecod.whatsapp.broadcasts.list({ status: "completed" });ecod.whatsapp.sendTemplate — Direct Template (Bypass Queue)
Use this for high-priority utility templates that must deliver immediately outside the automation queue.
const result = await ecod.whatsapp.sendTemplate({
phone: "+919876543210",
templateName: "payment_confirmation",
variables: {
name: "Priya",
amount: "₹1,500",
},
});
// result.messageId → WA message IDCRM
Access via ecod.crm.
ecod.crm.leads
Create
const { data: lead } = await ecod.crm.leads.create({
firstName: "Priya",
lastName: "Sharma",
phone: "+919876543210",
email: "[email protected]",
source: "website",
pipelineId: "pipeline_id",
stageId: "stage_id",
metadata: { utmSource: "google" },
});Upsert by phone — Creates if absent, updates if exists
await ecod.crm.leads.upsert({
leadData: { phone: "+919876543210", firstName: "Priya" },
trigger: "webinar_joined",
});List with filters
const { data } = await ecod.crm.leads.list({
status: "new",
source: "whatsapp",
pipelineId: "pipeline_id",
page: 1,
limit: 25,
});Retrieve
const { data: lead } = await ecod.crm.leads.retrieve("lead_id");
const { data } = await ecod.crm.leads.retrieveByPhone("+919876543210");
const { data } = await ecod.crm.leads.retrieveByRef("orderId", "ORD-123");Update
await ecod.crm.leads.update("lead_id", { email: "[email protected]" });
await ecod.crm.leads.updateMetadata("lead_id", {
refs: { orderId: "ORD-456" },
extra: { plan: "pro" },
});Move & Convert
await ecod.crm.leads.move("lead_id", "target_stage_id");
await ecod.crm.leads.convert("lead_id", "won", "Signed contract");Tags
await ecod.crm.leads.tags("lead_id", { add: ["vip", "hot"], remove: ["cold"] });Score
await ecod.crm.leads.recalculateScore("lead_id");Bulk
await ecod.crm.leads.import(leadArray); // Bulk import
await ecod.crm.leads.delete("lead_id");
await ecod.crm.leads.bulkDelete(["id_1", "id_2"]);ecod.crm.pipelines
// CRUD
const { data } = await ecod.crm.pipelines.list();
await ecod.crm.pipelines.create({ name: "Sales", isDefault: true });
await ecod.crm.pipelines.retrieve("pipeline_id");
await ecod.crm.pipelines.update("pipeline_id", { name: "Deals" });
await ecod.crm.pipelines.delete("pipeline_id");
// Advanced
await ecod.crm.pipelines.setDefault("pipeline_id");
await ecod.crm.pipelines.duplicate("pipeline_id", "Sales Copy");
await ecod.crm.pipelines.archive("pipeline_id");
const { data: board } = await ecod.crm.pipelines.board("pipeline_id");
const { data: forecast } = await ecod.crm.pipelines.forecast("pipeline_id");
// Stage management
await ecod.crm.pipelines.addStage("pipeline_id", { name: "Negotiation", color: "#f59e0b" });
await ecod.crm.pipelines.reorderStages("pipeline_id", ["stage_1", "stage_3", "stage_2"]);
await ecod.crm.pipelines.updateStage("stage_id", { probability: 80 });
await ecod.crm.pipelines.deleteStage("stage_id", "fallback_stage_id");ecod.crm.automations
// CRUD
const { data } = await ecod.crm.automations.list();
await ecod.crm.automations.create({ name: "Follow-up", trigger: "lead_created", nodes: [], edges: [] });
await ecod.crm.automations.update("rule_id", { isActive: false });
await ecod.crm.automations.toggle("rule_id");
await ecod.crm.automations.deleteRule("rule_id");
await ecod.crm.automations.bulkDelete(["rule_1", "rule_2"]);
// Testing & events
await ecod.crm.automations.test("rule_id", "lead_id");
const { data: events } = await ecod.crm.automations.getAvailableEvents();
// Enrollment management
const { data } = await ecod.crm.automations.enrollments("rule_id", { status: "active", page: 1 });
await ecod.crm.automations.getEnrollment("enrollment_id");
await ecod.crm.automations.pauseEnrollment("rule_id", "enrollment_id");
await ecod.crm.automations.resumeEnrollment("rule_id", "enrollment_id");
// Run inspection
const { data: runs } = await ecod.crm.automations.runs("rule_id");
await ecod.crm.automations.getRun("run_id");
await ecod.crm.automations.resumeRun("run_id");
await ecod.crm.automations.abortRun("run_id");
// Unlock a wait_event node from external tool (e.g. Zapier, payment gateway callback)
await ecod.crm.automations.webhookEvent("rule_id", "payment_received", {
amount: 1500,
currency: "INR",
});ecod.crm.sequences
Manually enroll / unenroll leads in drip automation sequences.
await ecod.crm.sequences.enroll({
leadId: "lead_id",
ruleId: "rule_id",
variables: { discount: "20%" },
});
await ecod.crm.sequences.unenroll("enrollment_id");
const { data } = await ecod.crm.sequences.listForLead("lead_id");ecod.crm.activities
// Lead timeline (all CRM events in order)
const { data } = await ecod.crm.activities.timeline("lead_id", { page: 1, limit: 50 });
// Filtered activity list
const { data } = await ecod.crm.activities.list("lead_id", { type: "call" });
// Log a call outcome
await ecod.crm.activities.logCall("lead_id", {
outcome: "answered",
duration: 120,
notes: "Interested in Pro plan",
});
// Log a generic activity
await ecod.crm.activities.log({
leadId: "lead_id",
type: "email_opened",
title: "Campaign Email Opened",
});
// --- Notes ---
const { data: notes } = await ecod.crm.activities.notes.list("lead_id");
await ecod.crm.activities.notes.create("lead_id", { content: "Call scheduled for Monday" });
await ecod.crm.activities.notes.update("note_id", "Updated note text");
await ecod.crm.activities.notes.pin("note_id"); // pin to top
await ecod.crm.activities.notes.pin("note_id", false); // unpinecod.crm.analytics
const { data: overview } = await ecod.crm.analytics.overview("pipeline_id");
const { data: conversion } = await ecod.crm.analytics.conversionRate("pipeline_id");
const { data: velocity } = await ecod.crm.analytics.dealVelocity("pipeline_id");
const { data: stageTime } = await ecod.crm.analytics.stageTime("pipeline_id");ecod.crm.scoring
const { data } = await ecod.crm.scoring.getConfig();
await ecod.crm.scoring.updateConfig({ rules: [...] });ecod.crm.payments
const { data } = await ecod.crm.payments.list("lead_id");ecod.crm.automationDashboard
const { data: stats } = await ecod.crm.automationDashboard.stats();
const { data: logs } = await ecod.crm.automationDashboard.logs({ limit: 10, status: "failed" });
await ecod.crm.automationDashboard.retryFailedEvent("log_id");Events & Workflows
Access via ecod.events. Connect your external applications to the CRM automation engine.
List all available triggers
const { data: triggers } = await ecod.events.list();Register a custom event
await ecod.events.assign({
name: "cart_abandoned",
displayName: "Shopping Cart Abandoned",
pipelineId: "pipeline_123",
});
// Deactivate
await ecod.events.unassign("cart_abandoned");
await ecod.events.unassignBulk(["event_a", "event_b"]);Fire an event / trigger workflows
await ecod.events.trigger({
trigger: "cart_abandoned",
phone: "+919876543210",
variables: { items: "T-Shirt, Mug", total: "₹850" },
createLeadIfMissing: true,
delayMinutes: 30,
// Optional: auto-book a Google Meet during the workflow
requiresMeet: true,
meetConfig: {
summary: "Follow-up call",
duration: 30,
timezone: "Asia/Kolkata",
},
});Custom event definitions (CRM-managed)
const { data } = await ecod.events.listCustomEvents();
await ecod.events.createCustomEvent({ name: "webinar_attended", displayName: "Webinar Attended" });
await ecod.events.deleteCustomEvent("event_id");
await ecod.events.emit({ eventName: "webinar_attended", leadId: "lead_id" });Marketing
Access via ecod.marketing.
Email campaigns
await ecod.marketing.emails.sendCampaign({
recipients: ["[email protected]"],
subject: "Summer Sale!",
html: "<h1>Get 20% off!</h1>",
});
await ecod.marketing.emails.sendTest("[email protected]");Campaigns (Email + SMS)
await ecod.marketing.campaigns.create({ name: "Q2 Push", type: "email" });
await ecod.marketing.campaigns.list({ status: "active" });
await ecod.marketing.campaigns.retrieve("campaign_id");
await ecod.marketing.campaigns.update("campaign_id", { subject: "New subject" });
await ecod.marketing.campaigns.send("campaign_id", { scheduledAt: "2026-05-01T09:00:00Z" });
await ecod.marketing.campaigns.stats("campaign_id");
await ecod.marketing.campaigns.delete("campaign_id");WhatsApp marketing (CRM-integrated template dispatch)
Unlike the direct ecod.whatsapp.sendTemplate, this endpoint resolves CRM field variables automatically.
await ecod.marketing.whatsapp.sendTemplate({
phone: "+919876543210",
templateName: "seasonal_offer",
variables: { discount: "40%" },
});Meetings
Access via ecod.meet. Backed by Google Meet.
const { data: meeting } = await ecod.meet.create({
leadId: "lead_id",
participantName: "Priya Sharma",
participantPhone: "+919876543210",
startTime: "2026-04-10T10:00:00.000Z",
endTime: "2026-04-10T10:30:00.000Z",
});
console.log(meeting.meetLink); // → https://meet.google.com/abc-defg-hij
const { data: meetings } = await ecod.meet.list({ status: "scheduled" });
const { data } = await ecod.meet.retrieve("meeting_id");
await ecod.meet.update("meeting_id", { startTime: "2026-04-11T11:00:00.000Z" });
await ecod.meet.delete("meeting_id");Storage
Access via ecod.storage. Powered by Cloudflare R2.
This handles the full presigned-URL orchestration transparently:
- Requests a presigned PUT URL from the backend.
- Uploads directly to R2 (no proxy overhead).
- Confirms the upload.
// Node.js: from a Buffer
import { readFileSync } from "fs";
const fileBuffer = readFileSync("./contract.pdf");
const { data } = await ecod.storage.upload(fileBuffer, {
folder: "customer_documents",
filename: "contract.pdf",
contentType: "application/pdf",
});
console.log(data.url); // → https://cdn.ecodrix.com/customer_documents/contract.pdf
// Browser: from an <input> element
const file = document.getElementById("file-input").files[0];
const { data } = await ecod.storage.upload(file, {
folder: "avatars",
filename: file.name,
contentType: file.type,
});Media
Access via ecod.media. Manage files stored in R2.
// Get a presigned download URL for a private file
const { data } = await ecod.media.getDownloadUrl("confidential/contract.pdf");
// Monitor storage quota
const { data: usage } = await ecod.media.getUsage();
console.log(`${usage.usedMB} MB of ${usage.limitMB} MB used`);
// List files in a folder
await ecod.media.list({ folder: "invoices" });
// Delete a file
await ecod.media.delete("invoices/old_invoice.pdf");Access via ecod.email. Backed by SMTP or AWS SES depending on tenant configuration.
await ecod.email.sendEmailCampaign({
subject: "Summer Discount!",
recipients: ["[email protected]"],
html: "<h1>Get 20% off all plans!</h1>",
});
// Verify SMTP configuration
await ecod.email.sendTestEmail("[email protected]");Notifications & Logs
Access via ecod.notifications.
// Automation execution logs
const { data: logs } = await ecod.notifications.listLogs({
trigger: "lead_created",
status: "failed",
startDate: "2026-04-01",
endDate: "2026-04-30",
});
await ecod.notifications.retrieveLog("log_id");
// Aggregate stats
const { data: stats } = await ecod.notifications.getStats({
startDate: "2026-04-01",
endDate: "2026-04-30",
});
// Provider webhook callback logs
const { data } = await ecod.notifications.listCallbacks({ limit: 20 });Queue Management
Access via ecod.queue. Inspect and manage background automation jobs.
// View failed jobs
const { data: failed } = await ecod.queue.listFailed();
// Get queue health by status
const stats = await ecod.queue.getStats();
// stats → { waiting: 12, active: 3, completed: 482, failed: 7, delayed: 0 }
// Retry a failed job
await ecod.queue.retryJob("job_id");
// Remove a job permanently
await ecod.queue.deleteJob("job_id");Health & Diagnostics
Access via ecod.health.
// Global platform status
const health = await ecod.health.system();
// { status: "ok", version: "...", env: "production", uptime: 3600, db: "connected", queueDepth: 5 }
// Tenant-scoped service readiness
const clientHealth = await ecod.health.clientHealth();
// { clientCode: "...", services: { whatsapp: "connected", email: "configured", googleMeet: "configured" }, ... }
// Background job status lookup
const job = await ecod.health.jobStatus("job_id");Webhooks
Access via ecod.webhooks.
⚠️ Node.js only — Requires
node:crypto. Not available in browser bundles.
Enterprise Capabilities
Auto-Paginating Iterators
Stream all records without manual page management:
// With optional type parameter for full type safety
for await (const lead of ecod.crm.leads.listAutoPaging<Lead>({ status: "won" })) {
await syncToMyDatabase(lead);
}Bulk Data Chunking
Insert thousands of leads without hitting rate limits. createMany automatically chunks and concurrently streams batches:
const results = await ecod.crm.leads.createMany(massiveArrayOfLeads, 100);
console.log(`Ingested ${results.length} leads.`);Idempotency Keys
Prevent duplicate requests caused by automatic retries:
await ecod.email.sendEmailCampaign(
{ subject: "Promo", recipients: ["[email protected]"], html: "..." },
{ idempotencyKey: "promo_campaign_uuid_2026_q2" },
);Webhook Signature Verification
Cryptographically verify that webhook payloads originated from ECODrIx:
app.post(
"/api/webhooks",
express.raw({ type: "application/json" }),
async (req, res) => {
try {
const event = await ecod.webhooks.constructEvent(
req.body.toString(),
req.headers["x-ecodrix-signature"],
"whsec_your_webhook_secret",
);
console.log("Verified event:", event);
res.json({ received: true });
} catch (err) {
return res.status(400).send(`Invalid signature: ${err.message}`);
}
},
);Raw Execution Engine
Access any API endpoint — including experimental or custom ones — with full authentication and retry benefits:
const { data } = await ecod.request("POST", "/api/saas/beta-feature", {
experimentalFlag: true,
});Real-time Events
The SDK maintains a persistent Socket.io connection, scoped to your clientCode tenant. Use ecod.on() to subscribe:
ecod
.on("whatsapp.message_received", (msg) => {
console.log(`New message from ${msg.from}: ${msg.body}`);
})
.on("crm.lead_created", ({ leadId }) => {
console.log(`New lead: ${leadId}`);
})
.on("crm.lead_updated", ({ leadId, stageName }) => {
console.log(`${leadId} moved to ${stageName}`);
})
.on("meet.scheduled", ({ meetLink }) => {
console.log(`Meeting booked: ${meetLink}`);
})
.on("automation.failed", ({ trigger, reason }) => {
alertTeam(`Automation ${trigger} failed: ${reason}`);
});
// Graceful shutdown
process.on("SIGTERM", () => ecod.disconnect());Standard Event Reference
| Event | Payload | Description |
| --------------------------- | -------------------------------- | ------------------------------ |
| whatsapp.message_received | { from, body, conversationId } | Inbound WhatsApp message |
| whatsapp.message_sent | { to, messageId } | Outbound message delivered |
| crm.lead_created | { leadId, phone } | New CRM lead created |
| crm.lead_updated | { leadId, stageName } | Lead stage or fields updated |
| meet.scheduled | { meetingId, meetLink } | Google Meet appointment booked |
| storage.upload_confirmed | { key, url, sizeBytes } | File upload confirmed |
| automation.failed | { trigger, reason, leadId } | Automation execution failed |
Error Handling
All methods throw typed errors you can catch and inspect:
import { APIError, AuthenticationError, RateLimitError, Ecodrix } from "@ecodrix/erix-api";
try {
const { data } = await ecod.crm.leads.retrieve("non_existent_id");
} catch (err) {
if (err instanceof AuthenticationError) {
// 401 — invalid API key or client code
console.error("Check your credentials.");
} else if (err instanceof RateLimitError) {
// 429 — too many requests (SDK auto-retries up to 3×)
console.warn("Rate limit hit.");
} else if (err instanceof APIError) {
console.error(`API Error [${err.status}]: ${err.message}`);
} else {
throw err;
}
}Error Classes
| Class | HTTP Status | Description |
| --------------------- | ----------- | ------------------------------------------ |
| EcodrixError | — | Base error class |
| APIError | varies | Generic API error with .status and .code |
| AuthenticationError | 401 | Invalid API key or client code |
| RateLimitError | 429 | Too many requests |
Browser / CDN Usage
For usage without a bundler:
<!-- Via CDN (jsDelivr) -->
<script src="https://cdn.jsdelivr.net/npm/@ecodrix/erix-api/dist/ts/browser/index.global.js"></script>
<script>
const ecod = new Ecodrix.Ecodrix({
apiKey: "your_api_key",
clientCode: "YOUR_CLIENT_CODE",
});
ecod.whatsapp.messages.send({
to: "+919876543210",
text: "Hello from the browser!",
});
</script>⚠️ Note:
ecod.webhooks.constructEventandecod.storage.upload(fromBuffer) are Node.js only and will not function in browser environments.
Contributing
- Fork the repository.
- Create a feature branch:
git checkout -b feat/my-feature - Make changes, then run
pnpm checkto validate formatting and lint. - Run
pnpm buildto verify the output compiles cleanly. - Submit a Pull Request.
License
MIT © 2026 ECODrIx Team
