@zindua/sdk 1.2.2 → 1.2.4

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.
package/README.md CHANGED
@@ -1,217 +1,350 @@
1
1
  # @zindua/sdk
2
2
 
3
- Official **server-side** SDK for [Zindua](https://zindua.run): one API for transactional **email** and **WhatsApp** (`POST /api/v1/send`).
3
+ Official **server-side** SDK for [Zindua](https://zindua.run): send transactional **email** and **WhatsApp** messages with one API.
4
4
 
5
- | Resource | Link |
6
- |----------|------|
7
- | Website & docs | [zindua.run/developers](https://zindua.run/developers) |
8
- | HTTP / cURL (no SDK) | [zindua.run/developers#http](https://zindua.run/developers#http) |
9
- | Pricing & plans | [zindua.run/pricing](https://zindua.run/pricing) |
10
- | Dashboard | [zindua.run/login](https://zindua.run/login) |
5
+ | | |
6
+ |---|---|
7
+ | Full API reference | [zindua.run/developers](https://zindua.run/developers) |
8
+ | Dashboard (keys, templates, services) | [zindua.run/login](https://zindua.run/login) |
9
+ | Pricing | [zindua.run/pricing](https://zindua.run/pricing) |
11
10
 
12
- Install: `npm install @zindua/sdk`
11
+ ```bash
12
+ npm install @zindua/sdk
13
+ ```
14
+
15
+ **Requirements:** Node.js 18+, run only on your **backend** (not in the browser).
13
16
 
14
17
  ---
15
18
 
16
- ## Templates and languages
19
+ ## Quick start
20
+
21
+ ### 1. Create a Zindua project
22
+
23
+ 1. Sign in at [zindua.run/login](https://zindua.run/login).
24
+ 2. Open **Projects** → create a project (or use an existing one).
25
+ 3. Copy the API key (`znd_live_…` for production, `znd_test_…` for sandbox).
26
+ 4. In the project: connect **Service** (Gmail, SMTP, …) for email, and/or link **WhatsApp** for OTP.
27
+ 5. Create at least one **template** (e.g. slug `otp`, `welcome`).
17
28
 
18
- Create templates in the **dashboard** (one **slug** per template, e.g. `welcome`, `otp-verification`). Each template can include **email HTML** and **WhatsApp text** for the same slug.
29
+ ### 2. Install and configure
30
+
31
+ ```bash
32
+ npm install @zindua/sdk
33
+ ```
34
+
35
+ ```bash
36
+ # .env — never commit real keys; never expose in frontend code
37
+ ZINDUA_API_KEY=znd_live_xxxxxxxxxxxxxxxxxxxxxxxx
38
+ ```
19
39
 
20
- Send in a locale with the `lang` field (ISO code: `fr`, `en`, `sw`, …). If you omit `lang`, Zindua uses your project default.
40
+ ```typescript
41
+ import { Zindua } from "@zindua/sdk";
21
42
 
22
- | Plan | Templates per project | Languages per template (email + WhatsApp) |
23
- |------|----------------------|-------------------------------------------|
24
- | **Free** | 5 | 3 |
25
- | **Pro** | 50 | 10 |
26
- | **Team** | 1000 | 100 |
43
+ export const zindua = new Zindua({
44
+ apiKey: process.env.ZINDUA_API_KEY!,
45
+ });
46
+ ```
27
47
 
28
- On **Free**, the **email API** (`channel: "email"`) is for dashboard/testing; production email via API needs **Pro** or **Team**. **WhatsApp OTP** works on Free (quota applies). See [pricing](https://zindua.run/pricing) for quotas.
48
+ ### 3. Send a message
29
49
 
30
50
  ```typescript
31
- // French version of the same template slug
51
+ // Email (default channel)
32
52
  await zindua.send({
33
53
  to: "user@example.com",
34
54
  template: "welcome",
35
- lang: "fr",
36
55
  variables: { name: "Alex" },
37
56
  });
38
57
 
39
- // WhatsApp: same slug, different channel + E.164 phone
58
+ // WhatsApp phone in E.164 with +
40
59
  await zindua.send({
41
60
  to: "+243812345678",
42
61
  channel: "whatsapp",
43
- template: "otp-verification",
44
- lang: "fr",
45
- variables: { code: "4592", app: "MonApp" },
62
+ template: "otp",
63
+ variables: { code: "482910" },
46
64
  });
47
65
  ```
48
66
 
49
67
  ---
50
68
 
51
- ## Requirements
69
+ ## Plans and channels
52
70
 
53
- Before using the SDK, set up your Zindua project in the dashboard:
71
+ | Plan | Email API | WhatsApp | Email / month | WhatsApp OTP / month |
72
+ |------|-----------|----------|---------------|----------------------|
73
+ | **Free** | Yes, if a **Service** is connected on the project | Yes | 25,000 | 200 |
74
+ | **Pro** | Yes | Yes | 160,000 | 20,000 |
75
+ | **Team** | Yes | Yes | High volume | Unlimited |
54
76
 
55
- | Requirement | Required for | Where |
56
- |-------------|--------------|--------|
57
- | Zindua account | All | [Sign up / login](https://zindua.run/login) |
58
- | Project + API key (`znd_live_…` or `znd_test_…`) | All | Dashboard → **Projects** → your project |
59
- | Email **Service** connected (Gmail, Outlook, SendGrid, SMTP, …) | `channel: "email"` | Dashboard → **Service** |
60
- | WhatsApp connected (QR) | `channel: "whatsapp"` | Dashboard → **WhatsApp** |
61
- | Template slug (e.g. `welcome`, `otp-verification`) | All | Dashboard → **Templates** |
62
- | **Pro** or **Team** plan | Email API on production sends (Free = WhatsApp OTP API) | Dashboard → **Billing** |
77
+ On **Free**, email works through **your** connected provider (Gmail, SMTP, etc.). **Pro** and **Team** raise template counts and monthly limits. Details: [pricing](https://zindua.run/pricing).
63
78
 
64
- **Runtime**
79
+ | | Free | Pro / Team |
80
+ |---|------|------------|
81
+ | Templates per project | 5 | 50+ |
82
+ | Languages per template | 3 | 10+ |
65
83
 
66
- - **Node.js 18+** (uses native `fetch`)
67
- - **Server only** — do not bundle this package for the browser (see [Mobile & SPA](#mobile--spa))
84
+ Use `znd_test_…` keys to try the API without consuming live quota (sandbox behaviour).
68
85
 
69
86
  ---
70
87
 
71
- ## Install
88
+ ## One project or several?
72
89
 
73
- ```bash
74
- npm install @zindua/sdk
75
- ```
90
+ | Setup | When it fits |
91
+ |-------|----------------|
92
+ | **One Zindua project, one API key** | One product, one sender, shared templates and quota. |
93
+ | **Several Zindua projects, one key each** | Several clients or brands: separate templates, Gmail accounts, logs, and quotas. |
76
94
 
77
- ```bash
78
- yarn add @zindua/sdk
79
- ```
95
+ ### Several keys in one backend
80
96
 
81
97
  ```bash
82
- pnpm add @zindua/sdk
98
+ ZINDUA_KEY_CLIENT_A=znd_live_aaaaaaaaaaaaaaaaaaaaaaaa
99
+ ZINDUA_KEY_CLIENT_B=znd_live_bbbbbbbbbbbbbbbbbbbbbbbb
83
100
  ```
84
101
 
85
- ---
102
+ ```typescript
103
+ import { Zindua } from "@zindua/sdk";
104
+
105
+ const clients = {
106
+ clientA: new Zindua({ apiKey: process.env.ZINDUA_KEY_CLIENT_A! }),
107
+ clientB: new Zindua({ apiKey: process.env.ZINDUA_KEY_CLIENT_B! }),
108
+ };
86
109
 
87
- ## Configure
110
+ await clients.clientA.send({
111
+ to: "user@example.com",
112
+ template: "otp",
113
+ variables: { code: "123456" },
114
+ });
115
+ ```
88
116
 
89
- ### 1. API key
117
+ Each key only accesses **its** Zindua project (templates, service, usage).
90
118
 
91
- Copy your project key from the dashboard. Format: `znd_live_` + 24 characters, or `znd_test_` for test mode.
119
+ ---
92
120
 
93
- ```bash
94
- # .env (example — load with dotenv or your framework)
95
- ZINDUA_API_KEY=znd_live_xxxxxxxxxxxxxxxxxxxxxxxx
96
- ```
121
+ ## Framework examples
97
122
 
98
- The SDK does **not** read `ZINDUA_API_KEY` automatically. Pass it to the constructor explicitly.
123
+ Use the same pattern everywhere: **your server** holds the API key; the client app calls **your** API.
99
124
 
100
- ### 2. Base URL (optional)
125
+ ### Next.js (App Router)
101
126
 
102
- | Variable | Default | Notes |
103
- |----------|---------|--------|
104
- | `ZINDUA_API_BASE_URL` | `https://zindua.run/api/v1` | Used when `baseUrl` is omitted in constructor |
105
- | Constructor `baseUrl` | env or default | Overrides env; must be **HTTPS** in production (`http://localhost` allowed for local dev) |
127
+ ```typescript
128
+ // lib/zindua.ts
129
+ import { Zindua } from "@zindua/sdk";
106
130
 
107
- ### 3. Timeout (optional)
131
+ export const zindua = new Zindua({ apiKey: process.env.ZINDUA_API_KEY! });
132
+ ```
108
133
 
109
- | Option | Default | Max |
110
- |--------|---------|-----|
111
- | `timeoutMs` | `30000` | `120000` |
134
+ ```typescript
135
+ // app/api/notify/route.ts
136
+ import { zindua } from "@/lib/zindua";
137
+ import { NextResponse } from "next/server";
138
+
139
+ export async function POST(req: Request) {
140
+ const { email, code } = await req.json();
141
+ const result = await zindua.send({
142
+ to: email,
143
+ template: "otp",
144
+ variables: { code },
145
+ });
146
+ return NextResponse.json(result, { status: 202 });
147
+ }
148
+ ```
112
149
 
113
- ### Minimal setup
150
+ ### Express
114
151
 
115
152
  ```typescript
153
+ import express from "express";
116
154
  import { Zindua } from "@zindua/sdk";
117
155
 
118
- export const zindua = new Zindua({
119
- apiKey: process.env.ZINDUA_API_KEY!,
120
- // baseUrl: process.env.ZINDUA_API_BASE_URL, // optional
121
- // timeoutMs: 30_000,
156
+ const app = express();
157
+ app.use(express.json());
158
+
159
+ const zindua = new Zindua({ apiKey: process.env.ZINDUA_API_KEY! });
160
+
161
+ app.post("/api/notify", async (req, res) => {
162
+ try {
163
+ const result = await zindua.send({
164
+ to: req.body.email,
165
+ template: req.body.template ?? "otp",
166
+ variables: req.body.variables ?? {},
167
+ });
168
+ res.status(202).json(result);
169
+ } catch (err) {
170
+ res.status(500).json({ error: "Send failed" });
171
+ }
122
172
  });
123
173
  ```
124
174
 
125
- ---
175
+ ### Fastify
126
176
 
127
- ## Usage
177
+ ```typescript
178
+ import Fastify from "fastify";
179
+ import { Zindua } from "@zindua/sdk";
128
180
 
129
- ### Send email
181
+ const zindua = new Zindua({ apiKey: process.env.ZINDUA_API_KEY! });
182
+ const app = Fastify();
130
183
 
131
- ```typescript
132
- await zindua.send({
133
- to: "user@example.com",
134
- template: "welcome",
135
- variables: { name: "Alex" },
184
+ app.post<{ Body: { email: string; code: string } }>("/notify", async (req, reply) => {
185
+ const result = await zindua.send({
186
+ to: req.body.email,
187
+ template: "otp",
188
+ variables: { code: req.body.code },
189
+ });
190
+ return reply.status(202).send(result);
136
191
  });
137
192
  ```
138
193
 
139
- ### Send WhatsApp OTP
140
-
141
- Phone numbers must be **E.164** with a leading `+` (e.g. `+243812345678`).
194
+ ### Hono
142
195
 
143
196
  ```typescript
144
- await zindua.send({
145
- to: "+243812345678",
146
- channel: "whatsapp",
147
- template: "otp-verification",
148
- variables: { code: "4592", app: "MyApp" },
197
+ import { Hono } from "hono";
198
+ import { Zindua } from "@zindua/sdk";
199
+
200
+ const zindua = new Zindua({ apiKey: process.env.ZINDUA_API_KEY! });
201
+ const app = new Hono();
202
+
203
+ app.post("/notify", async (c) => {
204
+ const { email, code } = await c.req.json();
205
+ const result = await zindua.send({
206
+ to: email,
207
+ template: "otp",
208
+ variables: { code },
209
+ });
210
+ return c.json(result, 202);
149
211
  });
150
212
  ```
151
213
 
152
- ### Optional fields (email only)
214
+ ### Mobile apps (React Native, Flutter)
215
+
216
+ Do **not** embed `@zindua/sdk` or `znd_live_…` in the app. Call your backend route above from the device.
217
+
218
+ ---
219
+
220
+ ## Configuration options
221
+
222
+ | Option | Default | Description |
223
+ |--------|---------|-------------|
224
+ | `apiKey` | — | **Required.** `znd_live_…` or `znd_test_…` from the dashboard. |
225
+ | `baseUrl` | `https://zindua.run/api/v1` | Override only for local testing (`http://localhost:3000/api/v1`). |
226
+ | `timeoutMs` | `30000` | Request timeout (max `120000`). |
153
227
 
154
228
  ```typescript
155
- await zindua.send({
156
- to: "user@example.com",
157
- template: "invoice",
158
- lang: "fr",
159
- cc: "billing@example.com",
160
- replyTo: "support@example.com",
161
- variables: { amount: "99" },
229
+ const zindua = new Zindua({
230
+ apiKey: process.env.ZINDUA_API_KEY!,
231
+ baseUrl: process.env.ZINDUA_API_BASE_URL, // optional
232
+ timeoutMs: 45_000,
162
233
  });
163
234
  ```
164
235
 
165
- ### Response
236
+ ---
237
+
238
+ ## Send options
239
+
240
+ | Field | Required | Description |
241
+ |-------|----------|-------------|
242
+ | `to` | Yes | Email address, or E.164 phone (`+243…`) for WhatsApp. |
243
+ | `template` | Yes | Template slug from your dashboard. |
244
+ | `channel` | No | `"email"` (default) or `"whatsapp"`. |
245
+ | `lang` | No | `fr`, `en`, … — falls back to project default. |
246
+ | `variables` | No | `{{placeholders}}` in the template. |
247
+ | `cc`, `bcc`, `replyTo` | No | Email only. |
248
+
249
+ ---
250
+
251
+ ## Successful response
166
252
 
167
253
  ```typescript
168
254
  const result = await zindua.send({ /* … */ });
169
- // { success: true, status: "queued", logId: "…", channel: "email" | "whatsapp", … }
170
255
  ```
171
256
 
257
+ | Field | Meaning |
258
+ |-------|---------|
259
+ | `success` | `true` |
260
+ | `status` | `queued`, `processing`, or `sent` (test keys often return `sent` immediately). |
261
+ | `logId` | Id for support / logs in the dashboard. |
262
+ | `channel` | `email` or `whatsapp` |
263
+ | `context` | Workspace snapshot (see below). |
264
+
265
+ ### `context` object
266
+
267
+ The API returns metadata so you can log or debug without exposing secrets:
268
+
269
+ ```json
270
+ {
271
+ "context": {
272
+ "project": { "name": "My App", "slug": "my-app" },
273
+ "apiKey": { "mode": "live", "suffix": "ejho" },
274
+ "plan": { "slug": "free", "name": "Free", "emailQuota": 25000, "emailsUsed": 12 },
275
+ "channels": { "email": true, "whatsapp": false }
276
+ }
277
+ }
278
+ ```
279
+
280
+ - **`context.apiKey.suffix`** — last 4 characters of the key (match in the dashboard).
281
+ - **`context.plan`** — current plan and usage for the workspace linked to this key.
282
+ - **`context.channels`** — whether email / WhatsApp is ready for this project.
283
+
284
+ The full API key is **never** returned in responses.
285
+
172
286
  ---
173
287
 
174
288
  ## Errors
175
289
 
290
+ Wrap sends in `try/catch` and handle `ZinduaError`:
291
+
176
292
  ```typescript
177
293
  import { Zindua, ZinduaError } from "@zindua/sdk";
178
294
 
179
295
  try {
180
- await zindua.send({ to: "+243…", channel: "whatsapp", template: "otp-verification" });
296
+ await zindua.send({ to: "user@example.com", template: "otp", variables: { code: "1" } });
181
297
  } catch (e) {
182
298
  if (e instanceof ZinduaError) {
183
- console.error(e.status, e.code, e.message);
184
- // Platform codes may include: WHATSAPP_NOT_CONNECTED, RATE_LIMIT_EXCEEDED, …
299
+ console.log(e.status); // HTTP status
300
+ console.log(e.code); // machine-readable code
301
+ console.log(e.message); // human-readable
302
+ console.log(e.details); // optional: hint, context, retryAfterSec, …
185
303
  }
186
- throw e;
187
304
  }
188
305
  ```
189
306
 
190
- `ZinduaError` never includes your API key in the message.
191
-
192
- ---
193
-
194
- ## Security
195
-
196
- - **Server only** throws in browser environments (`BROWSER_FORBIDDEN`).
197
- - **HTTPS** for `baseUrl` except `http://localhost` in development.
198
- - **No credentials in URL** rejects `baseUrl` with embedded username/password.
199
- - **Input validation** E.164 phones, email format, template slug, variable size limits (aligned with the platform API).
200
- - **Redirects disabled** `fetch` uses `redirect: "error"`.
307
+ ### Error codes
308
+
309
+ | Code | HTTP | What it means | What to do |
310
+ |------|------|---------------|------------|
311
+ | `INVALID_API_KEY` | 401 | Missing or malformed key. | Use `Authorization: Bearer znd_live_…` (24 chars after prefix). |
312
+ | `API_KEY_NOT_FOUND` | 401 | Key not linked to a project. | Copy the key from Dashboard → your project. |
313
+ | `ORIGIN_NOT_ALLOWED` | 403 | Browser origin blocked. | Prefer server-side SDK; or add origin in Dashboard Settings → Integrations. |
314
+ | `MISSING_FIELDS` | 400 | `to` or `template` missing. | Send both in the request body. |
315
+ | `INVALID_EMAIL` | 400 | Bad email format. | Use `user@domain.com`. |
316
+ | `INVALID_PHONE` | 400 | Bad WhatsApp number. | Use E.164 with `+`, e.g. `+243812345678`. |
317
+ | `NO_SUBSCRIPTION` | 403 | Workspace has no active plan. | Open Dashboard → Billing or contact support. |
318
+ | `EMAIL_NOT_AVAILABLE` | 403 | Email not allowed on current plan setup. | Connect a Service on the project (Free), or upgrade plan. |
319
+ | `EMAIL_SERVICE_NOT_CONFIGURED` | 422 | No Gmail/SMTP connected. | Dashboard → project → **Service**. |
320
+ | `QUOTA_EXCEEDED` | 429 | Monthly email limit reached. | Upgrade plan or wait for reset. |
321
+ | `WHATSAPP_NOT_AVAILABLE` | 403 | WhatsApp not on plan. | Check [pricing](https://zindua.run/pricing). |
322
+ | `WHATSAPP_NOT_CONNECTED` | 422 | WhatsApp not linked. | Dashboard → project → **WhatsApp** → scan QR. |
323
+ | `WHATSAPP_PAUSED` | 422 | WhatsApp session paused. | Resume in the dashboard. |
324
+ | `WHATSAPP_QUOTA_EXCEEDED` | 429 | Monthly WhatsApp limit reached. | Upgrade or wait. |
325
+ | `RATE_LIMIT_EXCEEDED` | 429 | Sending too fast (WhatsApp). | Wait `retryAfterSec` from `e.details`, then retry. |
326
+ | `TEMPLATE_NOT_FOUND` | 404 | Unknown template slug. | Create template or fix slug; check `availableTemplateSlugs` in details. |
327
+ | `TEMPLATE_NO_CONTENT` | 404 | Template has no language version. | Add content in Dashboard → Templates. |
328
+ | `TEMPLATE_NO_WHATSAPP_BODY` | 422 | No plain text for WhatsApp. | Add a text body for that language. |
329
+ | `QUEUE_UNAVAILABLE` | 503 | Temporary delivery queue issue. | Retry shortly; contact support if it persists. |
330
+ | `BROWSER_FORBIDDEN` | — | SDK used in browser. | Move the call to your backend. |
331
+ | `REQUEST_TIMEOUT` | — | Request exceeded `timeoutMs`. | Increase timeout or retry. |
332
+
333
+ Many errors include a **`context`** field (same shape as success) with `plan` and `project` to help you debug.
201
334
 
202
335
  ---
203
336
 
204
- ## Mobile & SPA
337
+ ## Best practices
205
338
 
206
- Never put `znd_live_` keys in React Native, Flutter, or browser code. Call your own backend; your backend uses this SDK.
207
-
208
- See [Developer docs Quickstart](https://zindua.run/developers#setup).
339
+ 1. Store the API key in environment variables or a secrets manager.
340
+ 2. Call Zindua from your **API routes** only, not from React/Vue/mobile bundles.
341
+ 3. Use **`znd_test_…`** in staging; **`znd_live_…`** in production.
342
+ 4. Pin the SDK version in `package.json`, e.g. `"@zindua/sdk": "1.2.4"`.
343
+ 5. One Zindua project per client when quotas, senders, or templates must stay isolated.
209
344
 
210
345
  ---
211
346
 
212
- ## HTTP without the SDK
213
-
214
- Any language can call the same endpoint:
347
+ ## Without the SDK (HTTP)
215
348
 
216
349
  ```bash
217
350
  curl -X POST https://zindua.run/api/v1/send \
@@ -220,34 +353,20 @@ curl -X POST https://zindua.run/api/v1/send \
220
353
  -d '{"to":"user@example.com","template":"welcome","variables":{"name":"Alex"}}'
221
354
  ```
222
355
 
223
- Full reference: [HTTP / cURL](https://zindua.run/developers#http).
356
+ See [HTTP / cURL](https://zindua.run/developers#http).
224
357
 
225
358
  ---
226
359
 
227
- ## API surface
360
+ ## API reference
228
361
 
229
362
  | Export | Description |
230
363
  |--------|-------------|
231
364
  | `Zindua` | Client class |
232
- | `ZinduaError` | Typed error |
365
+ | `ZinduaError` | Error type (`status`, `code`, `message`, `details`) |
366
+ | `ZinduaSendResult` | Success payload type |
367
+ | `ZinduaSendContext` | Type of `result.context` |
233
368
  | `DEFAULT_API_BASE` | `https://zindua.run/api/v1` |
234
- | `LIMITS` | Validation limits (documented constants) |
235
-
236
- ---
237
-
238
- ## Local development (monorepo)
239
-
240
- ```bash
241
- cd packages/zindua-js
242
- npm run build
243
- npm test
244
- ```
245
-
246
- From the app repo root:
247
-
248
- ```bash
249
- npm install file:./packages/zindua-js
250
- ```
369
+ | `LIMITS` | Client-side validation limits |
251
370
 
252
371
  ---
253
372
 
package/dist/client.d.ts CHANGED
@@ -17,6 +17,34 @@ export type ZinduaClientOptions = {
17
17
  /** Request timeout (default 30s, max 120s). */
18
18
  timeoutMs?: number;
19
19
  };
20
+ export type ZinduaSendContext = {
21
+ project: {
22
+ id: string;
23
+ name: string;
24
+ slug: string;
25
+ teamId: string;
26
+ };
27
+ apiKey: {
28
+ mode: "live" | "test";
29
+ prefix: string;
30
+ suffix: string;
31
+ };
32
+ plan: {
33
+ slug: string;
34
+ name: string;
35
+ status: string;
36
+ emailApiEnabled: boolean;
37
+ whatsappEnabled: boolean;
38
+ emailQuota: number;
39
+ emailsUsed: number;
40
+ whatsappOtpQuota: number | null;
41
+ whatsappUsed: number;
42
+ } | null;
43
+ channels: {
44
+ email: boolean;
45
+ whatsapp: boolean;
46
+ };
47
+ };
20
48
  export type ZinduaSendResult = {
21
49
  success: true;
22
50
  channel: SendChannel;
@@ -25,6 +53,8 @@ export type ZinduaSendResult = {
25
53
  langUsed?: string;
26
54
  langFallback?: boolean;
27
55
  testMode?: boolean;
56
+ project?: string;
57
+ context?: ZinduaSendContext;
28
58
  };
29
59
  export declare class Zindua {
30
60
  private readonly apiKey;
package/dist/client.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Zindua = void 0;
4
4
  const errors_1 = require("./errors");
5
5
  const validate_1 = require("./validate");
6
- const SDK_VERSION = "1.2.2";
6
+ const SDK_VERSION = "1.2.4";
7
7
  const USER_AGENT = `Zindua-JS/${SDK_VERSION}`;
8
8
  function buildPayload(options, channel) {
9
9
  const to = (0, validate_1.validateRecipient)(options.to, channel);
@@ -46,12 +46,25 @@ function parseApiError(status, body) {
46
46
  const details = {};
47
47
  if (typeof body.code === "string")
48
48
  details.code = body.code;
49
+ if (typeof body.hint === "string")
50
+ details.hint = body.hint;
49
51
  if (typeof body.retryAfterSec === "number")
50
52
  details.retryAfterSec = body.retryAfterSec;
51
- return new errors_1.ZinduaError(message, {
53
+ if (Array.isArray(body.availableTemplateSlugs)) {
54
+ details.availableTemplateSlugs = body.availableTemplateSlugs;
55
+ }
56
+ if (body.context && typeof body.context === "object") {
57
+ details.context = body.context;
58
+ }
59
+ const fullMessage = typeof body.hint === "string" && body.hint.length > 0 ? `${message} ${body.hint}` : message;
60
+ return new errors_1.ZinduaError(fullMessage, {
52
61
  status,
53
62
  code: typeof body.code === "string" ? body.code : "API_ERROR",
54
- details: Object.keys(details).length > 0 ? details : undefined,
63
+ details: Object.keys(details).length > 0
64
+ ? details
65
+ : typeof body.hint === "string"
66
+ ? { hint: body.hint }
67
+ : undefined,
55
68
  });
56
69
  }
57
70
  class Zindua {
@@ -115,6 +128,10 @@ class Zindua {
115
128
  langUsed: typeof data.langUsed === "string" ? data.langUsed : undefined,
116
129
  langFallback: typeof data.langFallback === "boolean" ? data.langFallback : undefined,
117
130
  testMode: typeof data.testMode === "boolean" ? data.testMode : undefined,
131
+ project: typeof data.project === "string" ? data.project : undefined,
132
+ context: data.context && typeof data.context === "object"
133
+ ? data.context
134
+ : undefined,
118
135
  };
119
136
  }
120
137
  catch (err) {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Zindua } from "./client";
2
- export type { ZinduaClientOptions, ZinduaSendOptions, ZinduaSendResult, SendChannel, } from "./client";
2
+ export type { ZinduaClientOptions, ZinduaSendContext, ZinduaSendOptions, ZinduaSendResult, SendChannel, } from "./client";
3
3
  export { ZinduaError } from "./errors";
4
4
  export type { ZinduaErrorCode } from "./errors";
5
5
  export { DEFAULT_API_BASE, LIMITS } from "./validate";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zindua/sdk",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Official Zindua SDK for Node.js — transactional email and WhatsApp via POST /api/v1/send.",
5
5
  "author": "Zindua",
6
6
  "license": "MIT",