@zindua/sdk 1.2.3 → 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 +247 -172
- package/dist/client.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,261 +1,350 @@
|
|
|
1
1
|
# @zindua/sdk
|
|
2
2
|
|
|
3
|
-
Official **server-side** SDK for [Zindua](https://zindua.run):
|
|
3
|
+
Official **server-side** SDK for [Zindua](https://zindua.run): send transactional **email** and **WhatsApp** messages with one API.
|
|
4
4
|
|
|
5
|
-
|
|
|
6
|
-
|
|
7
|
-
|
|
|
8
|
-
|
|
|
9
|
-
| 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
|
-
|
|
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
|
-
##
|
|
19
|
+
## Quick start
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
### 1. Create a Zindua project
|
|
19
22
|
|
|
20
|
-
|
|
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`).
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
### 2. Install and configure
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @zindua/sdk
|
|
33
|
+
```
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
```bash
|
|
36
|
+
# .env — never commit real keys; never expose in frontend code
|
|
37
|
+
ZINDUA_API_KEY=znd_live_xxxxxxxxxxxxxxxxxxxxxxxx
|
|
38
|
+
```
|
|
29
39
|
|
|
30
40
|
```typescript
|
|
31
|
-
|
|
41
|
+
import { Zindua } from "@zindua/sdk";
|
|
42
|
+
|
|
43
|
+
export const zindua = new Zindua({
|
|
44
|
+
apiKey: process.env.ZINDUA_API_KEY!,
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. Send a message
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
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
|
|
58
|
+
// WhatsApp — phone in E.164 with +
|
|
40
59
|
await zindua.send({
|
|
41
60
|
to: "+243812345678",
|
|
42
61
|
channel: "whatsapp",
|
|
43
|
-
template: "otp
|
|
44
|
-
|
|
45
|
-
variables: { code: "4592", app: "MonApp" },
|
|
62
|
+
template: "otp",
|
|
63
|
+
variables: { code: "482910" },
|
|
46
64
|
});
|
|
47
65
|
```
|
|
48
66
|
|
|
49
67
|
---
|
|
50
68
|
|
|
51
|
-
##
|
|
69
|
+
## Plans and channels
|
|
52
70
|
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
| **Service** on Free or **Pro/Team** plan | Email via API when Service is connected (Free) or full quotas (paid) | Dashboard → **Service** / **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
|
-
|
|
79
|
+
| | Free | Pro / Team |
|
|
80
|
+
|---|------|------------|
|
|
81
|
+
| Templates per project | 5 | 50+ |
|
|
82
|
+
| Languages per template | 3 | 10+ |
|
|
65
83
|
|
|
66
|
-
|
|
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
|
-
##
|
|
88
|
+
## One project or several?
|
|
72
89
|
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
78
|
-
yarn add @zindua/sdk
|
|
79
|
-
```
|
|
95
|
+
### Several keys in one backend
|
|
80
96
|
|
|
81
97
|
```bash
|
|
82
|
-
|
|
98
|
+
ZINDUA_KEY_CLIENT_A=znd_live_aaaaaaaaaaaaaaaaaaaaaaaa
|
|
99
|
+
ZINDUA_KEY_CLIENT_B=znd_live_bbbbbbbbbbbbbbbbbbbbbbbb
|
|
83
100
|
```
|
|
84
101
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
## Configure
|
|
88
|
-
|
|
89
|
-
### 1. API key
|
|
102
|
+
```typescript
|
|
103
|
+
import { Zindua } from "@zindua/sdk";
|
|
90
104
|
|
|
91
|
-
|
|
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
|
+
};
|
|
92
109
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
await clients.clientA.send({
|
|
111
|
+
to: "user@example.com",
|
|
112
|
+
template: "otp",
|
|
113
|
+
variables: { code: "123456" },
|
|
114
|
+
});
|
|
96
115
|
```
|
|
97
116
|
|
|
98
|
-
|
|
117
|
+
Each key only accesses **its** Zindua project (templates, service, usage).
|
|
99
118
|
|
|
100
|
-
|
|
101
|
-
|
|
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) |
|
|
119
|
+
---
|
|
106
120
|
|
|
107
|
-
|
|
121
|
+
## Framework examples
|
|
108
122
|
|
|
109
|
-
|
|
110
|
-
|--------|---------|-----|
|
|
111
|
-
| `timeoutMs` | `30000` | `120000` |
|
|
123
|
+
Use the same pattern everywhere: **your server** holds the API key; the client app calls **your** API.
|
|
112
124
|
|
|
113
|
-
###
|
|
125
|
+
### Next.js (App Router)
|
|
114
126
|
|
|
115
127
|
```typescript
|
|
128
|
+
// lib/zindua.ts
|
|
116
129
|
import { Zindua } from "@zindua/sdk";
|
|
117
130
|
|
|
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,
|
|
122
|
-
});
|
|
131
|
+
export const zindua = new Zindua({ apiKey: process.env.ZINDUA_API_KEY! });
|
|
123
132
|
```
|
|
124
133
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
```
|
|
128
149
|
|
|
129
|
-
###
|
|
150
|
+
### Express
|
|
130
151
|
|
|
131
152
|
```typescript
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
import express from "express";
|
|
154
|
+
import { Zindua } from "@zindua/sdk";
|
|
155
|
+
|
|
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
|
+
}
|
|
136
172
|
});
|
|
137
173
|
```
|
|
138
174
|
|
|
139
|
-
###
|
|
140
|
-
|
|
141
|
-
Phone numbers must be **E.164** with a leading `+` (e.g. `+243812345678`).
|
|
175
|
+
### Fastify
|
|
142
176
|
|
|
143
177
|
```typescript
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
178
|
+
import Fastify from "fastify";
|
|
179
|
+
import { Zindua } from "@zindua/sdk";
|
|
180
|
+
|
|
181
|
+
const zindua = new Zindua({ apiKey: process.env.ZINDUA_API_KEY! });
|
|
182
|
+
const app = Fastify();
|
|
183
|
+
|
|
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);
|
|
149
191
|
});
|
|
150
192
|
```
|
|
151
193
|
|
|
152
|
-
###
|
|
194
|
+
### Hono
|
|
153
195
|
|
|
154
196
|
```typescript
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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);
|
|
162
211
|
});
|
|
163
212
|
```
|
|
164
213
|
|
|
165
|
-
###
|
|
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`). |
|
|
166
227
|
|
|
167
228
|
```typescript
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// channel: "email",
|
|
174
|
-
// context: { project, apiKey: { mode, suffix }, plan, channels }
|
|
175
|
-
// }
|
|
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,
|
|
233
|
+
});
|
|
176
234
|
```
|
|
177
235
|
|
|
178
|
-
The API never returns your full API key. `context.apiKey.suffix` is the last 4 characters (match in Dashboard).
|
|
179
|
-
|
|
180
236
|
---
|
|
181
237
|
|
|
182
|
-
##
|
|
238
|
+
## Send options
|
|
183
239
|
|
|
184
|
-
|
|
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. |
|
|
185
248
|
|
|
186
|
-
|
|
187
|
-
# .env — server only
|
|
188
|
-
ZINDUA_KEY_MELLIA=znd_live_xxxxxxxxxxxxxxxxxxxxxxxx
|
|
189
|
-
ZINDUA_KEY_OVERLOOK=znd_live_yyyyyyyyyyyyyyyyyyyyyyyy
|
|
190
|
-
```
|
|
249
|
+
---
|
|
191
250
|
|
|
192
|
-
|
|
193
|
-
import { Zindua } from "@zindua/sdk";
|
|
251
|
+
## Successful response
|
|
194
252
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
};
|
|
253
|
+
```typescript
|
|
254
|
+
const result = await zindua.send({ /* … */ });
|
|
255
|
+
```
|
|
199
256
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
+
}
|
|
205
278
|
```
|
|
206
279
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
| **1 Zindua project** | Same sender and templates for everyone (one key) |
|
|
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.
|
|
211
283
|
|
|
212
|
-
|
|
284
|
+
The full API key is **never** returned in responses.
|
|
213
285
|
|
|
214
286
|
---
|
|
215
287
|
|
|
216
288
|
## Errors
|
|
217
289
|
|
|
290
|
+
Wrap sends in `try/catch` and handle `ZinduaError`:
|
|
291
|
+
|
|
218
292
|
```typescript
|
|
219
293
|
import { Zindua, ZinduaError } from "@zindua/sdk";
|
|
220
294
|
|
|
221
295
|
try {
|
|
222
|
-
await zindua.send({ to: "
|
|
296
|
+
await zindua.send({ to: "user@example.com", template: "otp", variables: { code: "1" } });
|
|
223
297
|
} catch (e) {
|
|
224
298
|
if (e instanceof ZinduaError) {
|
|
225
|
-
console.
|
|
226
|
-
//
|
|
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, …
|
|
227
303
|
}
|
|
228
|
-
throw e;
|
|
229
304
|
}
|
|
230
305
|
```
|
|
231
306
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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.
|
|
245
334
|
|
|
246
335
|
---
|
|
247
336
|
|
|
248
|
-
##
|
|
249
|
-
|
|
250
|
-
Never put `znd_live_` keys in React Native, Flutter, or browser code. Call your own backend; your backend uses this SDK.
|
|
337
|
+
## Best practices
|
|
251
338
|
|
|
252
|
-
|
|
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.
|
|
253
344
|
|
|
254
345
|
---
|
|
255
346
|
|
|
256
|
-
##
|
|
257
|
-
|
|
258
|
-
Any language can call the same endpoint:
|
|
347
|
+
## Without the SDK (HTTP)
|
|
259
348
|
|
|
260
349
|
```bash
|
|
261
350
|
curl -X POST https://zindua.run/api/v1/send \
|
|
@@ -264,34 +353,20 @@ curl -X POST https://zindua.run/api/v1/send \
|
|
|
264
353
|
-d '{"to":"user@example.com","template":"welcome","variables":{"name":"Alex"}}'
|
|
265
354
|
```
|
|
266
355
|
|
|
267
|
-
|
|
356
|
+
See [HTTP / cURL](https://zindua.run/developers#http).
|
|
268
357
|
|
|
269
358
|
---
|
|
270
359
|
|
|
271
|
-
## API
|
|
360
|
+
## API reference
|
|
272
361
|
|
|
273
362
|
| Export | Description |
|
|
274
363
|
|--------|-------------|
|
|
275
364
|
| `Zindua` | Client class |
|
|
276
|
-
| `ZinduaError` |
|
|
365
|
+
| `ZinduaError` | Error type (`status`, `code`, `message`, `details`) |
|
|
366
|
+
| `ZinduaSendResult` | Success payload type |
|
|
367
|
+
| `ZinduaSendContext` | Type of `result.context` |
|
|
277
368
|
| `DEFAULT_API_BASE` | `https://zindua.run/api/v1` |
|
|
278
|
-
| `LIMITS` |
|
|
279
|
-
|
|
280
|
-
---
|
|
281
|
-
|
|
282
|
-
## Local development (monorepo)
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
cd packages/zindua-js
|
|
286
|
-
npm run build
|
|
287
|
-
npm test
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
From the app repo root:
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
npm install file:./packages/zindua-js
|
|
294
|
-
```
|
|
369
|
+
| `LIMITS` | Client-side validation limits |
|
|
295
370
|
|
|
296
371
|
---
|
|
297
372
|
|
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.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);
|