@zindua/sdk 1.2.2 → 1.2.3
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 +47 -3
- package/dist/client.d.ts +30 -0
- package/dist/client.js +20 -3
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Send in a locale with the `lang` field (ISO code: `fr`, `en`, `sw`, …). If you
|
|
|
25
25
|
| **Pro** | 50 | 10 |
|
|
26
26
|
| **Team** | 1000 | 100 |
|
|
27
27
|
|
|
28
|
-
On **Free**,
|
|
28
|
+
On **Free**, **email API** works when you connect a **Service** (Gmail, SMTP, etc.) on the project. **WhatsApp OTP** works on Free (quota applies). **Pro/Team** raise template and volume limits. See [pricing](https://zindua.run/pricing).
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
31
|
// French version of the same template slug
|
|
@@ -59,7 +59,7 @@ Before using the SDK, set up your Zindua project in the dashboard:
|
|
|
59
59
|
| Email **Service** connected (Gmail, Outlook, SendGrid, SMTP, …) | `channel: "email"` | Dashboard → **Service** |
|
|
60
60
|
| WhatsApp connected (QR) | `channel: "whatsapp"` | Dashboard → **WhatsApp** |
|
|
61
61
|
| Template slug (e.g. `welcome`, `otp-verification`) | All | Dashboard → **Templates** |
|
|
62
|
-
| **
|
|
62
|
+
| **Service** on Free or **Pro/Team** plan | Email via API when Service is connected (Free) or full quotas (paid) | Dashboard → **Service** / **Billing** |
|
|
63
63
|
|
|
64
64
|
**Runtime**
|
|
65
65
|
|
|
@@ -166,9 +166,51 @@ await zindua.send({
|
|
|
166
166
|
|
|
167
167
|
```typescript
|
|
168
168
|
const result = await zindua.send({ /* … */ });
|
|
169
|
-
// {
|
|
169
|
+
// {
|
|
170
|
+
// success: true,
|
|
171
|
+
// status: "queued",
|
|
172
|
+
// logId: "…",
|
|
173
|
+
// channel: "email",
|
|
174
|
+
// context: { project, apiKey: { mode, suffix }, plan, channels }
|
|
175
|
+
// }
|
|
170
176
|
```
|
|
171
177
|
|
|
178
|
+
The API never returns your full API key. `context.apiKey.suffix` is the last 4 characters (match in Dashboard).
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Several clients (multi-project)
|
|
183
|
+
|
|
184
|
+
**Recommended:** one Zindua project per client → one `znd_live_…` per project.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# .env — server only
|
|
188
|
+
ZINDUA_KEY_MELLIA=znd_live_xxxxxxxxxxxxxxxxxxxxxxxx
|
|
189
|
+
ZINDUA_KEY_OVERLOOK=znd_live_yyyyyyyyyyyyyyyyyyyyyyyy
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { Zindua } from "@zindua/sdk";
|
|
194
|
+
|
|
195
|
+
const zinduaByTenant = {
|
|
196
|
+
mellia: new Zindua({ apiKey: process.env.ZINDUA_KEY_MELLIA! }),
|
|
197
|
+
overlook: new Zindua({ apiKey: process.env.ZINDUA_KEY_OVERLOOK! }),
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
await zinduaByTenant[tenantId].send({
|
|
201
|
+
to: "user@example.com",
|
|
202
|
+
template: "otp",
|
|
203
|
+
variables: { code: "482910" },
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
| Approach | When to use |
|
|
208
|
+
|----------|-------------|
|
|
209
|
+
| **2+ Zindua projects** | Different clients, senders, templates, or quotas |
|
|
210
|
+
| **1 Zindua project** | Same sender and templates for everyone (one key) |
|
|
211
|
+
|
|
212
|
+
More detail: [zindua.run/developers#multi-project](https://zindua.run/developers#multi-project) and repo `config/API-SECURITY-MULTI-PROJECT-FR.md`.
|
|
213
|
+
|
|
172
214
|
---
|
|
173
215
|
|
|
174
216
|
## Errors
|
|
@@ -194,10 +236,12 @@ try {
|
|
|
194
236
|
## Security
|
|
195
237
|
|
|
196
238
|
- **Server only** — throws in browser environments (`BROWSER_FORBIDDEN`).
|
|
239
|
+
- **Bearer header only** — `Authorization: Bearer znd_live_…`. Keys in URL, JSON body, or `X-Api-Key` are rejected by the platform API.
|
|
197
240
|
- **HTTPS** for `baseUrl` except `http://localhost` in development.
|
|
198
241
|
- **No credentials in URL** — rejects `baseUrl` with embedded username/password.
|
|
199
242
|
- **Input validation** — E.164 phones, email format, template slug, variable size limits (aligned with the platform API).
|
|
200
243
|
- **Redirects disabled** — `fetch` uses `redirect: "error"`.
|
|
244
|
+
- **CORS** — optional origin allowlist in Dashboard → Settings → Integrations (browser calls only).
|
|
201
245
|
|
|
202
246
|
---
|
|
203
247
|
|
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.
|
|
6
|
+
const SDK_VERSION = "1.2.3";
|
|
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
|
-
|
|
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
|
|
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";
|