Developers

ERIX API docs

Everything in ERIX over the API: leads, events and flows, the inbox, WhatsApp templates, bookings, files and live updates. Use the TypeScript SDK or plain HTTP.

Base URL
https://api.ecodrix.com/v1/api
Version
v1.0

Getting started

Authentication

Every request belongs to one workspace. Create keys in the dashboard under Settings → Developer. There are two kinds:

  • Workspace key. Send x-api-key and x-client-code. Full access to the workspace: it acts as the owner.
  • Scoped key. erix_<scope>_… sent as Authorization: Bearer. Limited to one module (crm, flow, connect, storage) or a sub-scope such as erix_connect_whatsapp_…. Revocable, and the right choice for integrations. Scoped keys can’t perform admin-only actions.

Keep keys on your server. Never put a key in browser code or in a NEXT_PUBLIC_* variable. In a browser, use the SDK’s proxy mode: requests go to your own route, which checks your user and adds the key server-side.

Base URL: https://api.ecodrix.com/v1/api. Paths are grouped by domain: /product/erix/* (CRM, events, invoices), /product/flow/* (automations), /platform/inbox/* (inbox), /infra/connect/* (channels, Meet) and /infra/storage/* (files). The SDK sets the base URL for you.

Headers
# Workspace key
x-api-key: YOUR_API_KEY
x-client-code: ERIX_CLNT_XXXXXX

# Scoped key
Authorization: Bearer erix_crm_YOUR_SECRET
Browser (proxy mode)
// No key in the page
const ecod = new ECODrIxAPI({
  proxy: {
    url: "/api/erix", // your server route adds x-api-key / x-client-code
    ticket: () => fetch("/api/erix/ticket").then((r) => r.text()), // optional, for realtime
  },
});

Getting started

Quick start

Install the SDK (Node.js 18 or later), then create a lead and fire your own event. Every active flow that listens for that event starts a run.

Prefer plain HTTP? Every sample on this page has a cURL tab; your choice is remembered.

Terminal
pnpm add @ecodrix/erix-api   # or: npm install @ecodrix/erix-api
import { ECODrIxAPI } from "@ecodrix/erix-api";

const ecod = new ECODrIxAPI({
  apiKey: process.env.ECOD_API_KEY,         // server-side only
  clientCode: process.env.ECOD_CLIENT_CODE, // e.g. ERIX_CLNT_XXXXXX
});

// 1. Save an enquiry as a lead
const { data: lead } = await ecod.product.erix.leads.create({
  firstName: "Asha",
  phone: "+91XXXXXXXXXX",
  source: "website",
});

// 2. Fire your own event
await ecod.product.erix.events.trigger({
  trigger: "enquiry_received",
  phone: "+91XXXXXXXXXX",
  variables: { product: "Annual plan" },
});

Getting started

Errors and limits

Responses are JSON. Success is { "success": true, "data": … }. Failures carry success: false, a readable message and a machine code in error or code. Validation failures add fieldErrors.

StatusMeaning
400Invalid input. See fieldErrors.
401Missing, invalid or revoked key.
403The key lacks the scope or role, or the feature isn’t on your plan.
404Not found in this workspace.
429Rate limit or monthly quota reached. Back off and retry.
  • Rate limits are per workspace. Current usage comes back in the standard RateLimit and RateLimit-Policy headers.
  • Retries. Send an Idempotency-Key header on channel sends, invoice and storage writes; a retry with the same key isn’t applied twice. /events/trigger takes idempotencyKey in the body.
  • SDK errors. Failed calls throw APIError (with status and code), with AuthenticationError and RateLimitError subclasses. The SDK retries 429s and network errors up to 3 times with backoff.
Error response
{
  "success": false,
  "error": "INVALID_KEY",
  "message": "Invalid, expired, or revoked API key."
}
import { APIError, RateLimitError } from "@ecodrix/erix-api";

try {
  await ecod.product.erix.leads.create({ firstName: "Asha", phone: "+91XXXXXXXXXX" });
} catch (err) {
  if (err instanceof RateLimitError) {
    // back off, then retry
  } else if (err instanceof APIError) {
    console.error(err.status, err.code, err.message);
  }
}

CRM

Leads

Contacts in your pipelines. Creating a lead logs a “Lead created” activity and starts any flow listening for new leads.

POST/v1/api/product/erix/crm/leads

Create a lead in a pipeline.

firstNamestringRequired
Contact’s first name.
phonestringRequired
Mobile number with country code, e.g. +91XXXXXXXXXX.
lastNamestringOptional
Contact’s last name.
emailstringOptional
Email address.
sourcestringOptional
Where the lead came from, e.g. website.
pipelineIdstringOptional
Defaults to your default pipeline.
stageIdstringOptional
Defaults to the pipeline’s first stage.
valuenumberOptional
Deal value.
metadataobjectOptional
Your own key/value data.

POST/v1/api/product/erix/crm/leads/upsert

Find a lead by phone, then update it or create it. Best for forms and webhooks.

leadDataobjectRequired
Lead fields; must include phone.
refsobjectOptional
Your own ids, e.g. { "orderId": "…" }. Saved on the lead for lookups.
pipelineIdstringOptional
Pipeline for a new lead.
stageIdstringOptional
Stage to create in, or move an existing lead to.
triggerstringOptional
A label for what caused the upsert, saved on the lead.

GET/v1/api/product/erix/crm/leads

Paginated, filterable list of leads.

searchstringOptional
Matches name, phone or email.
statusstringOptional
Lead status.
pipelineId / stageIdstringOptional
Limit to a pipeline or stage.
source / tagsstringOptional
Filter by source or tag.
startDate / endDateISO dateOptional
Created within this range.
page / limitnumberOptional
Pagination.
sortBy / sortDirstringOptional
Sort field and asc or desc.

GET/v1/api/product/erix/crm/leads/:leadId

Get one lead.

GET/v1/api/product/erix/crm/leads/phone/:phone

Look up a lead by phone number.

PATCH/v1/api/product/erix/crm/leads/:leadId

Update a lead’s fields.

PATCH/v1/api/product/erix/crm/leads/:leadId/move

Move a lead to another stage. Won and lost stages update its status.

stageIdstringRequired
Target stage.

PATCH/v1/api/product/erix/crm/leads/:leadId/tags

Add or remove tags.

addstring[]Optional
Tags to add.
removestring[]Optional
Tags to remove.

DELETE/v1/api/product/erix/crm/leads/:leadId

Archive a lead (soft delete).

const { data: lead } = await ecod.product.erix.leads.upsert({
  leadData: { firstName: "Asha", phone: "+91XXXXXXXXXX" },
  refs: { orderId: "ORD-1001" },
});

const { data: page } = await ecod.product.erix.leads.list({
  search: "Asha",
  limit: 20,
});

await ecod.product.erix.leads.move(lead.id, "STAGE_ID");
await ecod.product.erix.leads.tags(lead.id, { add: ["vip"] });

CRM

Pipelines and stages

A workspace can run several pipelines (sales, support, onboarding), each with ordered stages.

GET/v1/api/product/erix/crm/pipelines

List all pipelines with their stages.

POST/v1/api/product/erix/crm/pipelines

Create a pipeline.

namestringRequired
Pipeline name.
stagesstring[]Required
Stage names in order, e.g. ["New", "Won"].

GET/v1/api/product/erix/crm/pipelines/:pipelineId/board

Kanban view: stages with their leads.

POST/v1/api/product/erix/crm/pipelines/:pipelineId/stages

Add a stage to a pipeline.

const { data: pipelines } = await ecod.product.erix.pipelines.list();

await ecod.product.erix.pipelines.create({
  name: "Sales",
  stages: ["New", "Quoted", "Won"],
});

CRM

Notes and activity

Every lead has a timeline. Log calls, emails and meetings against it, or add notes for your team.

GET/v1/api/product/erix/crm/leads/:leadId/timeline

Everything that happened to a lead, newest first.

POST/v1/api/product/erix/crm/leads/:leadId/activities

Log an activity (call, email, meeting) on the timeline.

POST/v1/api/product/erix/crm/leads/:leadId/notes

Add a note to a lead.

contentstringRequired
The note text.
await ecod.product.erix.activities.notes.create(leadId, {
  content: "Asked for a quote",
});

const { data: timeline } = await ecod.product.erix.activities.timeline(leadId);

CRM

Lead scoring

Scores update from activity and deal progress. You can tune the weights and force a recalculation.

GET/v1/api/product/erix/crm/scoring

Get the current scoring weights.

PATCH/v1/api/product/erix/crm/scoring

Update the scoring weights.

POST/v1/api/product/erix/crm/scoring/:leadId/recalculate

Recalculate one lead’s score now.

const { data: config } = await ecod.product.erix.scoring.getConfig();
await ecod.product.erix.scoring.recalculate(leadId);

CRM

Analytics

The numbers behind the dashboard, for your own reports.

GET/v1/api/product/erix/crm/analytics/overview

Headline numbers: leads, conversion, pipeline value.

GET/v1/api/product/erix/crm/analytics/funnel

Stage-by-stage conversion for a pipeline.

pipelineIdstringRequired
Query parameter: the pipeline to analyse.

GET/v1/api/product/erix/crm/analytics/sources

Leads and wins by source.

GET/v1/api/product/erix/crm/analytics/team

Performance per team member.

const { data: overview } = await ecod.product.erix.analytics.overview();
const { data: funnel } = await ecod.product.erix.analytics.funnel(pipelineId);

Automation (Flow)

Fire an event

Flow is the automation engine. Your system tells it something happened (“order placed”, “form submitted”) and every active flow whose trigger matches starts a run. This is the only way to fire an event from outside.

Built-in events (payment captured, stage changed, …) are fired by ECODrIx itself and are rejected here with RESERVED_TRIGGER. Define your own under custom events. A scoped key needs the flow:events or flow scope. Each run counts toward your plan’s monthly runs.

POST/v1/api/product/erix/events/trigger

Fire an event. Returns trigger, idempotencyKey and, when a contact was found or created, leadId.

triggerstringRequired
Your event name. No spaces, max 100 characters.
phone / emailstringRequired
The contact. One is required unless your plan includes Flow v1.
variablesobjectOptional
Flat key/value pairs available to the flow’s steps.
dataobjectOptional
Any extra payload.
leadDataobjectOptional
firstName, lastName, source for a new contact.
createLeadIfMissingbooleanOptional
Find or create the CRM contact and attach it to the run.*
delaySeconds / delayMinutes / runAtnumber / ISO dateOptional
Run later, up to 30 days ahead. Use only one.*
idempotencyKeystringOptional
Same key, same event: safe to retry.

* Needs Flow v1 on your plan. Without it these fields are ignored and listed in data.ignored.

await ecod.product.erix.events.trigger({
  trigger: "order_placed",
  phone: "+91XXXXXXXXXX",
  variables: { orderId: "ORD-1001", amount: "4999" },
  idempotencyKey: "ORD-1001",
});

Automation (Flow)

Custom events

Define the event names your system will fire, so they show up as triggers in the Flow builder. Creating and deleting needs a workspace key or an admin session.

GET/v1/api/product/flow/config/triggers

Every trigger a flow can start on: built-ins plus your custom events.

POST/v1/api/product/flow/config/triggers

Create a custom event.

namestringRequired
Lowercase letters, digits and _ . -, e.g. appointment_confirm.
displayNamestringRequired
Name shown in the Flow builder.
descriptionstringOptional
What the event means.

DELETE/v1/api/product/flow/config/triggers/:id

Delete a custom event.

await ecod.product.erix.events.createCustomEvent({
  name: "order_placed",
  displayName: "Order placed",
});

const { data } = await ecod.product.erix.events.listCustomEvents();

Automation (Flow)

Flows and runs

Build flows in the dashboard. Over the API you can list them, start a run by hand and follow runs. Scoped keys need the flow scope. The SDK has no typed Flow methods yet; use the raw ecod.request(), which keeps auth and retries.

GET/v1/api/product/flow/workflows

List your flows.

POST/v1/api/product/flow/workflows/:id/run

Start a run now. Drafts can be test-run; archived flows return 409.

GET/v1/api/product/flow/runs

List recent runs.

GET/v1/api/product/flow/runs/:runId

One run with its step status.

GET/v1/api/product/flow/runs/:runId/logs

Step-by-step log for debugging.

const { data: flows } = await ecod.request("GET", "/product/flow/workflows");
const { data: run } = await ecod.request("GET", `/product/flow/runs/${runId}`);

Messaging

Unified inbox

One inbox holds your WhatsApp and phone-call conversations; Instagram, email and web chat are coming soon. A reply goes out on the conversation’s own channel.

GET/v1/api/platform/inbox/conversations

List conversations (cursor pagination, filter by status).

GET/v1/api/platform/inbox/conversations/:id/messages

Messages in a thread.

POST/v1/api/platform/inbox/conversations/:id/messages

Reply in a thread.

textstringOptional
Message text. Send text, media or both.
mediaobjectOptional
A file you uploaded: { key, mimeType, fileName }.

PATCH/v1/api/platform/inbox/conversations/:id/lead

Link the thread to an existing lead.

leadIdstringRequired
The lead to link.

POST/v1/api/platform/inbox/conversations/:id/read

Mark a thread as read.

GET/v1/api/platform/inbox/unread

Unread counts.

const { data: threads } = await ecod.product.erix.chat.conversations.list({
  status: "open",
  limit: 20,
});

await ecod.product.erix.chat.messages.send({
  conversationId: threads[0].id,
  text: "Thanks! Your quote is on the way.",
});

await ecod.product.erix.chat.conversations.linkLead(conversationId, leadId);

Messaging

Direct send

Send one message on a channel without an existing conversation: an OTP, an order update, a reminder. One endpoint per channel, plus a multi-channel fallback. Scoped keys need connect or the channel sub-scope (erix_connect_whatsapp_…). Bulk sends are Flow campaigns, not this endpoint.

POST/v1/api/infra/connect/send/whatsapp

Send one WhatsApp message.

phonestringRequired
Recipient with country code.
templateNamestringOptional
An approved template. Send this or text.
languagestringOptional
Template language, e.g. en.
variablesstring[]Optional
Template variables, in order.
textstringOptional
Free text. Needs an open 24-hour window.

POST/v1/api/infra/connect/send/email

Send one email.

tostringRequired
Recipient address.
subjectstringOptional
Required unless you send templateId.
htmlstringOptional
Email body.
templateIdstringOptional
A saved email template.
variablesobjectOptional
Template variables.

POST/v1/api/infra/connect/send/telegram

Send one Telegram message.

chatIdstringRequired
Telegram chat id.
textstringRequired
Message text.

POST/v1/api/infra/connect/send

Try channels in order until one succeeds.

tostringRequired
Recipient: phone or email.
messagestringOptional
Text. Send this or template.
templatestringOptional
Template name.
channelPreferencestring[]Optional
Order to try. Default ["whatsapp", "email"].
// WhatsApp template
await ecod.product.erix.chat.messages.sendTemplate({
  to: "+91XXXXXXXXXX",
  templateName: "order_update",
  language: "en",
  variables: ["Asha", "ORD-1001"],
});

// Email: no typed method yet, so use the raw request
await ecod.request("POST", "/infra/connect/send/email", {
  to: "customer@example.com",
  subject: "Your invoice",
  html: "<p>Thanks for your order.</p>",
});

Messaging

WhatsApp templates

Templates approved on your WhatsApp Business account. Business-initiated messages outside the 24-hour window must use one.

GET/v1/api/infra/connect/whatsapp/templates

List templates and their approval status.

POST/v1/api/infra/connect/whatsapp/templates/sync

Pull the latest templates from Meta.

const { data: templates } = await ecod.infra.connect.whatsapp.templates.list();
await ecod.infra.connect.whatsapp.templates.sync();

Platform

Google Meet

Book meetings against a lead; online meetings get a Meet link from your connected Google account. Flows can react to the meeting-created and meeting-rescheduled events, for reminders for example.

POST/v1/api/infra/connect/meet

Book a meeting.

leadIdstringRequired
The lead the meeting is for.
participantNamestringRequired
Who you’re meeting.
participantPhonestringRequired
Their phone number.
startTime / endTimeISO dateRequired
When it starts and ends.
participantEmailsstring[]Optional
Calendar invitees.
meetingModestringOptional
online (Meet link) or offline.
type / amountstring / numberOptional
free or paid, and the fee.

GET/v1/api/infra/connect/meet

List meetings (filter by leadId, status).

GET/v1/api/infra/connect/meet/:id

Get one meeting.

PATCH/v1/api/infra/connect/meet/:id

Reschedule or cancel a meeting.

startTime / endTimeISO dateOptional
New time.
statusstringOptional
cancelled to cancel.
const meeting = await ecod.infra.connect.meet.create({
  leadId,
  participantName: "Asha",
  participantPhone: "+91XXXXXXXXXX",
  startTime: "2026-11-02T10:00:00+05:30",
  endTime: "2026-11-02T10:30:00+05:30",
  meetingMode: "online",
});

await ecod.infra.connect.meet.reschedule(meetingId, { startTime, endTime });
await ecod.infra.connect.meet.delete(meetingId); // sets status "cancelled"

Platform

Storage and media

Files live in your workspace’s own storage, organised by folder. Post small files to /media. For large files, get a presigned URL, upload straight to storage, then confirm.

POST/v1/api/infra/storage/media

Upload files (multipart). Large images are compressed.

filefileRequired
Up to 50 files, 100 MB each.
folderstringOptional
Destination folder.

GET/v1/api/infra/storage/media

List media in a folder (?folder=).

DELETE/v1/api/infra/storage/media

Delete a file by ?key=.

POST/v1/api/infra/storage/files/upload-url

Get a presigned upload URL (plan feature).

folderstringRequired
Destination folder.
filenamestringRequired
File name with extension.
contentTypestringRequired
MIME type, e.g. application/pdf.

POST/v1/api/infra/storage/files/confirm-upload

Confirm a presigned upload; returns the file’s URL and type.

keystringRequired
The key returned with the upload URL.

POST/v1/api/infra/storage/files/download-url

Short-lived download URL for a file.

keystringRequired
The file’s key.

GET/v1/api/infra/storage/files/usage

Bytes used against your quota.

// Presign, upload straight to storage, and confirm in one call
const { data: file } = await ecod.infra.storage.media.upload(buffer, {
  folder: "invoices",
  filename: "INV-1001.pdf",
  contentType: "application/pdf",
});

Platform

Invoices and payments (coming soon)

Coming soon in the app. GST-ready invoices tied to a lead. Create one, attach a payment link from your connected payment gateway, and send it on WhatsApp or email. When the customer pays, the invoice is marked paid and flows can react.

POST/v1/api/product/erix/commerce/invoices

Create an invoice.

clientNamestringRequired
Who the invoice is for.
itemsobject[]Required
Line items: description, quantity, rate.
leadIdstringOptional
Link to a lead.
currencystringOptional
3-letter code, e.g. INR.
taxRate / discountnumberOptional
GST rate in percent, and discount.
dueDateISO dateOptional
Payment due date.
clientGstin / placeOfSupplystringOptional
15-character GSTIN and 2-digit state code.

GET/v1/api/product/erix/commerce/invoices

List invoices (page, limit, status).

POST/v1/api/product/erix/commerce/invoices/:id/payment-link

Create a payment link; the invoice moves to “sent”.

POST/v1/api/product/erix/commerce/invoices/:id/send-whatsapp

Send the invoice on WhatsApp.

POST/v1/api/product/erix/commerce/invoices/:id/send-email

Send the invoice by email.

const { data: invoice } = await ecod.product.erix.invoices.create({
  clientName: "Asha",
  leadId,
  currency: "INR",
  taxRate: 18,
  items: [{ description: "Annual plan", quantity: 1, rate: 4999 }],
});

await ecod.request("POST", `/product/erix/commerce/invoices/${invoice.id}/payment-link`);
await ecod.product.erix.invoices.sendViaWhatsApp(invoice.id);

Platform

Realtime

The SDK keeps a socket open to your workspace, so you get changes as they happen instead of polling. After a reconnect, missed events are replayed; a resync event means “refetch”. Keep handlers idempotent.

In a browser, get a 5-minute socket ticket from your server via POST /v1/api/platform/realtime/ticket (see proxy mode under Authentication).

TypeScript SDK
ecod.on("inbox.message", (event) => {
  // a message arrived or was sent in the unified inbox
});

process.on("SIGTERM", () => ecod.disconnect());

Platform

Health

Check the API before a batch job, or watch it from your monitoring.

GET/v1/api/health

Machine-readable API status. No key needed.

GET/health

The public status page. Send Accept: application/json for JSON.

const status = await ecod.platform.health.system();