{"_id":"@codemina/lumenta-sdk-nodejs","_rev":"4-8f61fafd8a62fbb10ae43a92790210f3","name":"@codemina/lumenta-sdk-nodejs","dist-tags":{"latest":"1.0.1"},"versions":{"0.1.1":{"name":"@codemina/lumenta-sdk-nodejs","version":"0.1.1","keywords":["lumenta","whatsapp","whatsapp-business","sdk","api-client","messaging"],"author":{"name":"Codemina","email":"hello@codemina.co.uk"},"license":"MIT","_id":"@codemina/lumenta-sdk-nodejs@0.1.1","maintainers":[{"name":"codemina","email":"ebrahim@codemina.com"}],"homepage":"https://github.com/codemina-ltd/lumenta-sdk-nodejs","bugs":{"url":"https://github.com/codemina-ltd/lumenta-sdk-nodejs/issues"},"dist":{"shasum":"f5a0dd37afcc6bae0c1b8415f31c82a66fd870ee","tarball":"https://registry.npmjs.org/@codemina/lumenta-sdk-nodejs/-/lumenta-sdk-nodejs-0.1.1.tgz","fileCount":19,"integrity":"sha512-sLx4G2HIAME3GjKg6iP/ssp9gS7pGE7CYoPvfsb0XxpqYkXKFHG7dAs1NAjAHK97ojOtX0oyRyJqzLNL2z0I6w==","signatures":[{"sig":"MEQCIDi/br7kLuvUcoChdQjg4HF8T11RuoMM4/O9bVKk/l3+AiBR6mi0QEpQYZVSfNaadxSayuha7/PXSWLGM7SIllPnWA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":406107},"main":"./dist/cjs/index.cjs","type":"module","types":"./dist/esm/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18.17"},"exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.cjs"},"./errors":{"types":"./dist/esm/errors.d.ts","import":"./dist/esm/errors.js","require":"./dist/cjs/errors.cjs"},"./webhooks":{"types":"./dist/esm/webhooks/index.d.ts","import":"./dist/esm/webhooks/index.js","require":"./dist/cjs/webhooks/index.cjs"}},"gitHead":"47dfd76e93ceb5d41f283282c0bfd5541943a8a4","scripts":{"lint":"eslint \"src/**/*.ts\" \"test/**/*.ts\"","test":"jest","build":"rm -rf dist && tsup","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","test:watch":"jest --watch","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"codemina","email":"ebrahim@codemina.com"},"repository":{"url":"git+https://github.com/codemina-ltd/lumenta-sdk-nodejs.git","type":"git"},"_npmVersion":"10.8.2","description":"Official Node.js SDK for the Lumenta WhatsApp Business API","directories":{},"_nodeVersion":"20.20.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.0.0","eslint":"^8.57.0","ts-jest":"^29.1.0","prettier":"^3.2.0","typescript":"^5.4.0","@types/jest":"^29.5.12","@types/node":"^20.11.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/lumenta-sdk-nodejs_0.1.1_1779637188499_0.8040631974288779","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@codemina/lumenta-sdk-nodejs","version":"0.2.0","keywords":["lumenta","whatsapp","whatsapp-business","sdk","api-client","messaging"],"author":{"name":"Codemina","email":"hello@codemina.co.uk"},"license":"MIT","_id":"@codemina/lumenta-sdk-nodejs@0.2.0","maintainers":[{"name":"codemina","email":"ebrahim@codemina.com"}],"homepage":"https://github.com/codemina-ltd/lumenta-sdk-nodejs","bugs":{"url":"https://github.com/codemina-ltd/lumenta-sdk-nodejs/issues"},"dist":{"shasum":"a0a6e07121c443773a9bb13f996649d2c5f37490","tarball":"https://registry.npmjs.org/@codemina/lumenta-sdk-nodejs/-/lumenta-sdk-nodejs-0.2.0.tgz","fileCount":19,"integrity":"sha512-7d5/60qu/6Zmi9DaY57Az4ugUCzO61GRlC2pasWdFvuqKgYefjY/WRY8lPEVbyM4qMAVxwSn5Fsuj0J4ckTgOw==","signatures":[{"sig":"MEUCIQCCpGBE4hfIMJ0QGEw5mcRRdsPJVwFA7hES0D4g+PkosgIgCQ0451Fv83oCY0FPpdEzDDwUP7jP8EKeeoPccx4RZ48=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":400568},"main":"./dist/cjs/index.cjs","type":"module","types":"./dist/esm/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18.17"},"exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.cjs"},"./errors":{"types":"./dist/esm/errors.d.ts","import":"./dist/esm/errors.js","require":"./dist/cjs/errors.cjs"},"./webhooks":{"types":"./dist/esm/webhooks/index.d.ts","import":"./dist/esm/webhooks/index.js","require":"./dist/cjs/webhooks/index.cjs"}},"gitHead":"52b078ec685dfbcf270ebf22ed4e7b94b9cd148c","scripts":{"lint":"eslint \"src/**/*.ts\" \"test/**/*.ts\"","test":"jest","build":"rm -rf dist && tsup","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","test:watch":"jest --watch","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"codemina","email":"ebrahim@codemina.com"},"repository":{"url":"git+https://github.com/codemina-ltd/lumenta-sdk-nodejs.git","type":"git"},"_npmVersion":"10.8.2","description":"Official Node.js SDK for the Lumenta WhatsApp Business API","directories":{},"_nodeVersion":"20.20.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.0.0","eslint":"^8.57.0","ts-jest":"^29.1.0","prettier":"^3.2.0","typescript":"^5.4.0","@types/jest":"^29.5.12","@types/node":"^20.19.43","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/lumenta-sdk-nodejs_0.2.0_1784343968976_0.502525405660091","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@codemina/lumenta-sdk-nodejs","version":"1.0.0","keywords":["lumenta","whatsapp","whatsapp-business","sdk","api-client","messaging"],"author":{"name":"Codemina","email":"hello@codemina.co.uk"},"license":"MIT","_id":"@codemina/lumenta-sdk-nodejs@1.0.0","maintainers":[{"name":"codemina","email":"ebrahim@codemina.com"}],"homepage":"https://github.com/codemina-ltd/lumenta-sdk-nodejs","bugs":{"url":"https://github.com/codemina-ltd/lumenta-sdk-nodejs/issues"},"dist":{"shasum":"3957da659e292552dc296adf867fb0f11483066d","tarball":"https://registry.npmjs.org/@codemina/lumenta-sdk-nodejs/-/lumenta-sdk-nodejs-1.0.0.tgz","fileCount":19,"integrity":"sha512-hdBSvVhByA0aQGn5mNZtNh9fMWSlRVHf8iH9u5hXk5C5+zdzuGIRvABohnuYVkk8xsacjAi0motmgun7Y+Obkw==","signatures":[{"sig":"MEQCIHuCynGSa9GOQs2nSFFHBr3HMCzcfvyAR8j8ZAZpFSpfAiAlqb7ITq3YI5LdOTFYljqfXD57vQyEju7vQR3tZAsLyg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":417891},"main":"./dist/cjs/index.cjs","type":"module","types":"./dist/esm/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18.17"},"exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.cjs"},"./errors":{"types":"./dist/esm/errors.d.ts","import":"./dist/esm/errors.js","require":"./dist/cjs/errors.cjs"},"./webhooks":{"types":"./dist/esm/webhooks/index.d.ts","import":"./dist/esm/webhooks/index.js","require":"./dist/cjs/webhooks/index.cjs"}},"gitHead":"584f861c659651f20ed58d32dca18ab63ec99ee1","scripts":{"lint":"eslint \"src/**/*.ts\" \"test/**/*.ts\"","test":"jest","build":"rm -rf dist && tsup","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","test:watch":"jest --watch","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"codemina","email":"ebrahim@codemina.com"},"repository":{"url":"git+https://github.com/codemina-ltd/lumenta-sdk-nodejs.git","type":"git"},"_npmVersion":"10.8.2","description":"Official Node.js SDK for the Lumenta WhatsApp Business API","directories":{},"_nodeVersion":"20.20.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.0.0","eslint":"^8.57.0","ts-jest":"^29.1.0","prettier":"^3.2.0","typescript":"^5.4.0","@types/jest":"^29.5.12","@types/node":"^20.19.43","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/lumenta-sdk-nodejs_1.0.0_1784346082272_0.31138259478546426","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@codemina/lumenta-sdk-nodejs","version":"1.0.1","description":"Official Node.js SDK for the Lumenta WhatsApp Business API","keywords":["lumenta","whatsapp","whatsapp-business","sdk","api-client","messaging"],"homepage":"https://github.com/codemina-ltd/lumenta-sdk-nodejs","bugs":{"url":"https://github.com/codemina-ltd/lumenta-sdk-nodejs/issues"},"repository":{"type":"git","url":"git+https://github.com/codemina-ltd/lumenta-sdk-nodejs.git"},"license":"MIT","author":{"name":"Codemina","email":"hello@codemina.co.uk"},"type":"module","main":"./dist/cjs/index.cjs","module":"./dist/esm/index.js","types":"./dist/esm/index.d.ts","exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.cjs"},"./webhooks":{"types":"./dist/esm/webhooks/index.d.ts","import":"./dist/esm/webhooks/index.js","require":"./dist/cjs/webhooks/index.cjs"},"./errors":{"types":"./dist/esm/errors.d.ts","import":"./dist/esm/errors.js","require":"./dist/cjs/errors.cjs"}},"engines":{"node":">=18.17"},"scripts":{"build":"rm -rf dist && tsup","typecheck":"tsc --noEmit","test":"jest","test:watch":"jest --watch","lint":"eslint \"src/**/*.ts\" \"test/**/*.ts\"","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","prepublishOnly":"npm run typecheck && npm test && npm run build"},"devDependencies":{"@types/jest":"^29.5.12","@types/node":"^20.19.43","@typescript-eslint/eslint-plugin":"^7.0.0","@typescript-eslint/parser":"^7.0.0","eslint":"^8.57.0","jest":"^29.7.0","prettier":"^3.2.0","ts-jest":"^29.1.0","tsup":"^8.0.0","typescript":"^5.4.0"},"publishConfig":{"access":"public"},"_id":"@codemina/lumenta-sdk-nodejs@1.0.1","gitHead":"0ef75bd16945578568759278b9527b9c16c029ee","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-lHZymUxm8ZCfgjxxu8BskOIMPjgcLkf0Ef+ftMQb0YVyISIwF+PF2DqeqMbZlc2Es3Rdo/I9UudbQeb7KMYfoA==","shasum":"9e70114112a55207999f924bd65a36d0515393c2","tarball":"https://registry.npmjs.org/@codemina/lumenta-sdk-nodejs/-/lumenta-sdk-nodejs-1.0.1.tgz","fileCount":19,"unpackedSize":423212,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDnqYQn/gw7yA+kYgTOm38Q/3TUHjzAN9P2rTsqGr3zCgIhANuNDSUItRN0BIKLU557snIwXxETRuYJEF28ajMMw03N"}]},"_npmUser":{"name":"codemina","email":"ebrahim@codemina.com"},"directories":{},"maintainers":[{"name":"codemina","email":"ebrahim@codemina.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/lumenta-sdk-nodejs_1.0.1_1784347280838_0.9968384758466646"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-24T15:39:48.399Z","modified":"2026-07-18T04:01:21.112Z","0.1.1":"2026-05-24T15:39:48.674Z","0.2.0":"2026-07-18T03:06:09.123Z","1.0.0":"2026-07-18T03:41:22.396Z","1.0.1":"2026-07-18T04:01:20.986Z"},"bugs":{"url":"https://github.com/codemina-ltd/lumenta-sdk-nodejs/issues"},"author":{"name":"Codemina","email":"hello@codemina.co.uk"},"license":"MIT","homepage":"https://github.com/codemina-ltd/lumenta-sdk-nodejs","keywords":["lumenta","whatsapp","whatsapp-business","sdk","api-client","messaging"],"repository":{"type":"git","url":"git+https://github.com/codemina-ltd/lumenta-sdk-nodejs.git"},"description":"Official Node.js SDK for the Lumenta WhatsApp Business API","maintainers":[{"name":"codemina","email":"ebrahim@codemina.com"}],"readme":"# Lumenta Node.js SDK\n\n[![npm version](https://img.shields.io/npm/v/@codemina/lumenta-sdk-nodejs.svg)](https://www.npmjs.com/package/@codemina/lumenta-sdk-nodejs)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nOfficial Node.js / TypeScript SDK for the [Lumenta](https://lumenta.io) WhatsApp Business API. Send messages, manage templates and flows, run campaigns, segment contacts, build automations, and receive webhooks — all with end-to-end types.\n\n## Installation\n\n```bash\nnpm install @codemina/lumenta-sdk-nodejs\n# or\nyarn add @codemina/lumenta-sdk-nodejs\n# or\npnpm add @codemina/lumenta-sdk-nodejs\n```\n\nRequires **Node.js 18.17+** (native `fetch`). Works in CommonJS and ES Modules.\n\n## Quickstart\n\n```ts\nimport { LumentaClient } from '@codemina/lumenta-sdk-nodejs';\n\nconst lumenta = new LumentaClient({\n  apiKey: process.env.LUMENTA_API_KEY!,   // pk_live_xxxx.yyyyyyyy\n  tenantId: process.env.LUMENTA_TENANT_ID, // optional if your key already binds to one tenant\n});\n\nconst message = await lumenta.messages.send({\n  to: '441234567890',\n  body: 'Hello from the Lumenta SDK 👋',\n});\n\nconsole.log(message.id, message.status);\n```\n\n## Authentication\n\nGenerate an API key in the Lumenta portal under **Settings → API keys**. Keys look like:\n\n```\npk_live_abcdef0123456789.<long random secret>\n```\n\nThe full key is shown **once** at creation time. Store it as a secret — anyone with the key can act on behalf of your tenant.\n\nThe SDK sends the key as a `Bearer` token in the `Authorization` header. The API also accepts `X-Lumenta-Api-Key` and `X-Api-Key` headers as alternatives (used by Zapier and generic tooling); the SDK uses the standard Bearer form.\n\n### Scopes\n\nEvery API key is minted with one or more scopes. The SDK does not pre-check scopes — calls fail at the API with `403 INSUFFICIENT_SCOPE` if the key cannot perform the action.\n\n| Resource     | Scopes                            |\n| ------------ | --------------------------------- |\n| Messages     | `messages:read`, `messages:write` |\n| Clients      | `clients:read`, `clients:write`   |\n| Templates    | `templates:read`, `templates:write` |\n| Campaigns    | `campaigns:read`, `campaigns:write` |\n| Segments     | `segments:read`, `segments:write` |\n| Flows        | `flows:read`, `flows:write`       |\n| Auto-replies | `campaigns:read`, `campaigns:write` |\n| Follow-ups   | `campaigns:read`, `campaigns:write` |\n| Chat widgets | `campaigns:read`, `campaigns:write` |\n| Suppression  | `clients:read`, `clients:write`   |\n| Senders (read-only) | `senders:read`             |\n| Dashboard    | `stats:read`                      |\n| Zapier hooks | `webhooks:manage`                 |\n\nThe wildcard `*` and per-resource wildcards (`messages:*`, `campaigns:*`, …) are also supported by the server.\n\n## Configuration\n\n```ts\nnew LumentaClient({\n  apiKey: 'pk_live_xxxx.yyyy',\n  baseUrl: 'https://api.lumenta.io',  // default\n  tenantId: 'optional-tenant-uuid',\n  timeoutMs: 30_000,                  // default 30 seconds\n  maxRetries: 2,                      // GET/PUT only; default 2\n  fetch: customFetchImpl,             // optional override (e.g. for tests)\n  defaultHeaders: { 'X-Trace-Id': '…' },\n  userAgent: 'my-app/1.0',\n});\n```\n\nEvery resource method accepts an optional `extras` object for per-call overrides:\n\n```ts\nawait lumenta.messages.send(\n  { to: '441234567890', body: 'Hi' },\n  { tenantId: 'other-tenant', timeoutMs: 5_000, maxRetries: 0, signal: ac.signal },\n);\n```\n\n## Messages\n\n```ts\n// Plain text\nawait lumenta.messages.send({\n  to: '441234567890',\n  body: 'Hello!',\n});\n\n// Approved template (by internal UUID)\nawait lumenta.messages.sendTemplate({\n  to: '441234567890',\n  templateId: '550e8400-e29b-41d4-a716-446655440000',\n  templateVariables: { '1': 'Alex', '2': '22 July' },\n});\n\n// External Meta template (by name)\nawait lumenta.messages.sendTemplate({\n  to: '441234567890',\n  externalTemplateId: 'welcome_message',\n  language: 'en_US',\n  templateVariables: { '1': 'Alex' },\n});\n\n// Media (image / video / document / voice note)\nimport { readFile } from 'node:fs/promises';\nconst file = await readFile('./photo.jpg');\nawait lumenta.messages.sendMedia({\n  to: '441234567890',\n  mediaType: 'image',\n  file,\n  filename: 'photo.jpg',\n  caption: 'Here you go',\n});\n\n// Voice note (audio re-encoded as a voice message)\nawait lumenta.messages.sendMedia({\n  to: '441234567890',\n  mediaType: 'audio',\n  file: await readFile('./greeting.ogg'),\n  voiceNote: true,\n});\n\n// Listing (paginated)\nconst recent = await lumenta.messages.list({ page: 1, limit: 25, direction: 'outbound' });\n```\n\n## Clients\n\n```ts\n// Find-or-create on phone\nconst existing = await lumenta.clients.findByPhone('441234567890');\nif (!existing) {\n  await lumenta.clients.create({ phoneNumber: '441234567890', profileName: 'Alex' });\n}\n\n// Bulk import (JSON)\nawait lumenta.clients.import({\n  clients: [\n    { phoneNumber: '441111111111', profileName: 'A' },\n    { phoneNumber: '442222222222' },\n  ],\n});\n\n// Bulk import (XLSX upload — columns: phoneNumber, profileName)\nimport { readFile } from 'node:fs/promises';\nconst xlsx = await readFile('./contacts.xlsx');\nconst result = await lumenta.clients.importXlsx(xlsx, { filename: 'contacts.xlsx' });\nconsole.log(result.success, result.failed, result.duplicatesSkipped);\n```\n\n## Campaigns\n\n```ts\n// Text campaign\nconst campaign = await lumenta.campaigns.createText({\n  senderId: 'sender-uuid',\n  recipients: { segmentIds: ['vip-segment-uuid'] },\n  message: 'Flash sale ends tonight!',\n});\n\n// Template campaign with variables\nawait lumenta.campaigns.createTemplate({\n  senderId: 'sender-uuid',\n  templateId: 'approved-template-uuid',\n  recipients: { clientIds: ['c1', 'c2'] },\n  templateVariables: { '1': 'Friend' },\n  buttonVariables: { coupon_code: 'SAVE20' },\n});\n\n// Schedule for later (ISO-8601, future timestamp at least 5 min ahead)\nawait lumenta.campaigns.createText({\n  senderId: 'sender-uuid',\n  recipients: { segmentIds: ['vip-segment-uuid'] },\n  message: 'Doors open at 9!',\n  scheduledFor: '2026-07-22T09:00:00.000Z',\n  timezone: 'Europe/London',\n});\n\n// Resend failed\nawait lumenta.campaigns.resendFailed('campaign-id'); // all failed\nawait lumenta.campaigns.resendFailed('campaign-id', { recipientIds: ['msg-id-1'] });\n\n// Cancel a scheduled campaign\nawait lumenta.campaigns.cancel('campaign-id');\n\n// Per-recipient records (paginated, filterable)\nawait lumenta.campaigns.listRecords('campaign-id', {\n  status: ['failed'],\n  search: '4412',\n});\n```\n\n## Templates\n\n```ts\nconst t = await lumenta.templates.create({\n  name: 'welcome_message',\n  category: 'marketing',\n  language: 'en_US',\n  body: 'Hi {{1}}, welcome to {{2}}!',\n  bodyExample: [['Alex', 'Acme']],\n  headerFormat: 'TEXT',\n  headerText: 'Welcome!',\n  footerText: 'Powered by Acme',\n  buttons: [\n    { type: 'QUICK_REPLY', text: 'Get started' },\n    { type: 'URL', text: 'Visit site', url: 'https://acme.com' },\n  ],\n});\n\n// Upload an image header asset, then pass the handle to create()\nimport { readFile } from 'node:fs/promises';\nconst { handle, s3Key } = await lumenta.templates.uploadHeaderMedia({\n  file: await readFile('./banner.jpg'),\n  filename: 'banner.jpg',\n  contentType: 'image/jpeg',\n  mediaType: 'image',\n});\n// → create({ ..., headerFormat: 'IMAGE', headerHandle: handle, headerS3Key: s3Key })\n```\n\n## Segments\n\n```ts\nconst seg = await lumenta.segments.create({ name: 'VIPs', description: '...' });\nawait lumenta.segments.addClients(seg.id, ['client-uuid-1', 'client-uuid-2']);\nconst members = await lumenta.segments.listClients(seg.id, { page: 1, limit: 50 });\n```\n\n## Flows\n\n```ts\nconst flow = await lumenta.flows.create({\n  name: 'Appointment Booking',\n  categories: ['APPOINTMENT_BOOKING'],\n  flowJson: JSON.stringify({ version: '5.0', screens: [/* … */] }),\n  publish: true,\n});\n\nawait lumenta.flows.send(flow.id, {\n  to: '441234567890',\n  flowCta: 'Book Appointment',\n  bodyText: 'Please complete the form below.',\n});\n```\n\n## Auto-replies\n\n```ts\nawait lumenta.autoReplies.create({\n  name: 'Office hours',\n  matchMode: 'contains',\n  keywords: ['hours', 'open'],\n  replyType: 'text',\n  replyBody: 'We are open Mon–Fri 9–17.',\n  cooldownSeconds: 300,\n});\n\n// Reorder priority (top first)\nawait lumenta.autoReplies.reorder(['rule-1-id', 'rule-2-id', 'rule-3-id']);\n```\n\n## Follow-ups\n\n```ts\nconst fu = await lumenta.followUps.create({\n  name: '24h check-in',\n  senderId: 'sender-uuid',\n  messageType: 'text',\n  messageBody: 'Hey, anything else we can help with?',\n  windowHours: 24,\n  minSilenceMinutes: 60,\n  quietHoursStart: '22:00',\n  quietHoursEnd: '08:00',\n  timezone: 'Europe/London',\n});\n\nawait lumenta.followUps.setStatus(fu.id, 'active');\nawait lumenta.followUps.runNow(fu.id);\n```\n\n## Chat widgets\n\n```ts\nconst widget = await lumenta.chatWidgets.create({\n  name: 'Main site widget',\n  senderId: 'sender-uuid',\n  themeColor: '#00C896',\n  greetingText: 'Hi! How can we help?',\n  allowedDomains: ['acme.com', 'www.acme.com'],\n});\n\nconst stats = await lumenta.chatWidgets.stats(widget.id, { windowDays: 30 });\n```\n\n## Suppression\n\n```ts\nawait lumenta.suppression.suppress('client-uuid', {\n  scope: 'marketing',\n  reason: 'user_optout',\n  notes: 'Replied STOP',\n});\n\nconst suppressed = await lumenta.suppression.listForClient('client-uuid');\n\n// Release a single scope, or all active suppressions if scope is omitted\nawait lumenta.suppression.release('client-uuid', { scope: 'marketing' });\n```\n\n## Dashboard\n\n```ts\nconst stats = await lumenta.dashboard.stats({\n  from: '2026-04-01T00:00:00Z',\n  to: '2026-05-01T00:00:00Z',\n});\n```\n\n## Webhook subscriptions (Zapier-style)\n\n```ts\nconst me = await lumenta.zapier.me(); // auth-test\nconst sub = await lumenta.zapier.subscribe({\n  target_url: 'https://hooks.zapier.com/hooks/catch/123/abc/',\n  event: 'message.received',\n});\nawait lumenta.zapier.unsubscribe(sub.id);\n```\n\n> **Note** — `target_url` must point at `hooks.zapier.com` (server-side guard). Use a forwarding service if you need to receive events on a different host.\n\n## Receiving webhooks\n\nThe SDK exports verification helpers for Meta-style signatures. Use them in your inbound webhook handler to confirm the request really came from Lumenta.\n\n```ts\nimport express from 'express';\nimport {\n  verifyMetaSignature,\n  isFreshMetaPayload,\n} from '@codemina/lumenta-sdk-nodejs/webhooks';\n\nconst app = express();\n\n// IMPORTANT: capture the *raw* body for signature verification.\napp.use(express.json({\n  verify: (req, _res, buf) => {\n    (req as any).rawBody = buf;\n  },\n}));\n\napp.post('/webhooks/meta', (req, res) => {\n  const ok = verifyMetaSignature(\n    (req as any).rawBody,\n    req.header('x-hub-signature-256'),\n    process.env.META_APP_SECRET!,\n  );\n  if (!ok || !isFreshMetaPayload(req.body, 300)) {\n    return res.status(401).end();\n  }\n  // … process req.body\n  res.status(200).end();\n});\n\n```\n\n## Error handling\n\nEvery error thrown by the SDK extends `LumentaError`. Specific subclasses let you handle common cases:\n\n```ts\nimport {\n  LumentaError,\n  ValidationError,\n  AuthenticationError,\n  ForbiddenError,\n  NotFoundError,\n  ConflictError,\n  RateLimitError,\n  ServerError,\n  TimeoutError,\n  NetworkError,\n} from '@codemina/lumenta-sdk-nodejs';\n\ntry {\n  await lumenta.messages.send({ to: 'bad', body: 'hi' });\n} catch (err) {\n  if (err instanceof RateLimitError) {\n    console.warn('rate-limited; retry after', err.retryAfterSeconds, 's');\n  } else if (err instanceof ValidationError) {\n    console.warn(err.code, err.details);\n  } else if (err instanceof LumentaError) {\n    console.error(err.status, err.code, err.message);\n  } else {\n    throw err;\n  }\n}\n```\n\nEvery error exposes `{ status, code, message, details, requestId, path }` — `code` mirrors the API's `errorCode` field (e.g. `INVALID_PHONE_NUMBER`, `INSUFFICIENT_SCOPE`).\n\n## Retries & timeouts\n\n- The transport automatically retries **GET** and **PUT** requests on `429`, `5xx`, and network errors with exponential backoff + jitter (cap 8 s). The `Retry-After` header is honoured when present.\n- Non-idempotent methods (POST, PATCH, DELETE) are **not** retried by default. Override per call with `{ maxRetries: 1 }` if your operation is idempotent.\n- The default request timeout is 30 s, configurable globally or per call.\n- All calls accept an `AbortSignal` for cooperative cancellation.\n\n## Escape hatch\n\nNeed to call an endpoint the SDK doesn't wrap yet?\n\n```ts\nconst raw = await lumenta.raw<{ data: unknown }>({\n  method: 'GET',\n  path: '/v1/some/new-endpoint',\n  query: { foo: 'bar' },\n});\n```\n\n## Coverage\n\nThe SDK wraps every public, API-key-callable endpoint of the Lumenta REST API as of v1, across **13 resources**:\n\n`messages` · `clients` · `templates` · `campaigns` · `segments` · `flows` · `autoReplies` · `followUps` · `chatWidgets` · `suppression` · `senders` (read-only) · `dashboard` · `zapier`\n\n## Examples\n\nSee [`examples/`](./examples) for runnable scripts.\n\n## Contributing\n\nIssues and PRs welcome at [github.com/codemina/lumenta-sdk-nodejs](https://github.com/codemina/lumenta-sdk-nodejs). See [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n## License\n\nMIT © [Codemina Ltd.](https://codemina.co.uk)\n","readmeFilename":"README.md"}