@ecodrix/erix-api 1.0.0

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.
@@ -0,0 +1,2 @@
1
+ var F=Object.defineProperty,B=Object.defineProperties;var _=Object.getOwnPropertyDescriptors;var N=Object.getOwnPropertySymbols;var V=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var j=(r,e)=>(e=Symbol[r])?e:Symbol.for("Symbol."+r);var U=(r,e,s)=>e in r?F(r,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):r[e]=s,u=(r,e)=>{for(var s in e||(e={}))V.call(e,s)&&U(r,s,e[s]);if(N)for(var s of N(e))W.call(e,s)&&U(r,s,e[s]);return r},y=(r,e)=>B(r,_(e));var c=(r,e,s)=>U(r,typeof e!="symbol"?e+"":e,s);var L=function(r,e){this[0]=r,this[1]=e},O=(r,e,s)=>{var a=(p,g,i,d)=>{try{var f=s[p](g),x=(g=f.value)instanceof L,K=f.done;Promise.resolve(x?g[0]:g).then(m=>x?a(p==="return"?p:"next",g[1]?{done:m.done,value:m.value}:m,i,d):i({value:m,done:K})).catch(m=>a("throw",m,i,d))}catch(m){d(m)}},t=p=>n[p]=g=>new Promise((i,d)=>a(p,g,i,d)),n={};return s=s.apply(r,e),n[j("asyncIterator")]=()=>n,t("next"),t("throw"),t("return"),n};import J from"axios";import M from"axios-retry";var q=class extends Error{constructor(e){super(e),this.name="EcodrixError"}},l=class extends q{constructor(s,a,t){super(s);c(this,"status");c(this,"code");this.name="APIError",this.status=a,this.code=t}},P=class extends l{constructor(e="Invalid API Key or Client Code"){super(e,401,"AUTH_FAILED"),this.name="AuthenticationError"}},$=class extends l{constructor(e="Too many requests. Please slow down."){super(e,429,"RATE_LIMIT_EXCEEDED"),this.name="RateLimitError"}};var o=class{constructor(e){this.client=e}async post(e,s,a){try{let t=this.buildConfig(a);return(await this.client.post(e,s,t)).data}catch(t){this.handleError(t)}}async get(e,s){try{let a=this.buildConfig(s);return(await this.client.get(e,a)).data}catch(a){this.handleError(a)}}async deleteRequest(e,s){try{let a=this.buildConfig(s);return(await this.client.delete(e,a)).data}catch(a){this.handleError(a)}}buildConfig(e){if(!e)return;let s=u({},e);return e.idempotencyKey&&(s.headers=y(u({},s.headers),{"Idempotency-Key":e.idempotencyKey})),s}handleError(e){var s,a,t;throw e.response?new l(((s=e.response.data)==null?void 0:s.message)||((a=e.response.data)==null?void 0:a.error)||"API Request Failed",e.response.status,(t=e.response.data)==null?void 0:t.code):new l(e.message||"Network Error")}};var w=class extends o{async send(e){return this.post("/api/saas/chat/send",e)}async sendTemplate(e){return this.post("/api/saas/chat/send",y(u({},e),{templateLanguage:e.language||"en_US"}))}async markRead(e){return this.post(`/api/saas/chat/conversations/${e}/read`)}};var v=class extends o{async list(e){return this.get("/api/saas/chat/conversations",{params:e})}async retrieve(e){return this.get(`/api/saas/chat/conversations/${e}`)}async messages(e,s){return this.get(`/api/saas/chat/conversations/${e}/messages`,{params:s})}async linkLead(e,s){return this.post(`/api/saas/chat/conversations/${e}/link-lead`,{leadId:s})}async delete(e){return this.deleteRequest(`/api/saas/chat/conversations/${e}`)}};var R=class extends o{constructor(s){super(s);c(this,"messages");c(this,"conversations");this.messages=new w(s),this.conversations=new v(s)}async sendTemplate(s){return this.post("/api/saas/whatsapp/send-template",s)}};var k=class extends o{async create(e){return this.post("/api/services/leads",e)}async createMany(e,s=50){let a=[];for(let t=0;t<e.length;t+=s){let p=e.slice(t,t+s).map(i=>this.create(i)),g=await Promise.allSettled(p);for(let i of g)if(i.status==="fulfilled")a.push(i.value);else throw i.reason}return a}async list(e){return this.get("/api/services/leads",{params:e})}listAutoPaging(e){return O(this,null,function*(){let s=(e==null?void 0:e.page)||1,a=!0;for(;a;){let t=yield new L(this.list(y(u({},e),{page:s}))),n=Array.isArray(t.data)?t.data:t||[];if(n.length===0){a=!1;break}for(let p of n)yield p;t.pagination&&s<t.pagination.pages||!t.pagination&&n.length>0?s++:a=!1}})}async retrieve(e){return this.get(`/api/services/leads/${e}`)}async update(e,s){return this.post(`/api/services/leads/${e}`,s)}async delete(e){return this.deleteRequest(`/api/services/leads/${e}`)}};var A=class{constructor(e){c(this,"leads");this.leads=new k(e)}};import H from"axios";var I=class extends o{async getUsage(){return this.get("/api/saas/storage/usage")}async createFolder(e){return this.post("/api/saas/storage/folders",{name:e})}async list(e,s){return this.get(`/api/saas/storage/files/${e}`,{params:s})}async delete(e){return this.deleteRequest("/api/saas/storage/files",{params:{key:e}})}async getDownloadUrl(e){return this.post("/api/saas/storage/download-url",{key:e})}async upload(e,s){let{data:a}=await this.client.post("/api/saas/storage/upload-url",s),{uploadUrl:t,key:n}=a;await H.put(t,e,{headers:{"Content-Type":s.contentType}});let p=e.size||e.byteLength||0;return this.post("/api/saas/storage/confirm-upload",{key:n,sizeBytes:p})}};var T=class extends o{async create(e){return this.post("/api/saas/meet",e)}async list(e){return this.get("/api/saas/meet",{params:e})}async retrieve(e){return this.get(`/api/saas/meet/${e}`)}async update(e,s){return this.client.patch(`/api/saas/meet/${e}`,s).then(a=>a.data)}async delete(e){return this.update(e,{status:"cancelled"})}};var C=class extends o{async listLogs(e){return this.get("/api/saas/events/logs",{params:e})}async retrieveLog(e){return this.get(`/api/saas/events/logs/${e}`)}async getStats(e){return this.get("/api/saas/events/stats",{params:e})}async listCallbacks(e){return this.get("/api/saas/callbacks/logs",{params:e})}};var E=class extends o{async sendEmailCampaign(e){return this.post("/api/saas/emails/campaign",e)}async sendTestEmail(e){return this.post("/api/saas/emails/test",{to:e})}};var D=class extends o{async list(){return this.get("/api/saas/events")}async assign(e){return this.post("/api/saas/events/assign",e)}async unassign(e){return this.post("/api/saas/events/unassign",{name:e})}async unassignBulk(e){return this.post("/api/saas/events/unassign/bulk",{names:e})}async trigger(e){return this.post("/api/saas/workflows/trigger",e)}};var h=class extends l{constructor(e){super(e,400,"invalid_signature"),this.name="WebhookSignatureError"}},S=class{async constructEvent(e,s,a){if(!s)throw new h("No webhook signature provided");let t=Array.isArray(s)?s[0]:s;t.startsWith("sha256=")&&(t=t.slice(7));try{let n=await import("crypto"),g=n.createHmac("sha256",a).update(e).digest("hex");if(!n.timingSafeEqual(Buffer.from(g),Buffer.from(t)))throw new h("Invalid webhook signature provided");return JSON.parse(e.toString("utf8"))}catch(n){throw n instanceof h?n:new h(`Webhook payload parsing failed: ${n.message}`)}}};import{io as z}from"socket.io-client";var b=class{constructor(e){c(this,"client");c(this,"socket");c(this,"whatsapp");c(this,"crm");c(this,"media");c(this,"meet");c(this,"notifications");c(this,"email");c(this,"events");c(this,"webhooks");var g,i,d;if(!e.apiKey)throw new P("API Key is required");let s=e.baseUrl||"https://api.ecodrix.com",a=e.socketUrl||s,t=typeof window!="undefined"&&typeof window.document!="undefined",n=t?"browser":typeof process!="undefined"?`node ${process.version}`:"unknown",p=t?((g=globalThis.navigator)==null?void 0:g.userAgent)||"browser":typeof process!="undefined"?process.platform:"unknown";this.client=J.create({baseURL:s,headers:{"x-api-key":e.apiKey,"x-client-code":(i=e.clientCode)==null?void 0:i.toUpperCase(),"Content-Type":"application/json","x-ecodrix-client-agent":JSON.stringify({sdk_version:"1.0.0",runtime:n,os:p})}}),M(this.client,{retries:3,retryDelay:M.exponentialDelay,retryCondition:f=>{var x;return M.isNetworkOrIdempotentRequestError(f)||((x=f.response)==null?void 0:x.status)===429}}),this.whatsapp=new R(this.client),this.crm=new A(this.client),this.media=new I(this.client),this.meet=new T(this.client),this.notifications=new C(this.client),this.email=new E(this.client),this.events=new D(this.client),this.webhooks=new S,this.socket=z(a,{extraHeaders:{"x-api-key":e.apiKey,"x-client-code":((d=e.clientCode)==null?void 0:d.toUpperCase())||""}}),this.setupSocket(e.clientCode)}setupSocket(e){this.socket.on("connect",()=>{e&&this.socket.emit("join-room",e.toUpperCase())})}on(e,s){return this.socket.on(e,s),this}disconnect(){this.socket.disconnect()}async request(e,s,a,t){var n,p,g;try{return(await this.client.request({method:e,url:s,data:a,params:t})).data}catch(i){throw i.response?new l(((n=i.response.data)==null?void 0:n.message)||((p=i.response.data)==null?void 0:p.error)||"Raw Execution Failed",i.response.status,(g=i.response.data)==null?void 0:g.code):new l(i.message||"Network Error")}}};var je=b;export{l as APIError,P as AuthenticationError,v as Conversations,b as Ecodrix,q as EcodrixError,E as EmailResource,D as EventsResource,k as Leads,I as MediaResource,T as Meetings,w as Messages,C as Notifications,$ as RateLimitError,h as WebhookSignatureError,S as Webhooks,R as WhatsApp,je as default};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core.ts","../../../src/error.ts","../../../src/resource.ts","../../../src/resources/whatsapp/messages.ts","../../../src/resources/whatsapp/conversations.ts","../../../src/resources/whatsapp/index.ts","../../../src/resources/crm/leads.ts","../../../src/resources/crm/index.ts","../../../src/resources/media.ts","../../../src/resources/meet.ts","../../../src/resources/notifications.ts","../../../src/resources/email.ts","../../../src/resources/events.ts","../../../src/resources/webhooks.ts","../../../src/index.ts"],"sourcesContent":["import axios, { type AxiosInstance, type Method } from \"axios\";\nimport axiosRetry from \"axios-retry\";\nimport { AuthenticationError, APIError } from \"./error\";\nimport { WhatsApp } from \"./resources/whatsapp\";\nimport { CRM } from \"./resources/crm\";\nimport { MediaResource } from \"./resources/media\";\nimport { Meetings } from \"./resources/meet\";\nimport { Notifications } from \"./resources/notifications\";\nimport { EmailResource } from \"./resources/email\";\nimport { EventsResource } from \"./resources/events\";\nimport { Webhooks } from \"./resources/webhooks\";\nimport { io, type Socket } from \"socket.io-client\";\n\ndeclare const process: any;\n\n/**\n * Configuration options for the Ecodrix client.\n */\nexport interface EcodrixOptions {\n /**\n * Your ECODrIx Platform API key.\n * Obtain this from the ECODrIx dashboard under Settings → API Keys.\n * @example \"ecod_live_sk_...\"\n */\n apiKey: string;\n\n /**\n * Your tenant ID (Client Code).\n * This scopes all API requests to your specific organisation.\n * Required for most operations.\n * @example \"ERIX_CLNT_JHBJHF\"\n */\n clientCode?: string;\n\n /**\n * Override the base URL of the ECODrIx API.\n * Useful for pointing to a local development or staging server.\n * @default \"https://api.ecodrix.com\"\n */\n baseUrl?: string;\n\n /**\n * Override the Socket.io server URL for real-time events.\n * Defaults to the same value as `baseUrl`.\n * @default \"https://api.ecodrix.com\"\n */\n socketUrl?: string;\n}\n\n/**\n * The primary entry point for the ECODrIx SDK.\n *\n * Initialise once with your credentials and use the namespaced resources\n * to interact with every part of the platform.\n *\n * @example\n * ```typescript\n * import { Ecodrix } from \"@ecodrix/erix-api\";\n *\n * const ecod = new Ecodrix({\n * apiKey: process.env.ECOD_API_KEY!,\n * clientCode: \"ERIX_CLNT_JHBJHF\",\n * });\n *\n * await ecod.whatsapp.messages.send({ to: \"+91...\", text: \"Hello!\" });\n * const lead = await ecod.crm.leads.create({ firstName: \"Alice\", phone: \"+91...\" });\n * ```\n */\nexport class Ecodrix {\n private readonly client: AxiosInstance;\n private readonly socket: Socket;\n\n /** WhatsApp messaging and conversation management. */\n public readonly whatsapp: WhatsApp;\n\n /** CRM resources — Leads and related sub-resources. */\n public readonly crm: CRM;\n\n /** Cloudflare R2-backed media storage. */\n public readonly media: MediaResource;\n\n /** Google Meet appointment scheduling. */\n public readonly meet: Meetings;\n\n /** Automation execution logs and provider webhook callbacks. */\n public readonly notifications: Notifications;\n\n /** Outbound email marketing engine. */\n public readonly email: EmailResource;\n\n /** Lead events and workflow automation triggers. */\n public readonly events: EventsResource;\n\n /** Cryptographic webhook signature verification. */\n public readonly webhooks: Webhooks;\n\n constructor(options: EcodrixOptions) {\n if (!options.apiKey) {\n throw new AuthenticationError(\"API Key is required\");\n }\n\n const baseUrl = options.baseUrl || \"https://api.ecodrix.com\";\n const socketUrl = options.socketUrl || baseUrl;\n\n const isBrowser =\n typeof window !== \"undefined\" && typeof window.document !== \"undefined\";\n const runtime = isBrowser\n ? \"browser\"\n : typeof process !== \"undefined\"\n ? `node ${process.version}`\n : \"unknown\";\n const os = isBrowser\n ? globalThis.navigator?.userAgent || \"browser\"\n : typeof process !== \"undefined\"\n ? process.platform\n : \"unknown\";\n\n this.client = axios.create({\n baseURL: baseUrl,\n headers: {\n \"x-api-key\": options.apiKey,\n \"x-client-code\": options.clientCode?.toUpperCase(),\n \"Content-Type\": \"application/json\",\n \"x-ecodrix-client-agent\": JSON.stringify({\n sdk_version: \"1.0.0\", // Can be auto-injected during build in future\n runtime,\n os,\n }),\n },\n });\n\n // Make the client completely bulletproof for execution from external projects.\n // It will automatically handle network blips, 502 Bad Gateways, and 429 Rate Limits.\n axiosRetry(this.client, {\n retries: 3,\n retryDelay: axiosRetry.exponentialDelay,\n retryCondition: (error) => {\n return (\n axiosRetry.isNetworkOrIdempotentRequestError(error) ||\n error.response?.status === 429\n );\n },\n });\n\n // Initialise resources\n this.whatsapp = new WhatsApp(this.client);\n this.crm = new CRM(this.client);\n this.media = new MediaResource(this.client);\n this.meet = new Meetings(this.client);\n this.notifications = new Notifications(this.client);\n this.email = new EmailResource(this.client);\n this.events = new EventsResource(this.client);\n this.webhooks = new Webhooks();\n\n // Establish persistent Socket.io connection\n this.socket = io(socketUrl, {\n extraHeaders: {\n \"x-api-key\": options.apiKey,\n \"x-client-code\": options.clientCode?.toUpperCase() || \"\",\n },\n });\n\n this.setupSocket(options.clientCode);\n }\n\n private setupSocket(clientCode?: string) {\n this.socket.on(\"connect\", () => {\n if (clientCode) {\n // Join the tenant-scoped room to receive only relevant events.\n this.socket.emit(\"join-room\", clientCode.toUpperCase());\n }\n });\n }\n\n /**\n * Subscribe to a real-time event emitted by the ECODrIx platform.\n *\n * The SDK maintains a persistent Socket.io connection. Events are\n * scoped to your `clientCode` — you will only receive events for\n * your own tenant.\n *\n * **Standard events:**\n * - `whatsapp.message_received` — inbound WhatsApp message\n * - `whatsapp.message_sent` — outbound message delivered\n * - `crm.lead_created` — new CRM lead ingested\n * - `crm.lead_updated` — lead details or stage changed\n * - `meet.scheduled` — Google Meet appointment booked\n * - `storage.upload_confirmed` — file upload completed\n * - `automation.failed` — automation execution error\n *\n * @param event - The event name to subscribe to.\n * @param callback - The handler function invoked when the event fires.\n * @returns `this` for method chaining.\n *\n * @example\n * ```typescript\n * ecod\n * .on(\"whatsapp.message_received\", (msg) => console.log(msg.body))\n * .on(\"automation.failed\", (err) => alertTeam(err));\n * ```\n */\n public on(event: string, callback: (...args: any[]) => void): this {\n this.socket.on(event, callback);\n return this;\n }\n\n /**\n * Gracefully disconnect the real-time Socket.io connection.\n * Call this when shutting down your server or when the client is\n * no longer needed to free up resources.\n *\n * @example\n * ```typescript\n * process.on(\"SIGTERM\", () => ecod.disconnect());\n * ```\n */\n public disconnect() {\n this.socket.disconnect();\n }\n\n /**\n * Raw Execution Escape-Hatch.\n * Send an authenticated HTTP request directly to the ECODrIx backend from ANY external project.\n *\n * This is extremely powerful giving you full unrestricted access to make calls\n * against new, experimental, or completely custom backend APIs while still benefitting\n * from the SDK's built-in authentication and automatic `axios-retry` logic.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.request(\"POST\", \"/api/saas/experimental-feature\", { flag: true });\n * console.log(data);\n * ```\n */\n public async request<T = any>(\n method: Method,\n path: string,\n data?: any,\n params?: any,\n ): Promise<T> {\n try {\n const response = await this.client.request<T>({\n method,\n url: path,\n data,\n params,\n });\n return response.data;\n } catch (error: any) {\n if (error.response) {\n throw new APIError(\n error.response.data?.message ||\n error.response.data?.error ||\n \"Raw Execution Failed\",\n error.response.status,\n error.response.data?.code,\n );\n }\n throw new APIError(error.message || \"Network Error\");\n }\n }\n}\n","/**\n * Typed error hierarchy for the @ecodrix/erix-api SDK.\n *\n * All errors thrown by the SDK extend `EcodrixError`, so a single\n * `catch` block can handle them all, while still allowing granular\n * differentiation between auth failures, rate limits, and generic API errors.\n *\n * @example\n * ```typescript\n * import { APIError, AuthenticationError, RateLimitError } from \"@ecodrix/erix-api\";\n *\n * try {\n * await ecod.crm.leads.retrieve(\"invalid_id\");\n * } catch (err) {\n * if (err instanceof AuthenticationError) {\n * console.error(\"Invalid API key or client code.\");\n * } else if (err instanceof RateLimitError) {\n * console.warn(\"Slow down — rate limit hit.\");\n * } else if (err instanceof APIError) {\n * console.error(`API error ${err.status}: ${err.message}`);\n * }\n * }\n * ```\n */\n\n/** Base class for all ECODrIx SDK errors. */\nexport class EcodrixError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EcodrixError\";\n }\n}\n\n/**\n * Represents an error returned by the ECODrIx API.\n * Carries the HTTP `status` code and an optional `code` string.\n */\nexport class APIError extends EcodrixError {\n /** HTTP status code returned by the API (e.g. 400, 404, 500). */\n public status?: number;\n /** Machine-readable error code (e.g. `\"NOT_FOUND\"`, `\"VALIDATION_ERROR\"`). */\n public code?: string;\n\n constructor(message: string, status?: number, code?: string) {\n super(message);\n this.name = \"APIError\";\n this.status = status;\n this.code = code;\n }\n}\n\n/**\n * Thrown when the API key or client code is missing, invalid, or expired.\n * HTTP 401.\n */\nexport class AuthenticationError extends APIError {\n constructor(message = \"Invalid API Key or Client Code\") {\n super(message, 401, \"AUTH_FAILED\");\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Thrown when the rate limit for the API key has been exceeded.\n * HTTP 429. Implement exponential backoff before retrying.\n */\nexport class RateLimitError extends APIError {\n constructor(message = \"Too many requests. Please slow down.\") {\n super(message, 429, \"RATE_LIMIT_EXCEEDED\");\n this.name = \"RateLimitError\";\n }\n}\n","import type { AxiosInstance, AxiosRequestConfig } from \"axios\";\nimport { APIError } from \"./error\";\n\nexport interface RequestOptions extends AxiosRequestConfig {\n /**\n * If true, will not add the x-client-code header.\n */\n ignoreClientCode?: boolean;\n\n /**\n * Safe execution idempotency key.\n * If provided, the backend will safely ignore duplicate requests retried with the same key.\n */\n idempotencyKey?: string;\n}\n\nexport abstract class APIResource {\n public constructor(protected readonly client: AxiosInstance) {}\n\n protected async post<T>(url: string, data?: any, options?: RequestOptions): Promise<T> {\n try {\n const config = this.buildConfig(options);\n const response = await this.client.post(url, data, config);\n return response.data;\n } catch (error: any) {\n this.handleError(error);\n }\n }\n\n protected async get<T>(url: string, options?: RequestOptions): Promise<T> {\n try {\n const config = this.buildConfig(options);\n const response = await this.client.get(url, config);\n return response.data;\n } catch (error: any) {\n this.handleError(error);\n }\n }\n\n protected async deleteRequest<T>(url: string, options?: RequestOptions): Promise<T> {\n try {\n const config = this.buildConfig(options);\n const response = await this.client.delete(url, config);\n return response.data;\n } catch (error: any) {\n this.handleError(error);\n }\n }\n\n private buildConfig(options?: RequestOptions): AxiosRequestConfig | undefined {\n if (!options) return undefined;\n\n const config: AxiosRequestConfig = { ...options };\n if (options.idempotencyKey) {\n config.headers = {\n ...config.headers,\n \"Idempotency-Key\": options.idempotencyKey,\n };\n }\n return config;\n }\n\n private handleError(error: any): never {\n if (error.response) {\n throw new APIError(\n error.response.data?.message || error.response.data?.error || \"API Request Failed\",\n error.response.status,\n error.response.data?.code\n );\n }\n throw new APIError(error.message || \"Network Error\");\n }\n}\n","import { APIResource } from \"../../resource\";\n\n/**\n * Parameters for sending a free-form WhatsApp message.\n */\nexport interface SendMessageParams {\n /**\n * Recipient's phone number in E.164 format.\n * @example \"+919876543210\"\n */\n to: string;\n /** Plain text body of the message. */\n text?: string;\n /** Public URL of the media asset to send. */\n mediaUrl?: string;\n /** MIME category of the media. */\n mediaType?: \"image\" | \"video\" | \"audio\" | \"document\";\n /** The `messageId` of the message to reply to (quoted replies). */\n replyToId?: string;\n /** Filename shown to the recipient (required for `document` type). */\n filename?: string;\n /** Arbitrary metadata stored with the message record. */\n metadata?: Record<string, any>;\n}\n\n/**\n * Parameters for sending a pre-approved WhatsApp Business template.\n */\nexport interface SendTemplateParams {\n /**\n * Recipient's phone number in E.164 format.\n * @example \"+919876543210\"\n */\n to: string;\n /** The exact template name as approved in Meta Business Manager. */\n templateName: string;\n /**\n * BCP-47 language code for the template.\n * @default \"en_US\"\n */\n language?: string;\n /**\n * Ordered array of variable substitutions for template placeholders.\n * @example [\"Alice\", \"10 April 2026\", \"10:00 AM\"]\n */\n variables?: string[];\n /** Optional header media URL (for templates with media headers). */\n mediaUrl?: string;\n /** Media type for the header (e.g. \"image\", \"document\"). */\n mediaType?: string;\n}\n\n/**\n * WhatsApp outbound messaging resource.\n *\n * Access via `ecod.whatsapp.messages`.\n *\n * @example\n * ```typescript\n * await ecod.whatsapp.messages.send({\n * to: \"+919876543210\",\n * text: \"Your appointment is confirmed!\",\n * });\n * ```\n */\nexport class Messages extends APIResource {\n /**\n * Send a free-text or media message to a WhatsApp number.\n *\n * For text-only messages, supply `text`. For media, supply `mediaUrl`\n * and `mediaType`. Both can be combined.\n *\n * @param params - Message parameters.\n * @returns The created message record.\n *\n * @example Text message\n * ```typescript\n * await ecod.whatsapp.messages.send({\n * to: \"+919876543210\",\n * text: \"Hello!\",\n * });\n * ```\n *\n * @example Media message\n * ```typescript\n * await ecod.whatsapp.messages.send({\n * to: \"+919876543210\",\n * mediaUrl: \"https://cdn.ecodrix.com/invoice.pdf\",\n * mediaType: \"document\",\n * filename: \"invoice.pdf\",\n * });\n * ```\n */\n async send(params: SendMessageParams) {\n return this.post(\"/api/saas/chat/send\", params);\n }\n\n /**\n * Send a pre-approved WhatsApp Business template message.\n *\n * Templates must be approved in Meta Business Manager before use.\n * Variable placeholders in the template body are filled left-to-right\n * from the `variables` array.\n *\n * @param params - Template message parameters.\n * @returns The created message record.\n *\n * @example\n * ```typescript\n * await ecod.whatsapp.messages.sendTemplate({\n * to: \"+919876543210\",\n * templateName: \"appointment_reminder\",\n * language: \"en_US\",\n * variables: [\"Alice\", \"10 April\", \"10:00 AM\"],\n * });\n * ```\n */\n async sendTemplate(params: SendTemplateParams) {\n return this.post(\"/api/saas/chat/send\", {\n ...params,\n templateLanguage: params.language || \"en_US\",\n });\n }\n\n /**\n * Mark all messages in a conversation as read (double-tick).\n *\n * @param conversationId - The conversation to mark as read.\n *\n * @example\n * ```typescript\n * await ecod.whatsapp.messages.markRead(\"conv_64abc...\");\n * ```\n */\n async markRead(conversationId: string) {\n return this.post(`/api/saas/chat/conversations/${conversationId}/read`);\n }\n}\n","import { APIResource } from \"../../resource\";\n\nexport interface ListParams {\n limit?: number;\n before?: string;\n}\n\nexport class Conversations extends APIResource {\n /**\n * List conversations for the tenant.\n */\n async list(params?: ListParams): Promise<any> {\n return this.get(\"/api/saas/chat/conversations\", { params } as any);\n }\n\n /**\n * Retrieve details of a specific conversation.\n */\n async retrieve(conversationId: string): Promise<any> {\n return this.get(`/api/saas/chat/conversations/${conversationId}`);\n }\n\n /**\n * Get messages for a specific conversation.\n */\n async messages(conversationId: string, params?: ListParams): Promise<any> {\n return this.get(`/api/saas/chat/conversations/${conversationId}/messages`, { params } as any);\n }\n\n /**\n * Link a conversation to a lead.\n */\n async linkLead(conversationId: string, leadId: string) {\n return this.post(`/api/saas/chat/conversations/${conversationId}/link-lead`, { leadId });\n }\n\n /**\n * Delete a conversation.\n */\n async delete(conversationId: string): Promise<any> {\n return this.deleteRequest(`/api/saas/chat/conversations/${conversationId}`);\n }\n}\n","import { APIResource } from \"../../resource\";\nimport { Messages } from \"./messages\";\nimport { Conversations } from \"./conversations\";\nimport type { AxiosInstance } from \"axios\";\n\nexport interface SendTemplatePayload {\n /** Phone number in E.164 format. */\n phone: string;\n /** Name of the pre-approved WhatsApp template. */\n templateName: string;\n /** Language code (defaults to \"en\"). */\n languageCode?: string;\n /** Key-value pairs matching variables in your template (e.g., {{1}}, {{2}}). */\n variables?: Record<string, string>;\n /** Explicitly resolved variables if bypassing the internal resolution engine. */\n resolvedVariables?: any[];\n}\n\nexport class WhatsApp extends APIResource {\n public messages: Messages;\n public conversations: Conversations;\n\n constructor(client: AxiosInstance) {\n super(client);\n this.messages = new Messages(client);\n this.conversations = new Conversations(client);\n }\n\n /**\n * Dispatch a WhatsApp template message directly to a specific phone number.\n * Bypasses the automation queue for immediate high-priority delivery.\n *\n * @param payload - The template dispatch payload.\n * @returns Information about the dispatched message.\n */\n async sendTemplate(payload: SendTemplatePayload): Promise<{\n success: boolean;\n messageId?: string;\n templateName?: string;\n resolvedVariables?: any[];\n }> {\n return this.post<{\n success: boolean;\n messageId?: string;\n templateName?: string;\n resolvedVariables?: any[];\n }>(\"/api/saas/whatsapp/send-template\", payload);\n }\n}\n","import { APIResource } from \"../../resource\";\n\n/**\n * Parameters for creating a new CRM Lead.\n */\nexport interface CreateLeadParams {\n /** Lead's first name. Required. */\n firstName: string;\n /** Lead's last name. */\n lastName?: string;\n /** Lead's email address. */\n email?: string;\n /** Lead's phone number in E.164 format (e.g. \"+919876543210\"). */\n phone?: string;\n /**\n * Acquisition channel.\n * @example \"website\" | \"whatsapp\" | \"direct\" | \"referral\"\n */\n source?: string;\n /** Arbitrary key-value metadata (UTM params, order IDs, etc.). */\n metadata?: Record<string, any>;\n}\n\n/**\n * CRM Lead resource — full lifecycle management.\n *\n * Access via `ecod.crm.leads`.\n *\n * @example\n * ```typescript\n * const { data: lead } = await ecod.crm.leads.create({\n * firstName: \"Alice\",\n * phone: \"+919876543210\",\n * source: \"website\",\n * });\n * ```\n */\nexport class Leads extends APIResource {\n /**\n * Create a new lead in the CRM pipeline.\n *\n * @param params - Lead creation parameters. `firstName` is required.\n * @returns The newly created Lead document.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.crm.leads.create({\n * firstName: \"Alice\",\n * phone: \"+919876543210\",\n * });\n * ```\n */\n async create<T = any>(params: CreateLeadParams) {\n return this.post<T>(\"/api/services/leads\", params);\n }\n\n /**\n * Bulk ingest leads efficiently in parallel using automatic chunking.\n * Prevents rate limit exhaustion by executing `chunkSize` requests at a time.\n *\n * @param leads - Array of leads to create.\n * @param chunkSize - Number of leads to send concurrently (default: 50)\n * @returns Array of created lead results.\n */\n async createMany(leads: CreateLeadParams[], chunkSize = 50): Promise<any[]> {\n const results: any[] = [];\n \n for (let i = 0; i < leads.length; i += chunkSize) {\n const chunk = leads.slice(i, i + chunkSize);\n \n const chunkPromises = chunk.map((lead) => this.create(lead));\n const chunkResults = await Promise.allSettled(chunkPromises);\n \n for (const res of chunkResults) {\n if (res.status === \"fulfilled\") {\n results.push(res.value);\n } else {\n throw res.reason;\n }\n }\n }\n\n return results;\n }\n\n /**\n * List leads with optional filtering and pagination.\n *\n * @param params - Filter options (status, source, pipelineId, page, limit, etc.)\n * @returns Paginated list of Lead documents.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.crm.leads.list({ status: \"new\", limit: 25 });\n * ```\n */\n async list<T = any>(params?: Record<string, any>) {\n return this.get<T>(\"/api/services/leads\", { params } as any);\n }\n\n /**\n * Auto-paginating iterator for leads.\n * Seamlessly fetches leads page by page as you iterate.\n *\n * @example\n * ```typescript\n * for await (const lead of ecod.crm.leads.listAutoPaging({ status: \"won\" })) {\n * console.log(lead.firstName);\n * }\n * ```\n */\n async *listAutoPaging(params?: Record<string, any>): AsyncGenerator<any, void, unknown> {\n let currentPage = params?.page || 1;\n let hasMore = true;\n\n while (hasMore) {\n const response: any = await this.list({ ...params, page: currentPage });\n \n const items = Array.isArray(response.data) ? response.data : response || [];\n \n if (items.length === 0) {\n hasMore = false;\n break;\n }\n\n for (const item of items) {\n yield item;\n }\n\n if (response.pagination && currentPage < response.pagination.pages) {\n currentPage++;\n } else if (!response.pagination && items.length > 0) {\n // Fallback: If no explicit pagination struct, assume simple array and just keep pulling until empty\n currentPage++;\n } else {\n hasMore = false;\n }\n }\n }\n\n /**\n * Retrieve a single lead by its unique ID.\n *\n * @param leadId - The MongoDB ObjectId of the lead.\n * @returns The Lead document, or a 404 error if not found.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.crm.leads.retrieve(\"64abc...\");\n * ```\n */\n async retrieve(leadId: string) {\n return this.get(`/api/services/leads/${leadId}`);\n }\n\n /**\n * Update the fields of an existing lead.\n *\n * @param leadId - The ID of the lead to update.\n * @param params - Partial lead fields to update.\n * @returns The updated Lead document.\n *\n * @example\n * ```typescript\n * await ecod.crm.leads.update(\"64abc...\", { email: \"new@email.com\" });\n * ```\n */\n async update(leadId: string, params: Partial<CreateLeadParams>) {\n return this.post(`/api/services/leads/${leadId}`, params);\n }\n\n /**\n * Archive (soft-delete) a lead. The record is retained in the database\n * for audit purposes but is excluded from all standard list views.\n *\n * @param leadId - The ID of the lead to archive.\n *\n * @example\n * ```typescript\n * await ecod.crm.leads.delete(\"64abc...\");\n * ```\n */\n async delete(leadId: string) {\n return this.deleteRequest(`/api/services/leads/${leadId}`);\n }\n}\n","import { Leads } from \"./leads\";\nimport type { AxiosInstance } from \"axios\";\n\nexport class CRM {\n public leads: Leads;\n\n constructor(client: AxiosInstance) {\n this.leads = new Leads(client);\n }\n}\n","import axios from \"axios\";\nimport { APIResource } from \"../resource\";\n\n/**\n * Options for the `upload()` elite helper.\n */\nexport interface UploadOptions {\n /**\n * The destination folder key in R2.\n * @example \"customer_documents\" | \"avatars\" | \"invoices\"\n */\n folder: string;\n /**\n * The desired filename, including extension.\n * @example \"contract.pdf\" | \"profile.jpg\"\n */\n filename: string;\n /**\n * The MIME type of the file.\n * @example \"application/pdf\" | \"image/jpeg\" | \"video/mp4\"\n */\n contentType: string;\n}\n\n/**\n * Cloudflare R2-backed media resource.\n *\n * Access via `ecod.media`.\n *\n * The `upload()` method is the recommended approach. It acts as a client-side\n * orchestrator: it fetches a presigned URL from the backend, uploads directly\n * to R2 (bypassing the API server), then confirms the upload — all in one call.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.media.upload(fileBuffer, {\n * folder: \"invoices\",\n * filename: \"invoice-001.pdf\",\n * contentType: \"application/pdf\",\n * });\n * console.log(data.url); // → https://cdn.ecodrix.com/invoices/invoice-001.pdf\n * ```\n */\nexport class MediaResource extends APIResource {\n /**\n * Get current storage usage metrics for the tenant.\n *\n * @returns `{ usedMB, limitMB, fileCount }`\n *\n * @example\n * ```typescript\n * const { data } = await ecod.media.getUsage();\n * console.log(`${data.usedMB} / ${data.limitMB} MB used`);\n * ```\n */\n async getUsage() {\n return this.get(\"/api/saas/storage/usage\");\n }\n\n /**\n * Create a new top-level folder in the tenant's R2 bucket.\n *\n * @param name - Folder name (alphanumeric, hyphens, underscores).\n *\n * @example\n * ```typescript\n * await ecod.media.createFolder(\"patient_records\");\n * ```\n */\n async createFolder(name: string) {\n return this.post(\"/api/saas/storage/folders\", { name });\n }\n\n /**\n * List files within a specific folder, with optional date filtering.\n *\n * @param folder - The folder key to list.\n * @param params - Optional `year` and `month` filters (e.g. `{ year: \"2026\", month: \"04\" }`).\n * @returns Array of file metadata objects.\n *\n * @example\n * ```typescript\n * const { data: files } = await ecod.media.list(\"invoices\", { year: \"2026\" });\n * ```\n */\n async list(folder: string, params?: { year?: string; month?: string }) {\n return this.get(`/api/saas/storage/files/${folder}`, { params } as any);\n }\n\n /**\n * Delete a file from R2 by its storage key.\n *\n * @param key - The full storage key of the file (e.g. `\"invoices/contract.pdf\"`).\n *\n * @example\n * ```typescript\n * await ecod.media.delete(\"invoices/old-contract.pdf\");\n * ```\n */\n async delete(key: string) {\n return this.deleteRequest(\"/api/saas/storage/files\", { params: { key } });\n }\n\n /**\n * Request a temporary presigned download URL for a private file.\n *\n * @param key - The full storage key of the file.\n * @returns `{ url }` — a time-limited download URL.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.media.getDownloadUrl(\"invoices/contract.pdf\");\n * // → { url: \"https://cdn.ecodrix.com/...?token=...\" }\n * ```\n */\n async getDownloadUrl(key: string) {\n return this.post(\"/api/saas/storage/download-url\", { key });\n }\n\n /**\n * **Elite Upload Helper** — uploads a file to Cloudflare R2 in a single call.\n *\n * This orchestrates 3 steps transparently:\n * 1. Fetch a presigned `PUT` URL from the ECODrIx backend.\n * 2. Upload the file _directly_ to R2 (no API proxy, maximum throughput).\n * 3. Confirm the upload with the backend so it is indexed and tracked.\n *\n * Works with Node.js `Buffer`, browser `File`/`Blob`, or any stream-like\n * object that Axios can PUT.\n *\n * @param file - The file data to upload.\n * @param options - Folder, filename, and content type.\n * @returns Confirmed file metadata including `key` and `url`.\n *\n * @example Node.js\n * ```typescript\n * import { readFileSync } from \"fs\";\n * const buf = readFileSync(\"./invoice.pdf\");\n * const { data } = await ecod.media.upload(buf, {\n * folder: \"invoices\",\n * filename: \"invoice-001.pdf\",\n * contentType: \"application/pdf\",\n * });\n * ```\n *\n * @example Browser\n * ```typescript\n * const file = inputElement.files[0];\n * const { data } = await ecod.media.upload(file, {\n * folder: \"avatars\",\n * filename: file.name,\n * contentType: file.type,\n * });\n * ```\n */\n async upload(file: any, options: UploadOptions): Promise<any> {\n // Step 1: Get presigned URL\n const { data: presignedData } = await this.client.post(\n \"/api/saas/storage/upload-url\",\n options,\n );\n const { uploadUrl, key } = presignedData;\n\n // Step 2: Upload directly to R2 (bypasses the API server for performance)\n await axios.put(uploadUrl, file, {\n headers: { \"Content-Type\": options.contentType },\n });\n\n const sizeBytes = file.size || file.byteLength || 0;\n\n // Step 3: Confirm with backend\n return this.post(\"/api/saas/storage/confirm-upload\", { key, sizeBytes });\n }\n}\n","import { APIResource } from \"../resource\";\n\n/**\n * Parameters for scheduling a new Google Meet appointment.\n */\nexport interface CreateMeetingParams {\n /** The CRM Lead ID this meeting belongs to. */\n leadId: string;\n /** Full name of the external participant. */\n participantName: string;\n /** Phone number of the participant in E.164 format. */\n participantPhone: string;\n /**\n * Meeting start time.\n * @example new Date(\"2026-04-10T10:00:00.000Z\")\n */\n startTime: Date | string;\n /**\n * Meeting end time.\n * @example new Date(\"2026-04-10T10:30:00.000Z\")\n */\n endTime: Date | string;\n /** Arbitrary metadata to attach to the meeting record. */\n metadata?: Record<string, any>;\n}\n\n/**\n * Parameters for updating an existing meeting.\n */\nexport interface UpdateMeetingParams {\n /**\n * Update the meeting status.\n * @example \"scheduled\" | \"completed\" | \"cancelled\"\n */\n status?: string;\n /** Update the payment status for paid consultations. */\n paymentStatus?: string;\n /** New start time for rescheduling. */\n startTime?: Date | string;\n /** New end time for rescheduling. */\n endTime?: Date | string;\n /** Duration in minutes. */\n duration?: number;\n}\n\n/**\n * Google Meet appointment scheduling resource.\n *\n * Access via `ecod.meet`.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.meet.create({\n * leadId: \"64abc...\",\n * participantName: \"Alice\",\n * participantPhone: \"+919876543210\",\n * startTime: \"2026-04-10T10:00:00Z\",\n * endTime: \"2026-04-10T10:30:00Z\",\n * });\n * console.log(data.meetLink); // → https://meet.google.com/abc-defg-hij\n * ```\n */\nexport class Meetings extends APIResource {\n /**\n * Schedule a new Google Meet with a lead.\n *\n * The backend automatically creates a Google Calendar event and generates\n * a Meet link, then sends confirmation notifications via WhatsApp.\n *\n * @param data - Meeting details.\n * @returns The created Meeting document including `meetLink`.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.meet.create({\n * leadId: \"64abc...\",\n * participantName: \"Alice\",\n * participantPhone: \"+91...\",\n * startTime: \"2026-04-10T10:00:00Z\",\n * endTime: \"2026-04-10T10:30:00Z\",\n * });\n * ```\n */\n async create(data: CreateMeetingParams): Promise<any> {\n return this.post(\"/api/saas/meet\", data);\n }\n\n /**\n * List all meetings for the tenant, with optional filters.\n *\n * @param params - `leadId` to filter by lead; `status` to filter by state.\n * @returns Array of Meeting documents.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.meet.list({ status: \"scheduled\" });\n * ```\n */\n async list(params?: { leadId?: string; status?: string }): Promise<any> {\n return this.get(\"/api/saas/meet\", { params } as any);\n }\n\n /**\n * Retrieve the full details for a specific meeting.\n *\n * @param meetingId - The unique meeting ID.\n * @returns The Meeting document.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.meet.retrieve(\"meeting_id\");\n * ```\n */\n async retrieve(meetingId: string): Promise<any> {\n return this.get(`/api/saas/meet/${meetingId}`);\n }\n\n /**\n * Update or reschedule an existing meeting.\n *\n * Partial updates are supported — only supply the fields you wish to change.\n *\n * @param meetingId - The meeting to update.\n * @param data - Fields to update.\n * @returns The updated Meeting document.\n *\n * @example\n * ```typescript\n * // Reschedule\n * await ecod.meet.update(\"meeting_id\", {\n * startTime: \"2026-04-11T11:00:00Z\",\n * endTime: \"2026-04-11T11:30:00Z\",\n * });\n * ```\n */\n async update(meetingId: string, data: UpdateMeetingParams): Promise<any> {\n return this.client.patch(`/api/saas/meet/${meetingId}`, data).then((res) => res.data);\n }\n\n /**\n * Cancel a meeting. This sets the meeting status to `\"cancelled\"`.\n *\n * @param meetingId - The meeting to cancel.\n *\n * @example\n * ```typescript\n * await ecod.meet.delete(\"meeting_id\");\n * ```\n */\n async delete(meetingId: string): Promise<any> {\n return this.update(meetingId, { status: \"cancelled\" });\n }\n}\n","import { APIResource } from \"../resource\";\n\n/**\n * Filters for querying event and automation execution logs.\n */\nexport interface LogFilter {\n /** Page number for pagination (1-indexed). */\n page?: number;\n /** Maximum number of records to return per page. */\n limit?: number;\n /**\n * Filter by automation trigger name.\n * @example \"lead_created\" | \"appointment_booked\"\n */\n trigger?: string;\n /**\n * Filter by execution status.\n * @example \"success\" | \"failed\" | \"pending\"\n */\n status?: string;\n /** Filter logs associated with a specific phone number. */\n phone?: string;\n /** Start of the date range (inclusive). ISO 8601 or Date object. */\n startDate?: string | Date;\n /** End of the date range (inclusive). ISO 8601 or Date object. */\n endDate?: string | Date;\n}\n\n/**\n * Automation event log and provider callback resource.\n *\n * Access via `ecod.notifications`.\n *\n * This resource is **read-only**. It exposes platform audit trails — useful\n * for debugging automation failures, monitoring webhook callbacks, and\n * building internal ops dashboards.\n *\n * @example\n * ```typescript\n * // Find failed automations in April\n * const { data } = await ecod.notifications.listLogs({\n * status: \"failed\",\n * startDate: \"2026-04-01\",\n * endDate: \"2026-04-30\",\n * });\n * ```\n */\nexport class Notifications extends APIResource {\n /**\n * List automation execution logs with optional filtering.\n *\n * Each log entry represents one automation run triggered by a platform event.\n * Filter by `status: \"failed\"` to build a failure alerting pipeline.\n *\n * @param params - Optional filter criteria.\n * @returns Paginated list of event log objects.\n *\n * @example\n * ```typescript\n * const { data: failed } = await ecod.notifications.listLogs({\n * status: \"failed\",\n * trigger: \"lead_created\",\n * limit: 50,\n * });\n * ```\n */\n async listLogs(params?: LogFilter) {\n return this.get(\"/api/saas/events/logs\", { params } as any);\n }\n\n /**\n * Retrieve the full details for a single automation event log entry.\n *\n * @param logId - The unique log ID.\n * @returns A single event log document including the payload and error trace.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.notifications.retrieveLog(\"log_64abc...\");\n * console.log(data.error); // → \"Provider timeout after 30s\"\n * ```\n */\n async retrieveLog(logId: string) {\n return this.get(`/api/saas/events/logs/${logId}`);\n }\n\n /**\n * Get a summary statistics object for automation event execution.\n *\n * @param params - Optional date range for the summary window.\n * @returns `{ total, success, failed, pending }` counts.\n *\n * @example\n * ```typescript\n * const { data: stats } = await ecod.notifications.getStats({\n * startDate: \"2026-04-01\",\n * endDate: \"2026-04-30\",\n * });\n * console.log(`Failed: ${stats.failed} / ${stats.total}`);\n * ```\n */\n async getStats(params?: { startDate?: string; endDate?: string }) {\n return this.get(\"/api/saas/events/stats\", { params } as any);\n }\n\n /**\n * List external provider webhook callback logs.\n *\n * These are inbound HTTP callbacks from third-party providers\n * (payment gateways, email services, etc.) stored for audit purposes.\n *\n * @param params - Pagination and date filter options.\n * @returns Paginated list of callback log records.\n *\n * @example\n * ```typescript\n * const { data } = await ecod.notifications.listCallbacks({ limit: 20 });\n * ```\n */\n async listCallbacks(params?: Omit<LogFilter, \"trigger\" | \"phone\">) {\n return this.get(\"/api/saas/callbacks/logs\", { params } as any);\n }\n}\n","import type { AxiosInstance } from \"axios\";\nimport { APIResource } from \"../resource\";\nimport { APIError } from \"../error\";\n\n/**\n * Payload to send a high-throughput email campaign.\n */\nexport interface SendCampaignPayload {\n /** Array of recipient email addresses. */\n recipients: string[];\n /** Subject line of the email. */\n subject: string;\n /** HTML body of the email. */\n html: string;\n}\n\n\n/**\n * Interface representing the result of a campaign dispatch.\n */\nexport interface CampaignResult {\n success: boolean;\n message?: string;\n [key: string]: any;\n}\n\nexport class EmailResource extends APIResource {\n /**\n * Send an HTML email campaign to a list of recipients.\n *\n * @param payload - The campaign details (recipients, subject, html).\n * @returns The dispatch result.\n */\n async sendEmailCampaign(\n payload: SendCampaignPayload,\n ): Promise<CampaignResult> {\n return this.post<CampaignResult>(\"/api/saas/emails/campaign\", payload);\n }\n\n /**\n * Send a system verification/test email to validate SMTP configuration.\n *\n * @param to - The recipient's email address.\n * @returns The dispatch result.\n */\n async sendTestEmail(to: string): Promise<CampaignResult> {\n return this.post<CampaignResult>(\"/api/saas/emails/test\", { to });\n }\n\n // WhatsApp moved to native wrapper\n}\n","import type { AxiosInstance } from \"axios\";\nimport { APIResource } from \"../resource\";\n\n/**\n * Event definition representing an entry point capable of triggering automations.\n */\nexport interface EventDefinition {\n name: string;\n displayName: string;\n description?: string;\n pipelineId?: string;\n stageId?: string;\n defaultSource?: string;\n [key: string]: any;\n}\n\n/**\n * Payload to register/assign a new custom event definition.\n */\nexport interface AssignEventPayload {\n /** The internal machine-readable name of the event (e.g. \"webinar_joined\") */\n name: string;\n /** Human-readable display name */\n displayName: string;\n /** Event description */\n description?: string;\n /** ID of the pipeline to associate with matching leads */\n pipelineId?: string;\n /** ID of the stage within the pipeline */\n stageId?: string;\n /** Default source tag for leads created via this event */\n defaultSource?: string;\n}\n\n/**\n * Payload to programmatically trigger an event/workflow.\n */\nexport interface TriggerPayload {\n /** The name of the event to fire (e.g., \"webinar_joined\") */\n trigger: string;\n /** Phone number of the lead (E.164 format) */\n phone: string;\n /** Email of the lead */\n email?: string;\n /** Key-value pairs for string templates */\n variables?: Record<string, string>;\n /** Deep payload data mapping to the event */\n data?: any;\n /** URL to receive an acknowledgment callback when processing completes */\n callbackUrl?: string;\n /** Secret metadata passed back to the callback URL */\n callbackMetadata?: any;\n /** Auto-create lead if phone does not exist in CRM */\n createLeadIfMissing?: boolean;\n /** Pre-fill data if creating a new lead */\n leadData?: {\n firstName?: string;\n lastName?: string;\n source?: string;\n };\n /** Delay execution of matched workflows (in seconds) */\n delaySeconds?: number;\n /** Delay execution of matched workflows (in minutes) */\n delayMinutes?: number;\n /** Explicit ISO timestamp to trigger the workflow run */\n runAt?: string;\n}\n\n/**\n * Response returned when triggering an event.\n */\nexport interface TriggerResponse {\n success: boolean;\n data?: {\n eventLogId: string;\n trigger: string;\n leadId: string;\n rulesMatched: number;\n };\n message?: string;\n code?: string;\n}\n\nexport class EventsResource extends APIResource {\n /**\n * Retrieve all valid events (system + custom) that can be used as Automation Rule triggers.\n */\n async list(): Promise<{ success: boolean; data: EventDefinition[] }> {\n return this.get<{ success: boolean; data: EventDefinition[] }>(\"/api/saas/events\");\n }\n\n /**\n * Register a new custom event entry point into the CRM automation engine.\n * Useful when introducing new granular triggers.\n */\n async assign(payload: AssignEventPayload): Promise<{ success: boolean; data: EventDefinition }> {\n return this.post<{ success: boolean; data: EventDefinition }>(\"/api/saas/events/assign\", payload);\n }\n\n /**\n * Deactivate a custom event assignment by name.\n */\n async unassign(name: string): Promise<{ success: boolean; data: any }> {\n return this.post<{ success: boolean; data: any }>(\"/api/saas/events/unassign\", { name });\n }\n\n /**\n * Deactivate multiple custom event assignments simultaneously.\n */\n async unassignBulk(names: string[]): Promise<{ success: boolean; message: string }> {\n return this.post<{ success: boolean; message: string }>(\"/api/saas/events/unassign/bulk\", { names });\n }\n\n /**\n * Programmatically fire an event into the CRM automation engine.\n * Emits to the internal EventBus to match with active Workflow Automation Rules.\n */\n async trigger(payload: TriggerPayload): Promise<TriggerResponse> {\n return this.post<TriggerResponse>(\"/api/saas/workflows/trigger\", payload);\n }\n}\n","import { APIError } from \"../error\";\n\nexport class WebhookSignatureError extends APIError {\n constructor(message: string) {\n super(message, 400, \"invalid_signature\");\n this.name = \"WebhookSignatureError\";\n }\n}\n\n/**\n * Validates and constructs webhook events sent by the ECODrIx platform.\n * Ensures the payload has not been tampered with.\n *\n * @example\n * ```typescript\n * import { ecod } from \"./services/ecodrix\";\n *\n * app.post(\"/api/webhooks\", async (req, res) => {\n * try {\n * const event = await ecod.webhooks.constructEvent(\n * req.rawBody,\n * req.headers[\"x-ecodrix-signature\"],\n * process.env.ECOD_WEBHOOK_SECRET\n * );\n * console.log(\"Verified event:\", event.type);\n * res.send({ received: true });\n * } catch (err) {\n * res.status(400).send(`Webhook Error: ${err.message}`);\n * }\n * });\n * ```\n */\nexport class Webhooks {\n /**\n * Cryptographically validates a webhook payload.\n * Note: This method dynamically imports Node's `crypto` module, meaning it will only execute gracefully in a Server environment.\n *\n * @param payload - The raw request body as a string or Buffer.\n * @param signature - The `x-ecodrix-signature` header value.\n * @param secret - The webhook signing secret for your tenant.\n * @returns The parsed JSON object of the event if the signature is valid.\n * @throws WebhookSignatureError if the validation fails.\n */\n public async constructEvent(\n payload: string | Buffer,\n signature: string | string[] | undefined,\n secret: string\n ): Promise<any> {\n if (!signature) {\n throw new WebhookSignatureError(\"No webhook signature provided\");\n }\n\n let sig = Array.isArray(signature) ? signature[0] : signature;\n if (sig.startsWith(\"sha256=\")) {\n sig = sig.slice(7);\n }\n\n try {\n // Dynamic import ensures Isomorphic bundles (like Vite UI) don't crash on import,\n // and only throw if someone incorrectly tries to verify a webhook in the browser.\n const crypto = await import(\"node:crypto\");\n\n const hmac = crypto.createHmac(\"sha256\", secret);\n const digest = hmac.update(payload).digest(\"hex\");\n\n const isValid = crypto.timingSafeEqual(\n Buffer.from(digest),\n Buffer.from(sig)\n );\n\n if (!isValid) {\n throw new WebhookSignatureError(\"Invalid webhook signature provided\");\n }\n\n return JSON.parse(payload.toString(\"utf8\"));\n } catch (error: any) {\n if (error instanceof WebhookSignatureError) {\n throw error;\n }\n throw new WebhookSignatureError(\n `Webhook payload parsing failed: ${error.message}`\n );\n }\n }\n}\n","export { Ecodrix, type EcodrixOptions } from \"./core\";\nexport * from \"./error\";\n\n// Resource Type Exports\nexport * from \"./resources/whatsapp/messages\";\nexport * from \"./resources/whatsapp/conversations\";\nexport * from \"./resources/whatsapp/index\";\nexport * from \"./resources/crm/leads\";\nexport * from \"./resources/meet\";\nexport * from \"./resources/media\";\nexport * from \"./resources/notifications\";\nexport * from \"./resources/email\";\nexport * from \"./resources/events\";\nexport * from \"./resources/webhooks\";\n\n// Export the main client also as default for better ergonomics\nimport { Ecodrix } from \"./core\";\nexport default Ecodrix;\n"],"mappings":"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","names":["axios","axiosRetry","EcodrixError","message","APIError","status","code","__publicField","AuthenticationError","RateLimitError","APIResource","client","url","data","options","config","error","__spreadValues","__spreadProps","_a","_b","_c","APIError","Messages","APIResource","params","__spreadProps","__spreadValues","conversationId","Conversations","APIResource","params","conversationId","leadId","WhatsApp","APIResource","client","__publicField","Messages","Conversations","payload","Leads","APIResource","params","leads","chunkSize","results","i","chunkPromises","lead","chunkResults","res","__asyncGenerator","currentPage","hasMore","response","__await","__spreadProps","__spreadValues","items","item","leadId","CRM","client","__publicField","Leads","axios","MediaResource","APIResource","name","folder","params","key","file","options","presignedData","uploadUrl","axios","sizeBytes","Meetings","APIResource","data","params","meetingId","res","Notifications","APIResource","params","logId","EmailResource","APIResource","payload","to","EventsResource","APIResource","payload","name","names","WebhookSignatureError","APIError","message","Webhooks","payload","signature","secret","sig","crypto","digest","error","io","Ecodrix","options","__publicField","_a","_b","_c","AuthenticationError","baseUrl","socketUrl","isBrowser","runtime","os","axios","axiosRetry","error","WhatsApp","CRM","MediaResource","Meetings","Notifications","EmailResource","EventsResource","Webhooks","io","clientCode","event","callback","method","path","data","params","APIError","index_default","Ecodrix"]}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@ecodrix/erix-api",
3
+ "version": "1.0.0",
4
+ "author": "ECODrIx Team <contact@ecodrix.com>",
5
+ "license": "MIT",
6
+ "description": "Official Isomorphic SDK for the ECODrIx platform. Native support for WhatsApp, CRM, Storage, and Meetings across TS, JS, Python, and Java.",
7
+ "keywords": [
8
+ "ecodrix",
9
+ "whatsapp",
10
+ "crm",
11
+ "sdk",
12
+ "automation",
13
+ "api",
14
+ "isomorphic"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/dhanesh1232/erix-api.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/dhanesh1232/erix-api/issues"
22
+ },
23
+ "homepage": "https://ecodrix.com",
24
+ "type": "module",
25
+ "main": "./dist/ts/cjs/index.cjs",
26
+ "module": "./dist/ts/esm/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "browser": "./dist/ts/browser/index.global.js",
29
+ "unpkg": "./dist/ts/browser/index.global.js",
30
+ "exports": {
31
+ ".": {
32
+ "types": {
33
+ "import": "./dist/ts/esm/index.d.ts",
34
+ "require": "./dist/ts/cjs/index.d.cts"
35
+ },
36
+ "import": "./dist/ts/esm/index.js",
37
+ "require": "./dist/ts/cjs/index.cjs"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "src",
43
+ "schema"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "postbuild": "cp dist/ts/esm/index.d.ts dist/index.d.ts",
48
+ "build:final": "pnpm clear && pnpm build",
49
+ "dev": "tsup --watch",
50
+ "clear": "rm -rf dist",
51
+ "lint": "biome lint .",
52
+ "format": "biome format --write .",
53
+ "check": "biome check --write .",
54
+ "prepublishOnly": "pnpm build"
55
+ },
56
+ "dependencies": {
57
+ "axios": "^1.7.0",
58
+ "axios-retry": "^4.5.0",
59
+ "socket.io-client": "^4.7.5"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^25.5.0",
63
+ "tsup": "^8.0.2",
64
+ "typescript": "^5.4.5"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public"
68
+ }
69
+ }
@@ -0,0 +1,199 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: ECOD platform API
4
+ description: The official API for the ECODrIx (ECOD) platform, supporting WhatsApp, CRM, Storage, and Meetings.
5
+ version: 1.0.0
6
+ servers:
7
+ - url: https://api.ecodrix.com
8
+ description: Production server
9
+
10
+ components:
11
+ securitySchemes:
12
+ ApiKeyAuth:
13
+ type: apiKey
14
+ in: header
15
+ name: x-api-key
16
+ ClientCodeAuth:
17
+ type: apiKey
18
+ in: header
19
+ name: x-client-code
20
+
21
+ schemas:
22
+ SuccessResponse:
23
+ type: object
24
+ properties:
25
+ success:
26
+ type: boolean
27
+ message:
28
+ type: string
29
+ data:
30
+ type: object
31
+
32
+ Lead:
33
+ type: object
34
+ properties:
35
+ id:
36
+ type: string
37
+ firstName:
38
+ type: string
39
+ lastName:
40
+ type: string
41
+ email:
42
+ type: string
43
+ phone:
44
+ type: string
45
+ status:
46
+ type: string
47
+ enum: [new, contacting, qualified, lost, won]
48
+ pipelineId:
49
+ type: string
50
+ stageId:
51
+ type: string
52
+
53
+ Meeting:
54
+ type: object
55
+ properties:
56
+ id:
57
+ type: string
58
+ title:
59
+ type: string
60
+ startTime:
61
+ type: string
62
+ format: date-time
63
+ endTime:
64
+ type: string
65
+ format: date-time
66
+ status:
67
+ type: string
68
+ enum: [scheduled, cancelled, completed]
69
+
70
+ WhatsAppMessage:
71
+ type: object
72
+ properties:
73
+ id:
74
+ type: string
75
+ to:
76
+ type: string
77
+ body:
78
+ type: string
79
+ type:
80
+ type: string
81
+ enum: [text, image, document]
82
+
83
+ security:
84
+ - ApiKeyAuth: []
85
+ ClientCodeAuth: []
86
+
87
+ paths:
88
+ # --- CRM LEADS ---
89
+ /saas/crm/leads:
90
+ get:
91
+ summary: List all leads
92
+ tags: [CRM]
93
+ responses:
94
+ "200":
95
+ description: A list of leads
96
+ content:
97
+ application/json:
98
+ schema:
99
+ type: object
100
+ properties:
101
+ success: { type: boolean }
102
+ data:
103
+ type: array
104
+ items: { $ref: "#/components/schemas/Lead" }
105
+ post:
106
+ summary: Create a new lead
107
+ tags: [CRM]
108
+ requestBody:
109
+ required: true
110
+ content:
111
+ application/json:
112
+ schema: { $ref: "#/components/schemas/Lead" }
113
+ responses:
114
+ "201":
115
+ description: Lead created
116
+ content:
117
+ application/json:
118
+ schema: { $ref: "#/components/schemas/SuccessResponse" }
119
+
120
+ /saas/crm/leads/{id}:
121
+ parameters:
122
+ - name: id
123
+ in: path
124
+ required: true
125
+ schema: { type: string }
126
+ get:
127
+ summary: Retrieve a specific lead
128
+ tags: [CRM]
129
+ responses:
130
+ "200":
131
+ description: The lead details
132
+ patch:
133
+ summary: Update a lead
134
+ tags: [CRM]
135
+ requestBody:
136
+ content:
137
+ application/json:
138
+ schema: { $ref: "#/components/schemas/Lead" }
139
+ responses:
140
+ "200":
141
+ description: Lead updated
142
+ delete:
143
+ summary: Delete a lead
144
+ tags: [CRM]
145
+ responses:
146
+ "200":
147
+ description: Lead deleted
148
+
149
+ # --- MEETINGS ---
150
+ /saas/meet/meetings:
151
+ get:
152
+ summary: List scheduled meetings
153
+ tags: [Meetings]
154
+ responses:
155
+ "200":
156
+ description: List of meetings
157
+ post:
158
+ summary: Schedule a new meeting
159
+ tags: [Meetings]
160
+ requestBody:
161
+ required: true
162
+ content:
163
+ application/json:
164
+ schema: { $ref: "#/components/schemas/Meeting" }
165
+ responses:
166
+ "201":
167
+ description: Meeting scheduled
168
+
169
+ # --- STORAGE ---
170
+ /saas/storage/presigned-url:
171
+ post:
172
+ summary: Generate a presigned URL for direct R2 upload
173
+ tags: [Storage]
174
+ requestBody:
175
+ required: true
176
+ content:
177
+ application/json:
178
+ schema:
179
+ type: object
180
+ properties:
181
+ filename: { type: string }
182
+ contentType: { type: string }
183
+ responses:
184
+ "200":
185
+ description: Presigned URL generated
186
+
187
+ # --- WHATSAPP ---
188
+ /saas/whatsapp/messages:
189
+ post:
190
+ summary: Send a WhatsApp message
191
+ tags: [WhatsApp]
192
+ requestBody:
193
+ required: true
194
+ content:
195
+ application/json:
196
+ schema: { $ref: "#/components/schemas/WhatsAppMessage" }
197
+ responses:
198
+ "200":
199
+ description: Message sent