JSPM

  • Created
  • Published
  • Downloads 401
  • Score
    100M100P100Q110259F
  • License MIT

Official Isomorphic SDK for the ECODrIx platform. Native support for WhatsApp, CRM, Storage, and Meetings across TS, JS, Python, and Java.

Package Exports

  • @ecodrix/erix-api

Readme

@ecodrix/erix-api

NPM Version License: MIT TypeScript OpenAPI Node.js

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

# pnpm (recommended)
pnpm add @ecodrix/erix-api

# npm
npm install @ecodrix/erix-api

Requires: 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

WhatsApp

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 ID

CRM

Access via ecod.crm.


ecod.crm.leads

Create

const { data: lead } = await ecod.crm.leads.create({
  firstName: "Priya",
  lastName: "Sharma",
  phone: "+919876543210",
  email: "priya@example.com",
  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: "new@email.com" });
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); // unpin

ecod.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: ["user@example.com"],
  subject: "Summer Sale!",
  html: "<h1>Get 20% off!</h1>",
});
await ecod.marketing.emails.sendTest("dev@example.com");

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:

  1. Requests a presigned PUT URL from the backend.
  2. Uploads directly to R2 (no proxy overhead).
  3. 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");

Email

Access via ecod.email. Backed by SMTP or AWS SES depending on tenant configuration.

await ecod.email.sendEmailCampaign({
  subject: "Summer Discount!",
  recipients: ["user@example.com"],
  html: "<h1>Get 20% off all plans!</h1>",
});

// Verify SMTP configuration
await ecod.email.sendTestEmail("admin@example.com");

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: ["user@example.com"], 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.constructEvent and ecod.storage.upload (from Buffer) are Node.js only and will not function in browser environments.


Contributing

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Make changes, then run pnpm check to validate formatting and lint.
  4. Run pnpm build to verify the output compiles cleanly.
  5. Submit a Pull Request.

License

MIT © 2026 ECODrIx Team