@unsent/sdk 1.0.0 → 1.0.2
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 +59 -5
- package/dist/index.d.mts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +35 -13
- package/dist/index.mjs +35 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ bun add @unsent/sdk
|
|
|
38
38
|
```typescript
|
|
39
39
|
import { unsent } from "@unsent/sdk";
|
|
40
40
|
|
|
41
|
-
const client = new unsent("
|
|
41
|
+
const client = new unsent("un_xxxx");
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
### Environment Variables
|
|
@@ -60,8 +60,8 @@ const { data, error } = await client.emails.send({
|
|
|
60
60
|
to: "hello@acme.com",
|
|
61
61
|
from: "hello@company.com",
|
|
62
62
|
subject: "unsent email",
|
|
63
|
-
html: "<p>unsent is the best
|
|
64
|
-
text: "unsent is the best
|
|
63
|
+
html: "<p>unsent is the best email service provider to send emails</p>",
|
|
64
|
+
text: "unsent is the best email service provider to send emails",
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
if (error) {
|
|
@@ -143,6 +143,60 @@ if (error) {
|
|
|
143
143
|
}
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
+
#### Idempotency Keys
|
|
147
|
+
|
|
148
|
+
Safely retry email sends with an idempotency key to prevent duplicate sends. This is especially useful for critical emails like signup confirmations or password resets.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Safely retry sends with an idempotency key
|
|
152
|
+
const { data, error } = await client.emails.send(
|
|
153
|
+
{
|
|
154
|
+
to: "hello@acme.com",
|
|
155
|
+
from: "hello@company.com",
|
|
156
|
+
subject: "unsent email",
|
|
157
|
+
html: "<p>unsent is the best open source product to send emails</p>",
|
|
158
|
+
},
|
|
159
|
+
{ idempotencyKey: "signup-123" },
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (error) {
|
|
163
|
+
console.error("Error:", error);
|
|
164
|
+
} else {
|
|
165
|
+
console.log("Email sent! ID:", data.id);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Idempotency keys also work for batch sends:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Works for bulk sends too
|
|
173
|
+
const { data, error } = await client.emails.batch(
|
|
174
|
+
[
|
|
175
|
+
{
|
|
176
|
+
to: "a@example.com",
|
|
177
|
+
from: "hello@company.com",
|
|
178
|
+
subject: "Welcome",
|
|
179
|
+
html: "<p>Hello A</p>",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
to: "b@example.com",
|
|
183
|
+
from: "hello@company.com",
|
|
184
|
+
subject: "Welcome",
|
|
185
|
+
html: "<p>Hello B</p>",
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
{ idempotencyKey: "bulk-welcome-1" },
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (error) {
|
|
192
|
+
console.error("Error:", error);
|
|
193
|
+
} else {
|
|
194
|
+
console.log(`Sent ${data.length} emails`);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
> **Note:** Reusing the same idempotency key with a different payload will return HTTP 409 (Conflict). This ensures that retries are safe and prevents accidental duplicate sends with modified content.
|
|
199
|
+
|
|
146
200
|
### Managing Emails
|
|
147
201
|
|
|
148
202
|
#### Get Email Details
|
|
@@ -249,7 +303,7 @@ Create and manage email campaigns:
|
|
|
249
303
|
```typescript
|
|
250
304
|
import { unsent } from "@unsent/sdk";
|
|
251
305
|
|
|
252
|
-
const client = new unsent("
|
|
306
|
+
const client = new unsent("un_xxxx");
|
|
253
307
|
|
|
254
308
|
// Create a campaign
|
|
255
309
|
const campaign = await client.campaigns.create({
|
|
@@ -336,7 +390,7 @@ The SDK is fully typed with TypeScript:
|
|
|
336
390
|
```typescript
|
|
337
391
|
import { unsent } from "@unsent/sdk";
|
|
338
392
|
|
|
339
|
-
const client = new unsent("
|
|
393
|
+
const client = new unsent("un_xxxx");
|
|
340
394
|
|
|
341
395
|
// Full type inference and autocomplete
|
|
342
396
|
const result = await client.emails.send({
|
package/dist/index.d.mts
CHANGED
|
@@ -1312,6 +1312,12 @@ type BatchEmailPayload = paths["/v1/emails/batch"]["post"]["requestBody"]["conte
|
|
|
1312
1312
|
* Successful response schema for batch email send.
|
|
1313
1313
|
*/
|
|
1314
1314
|
type BatchEmailResponseSuccess = paths["/v1/emails/batch"]["post"]["responses"]["200"]["content"]["application/json"];
|
|
1315
|
+
/**
|
|
1316
|
+
* Options for email requests.
|
|
1317
|
+
*/
|
|
1318
|
+
type EmailRequestOptions = {
|
|
1319
|
+
idempotencyKey?: string;
|
|
1320
|
+
};
|
|
1315
1321
|
/**
|
|
1316
1322
|
* Response structure for the batch method.
|
|
1317
1323
|
*/
|
|
@@ -1322,15 +1328,15 @@ type BatchEmailResponse = {
|
|
|
1322
1328
|
declare class Emails {
|
|
1323
1329
|
private readonly unsent;
|
|
1324
1330
|
constructor(unsent: unsent);
|
|
1325
|
-
send(payload: SendEmailPayload): Promise<CreateEmailResponse>;
|
|
1326
|
-
create(payload: SendEmailPayload): Promise<CreateEmailResponse>;
|
|
1331
|
+
send(payload: SendEmailPayload, options?: EmailRequestOptions): Promise<CreateEmailResponse>;
|
|
1332
|
+
create(payload: SendEmailPayload, options?: EmailRequestOptions): Promise<CreateEmailResponse>;
|
|
1327
1333
|
/**
|
|
1328
1334
|
* Send up to 100 emails in a single request.
|
|
1329
1335
|
*
|
|
1330
1336
|
* @param payload An array of email payloads. Max 100 emails.
|
|
1331
1337
|
* @returns A promise that resolves to the list of created email IDs or an error.
|
|
1332
1338
|
*/
|
|
1333
|
-
batch(payload: BatchEmailPayload): Promise<BatchEmailResponse>;
|
|
1339
|
+
batch(payload: BatchEmailPayload, options?: EmailRequestOptions): Promise<BatchEmailResponse>;
|
|
1334
1340
|
get(id: string): Promise<GetEmailResponse>;
|
|
1335
1341
|
update(id: string, payload: UpdateEmailPayload): Promise<UpdateEmailResponse>;
|
|
1336
1342
|
cancel(id: string): Promise<CancelEmailResponse>;
|
|
@@ -1419,7 +1425,9 @@ declare class unsent {
|
|
|
1419
1425
|
data: T | null;
|
|
1420
1426
|
error: ErrorResponse | null;
|
|
1421
1427
|
}>;
|
|
1422
|
-
post<T>(path: string, body: unknown
|
|
1428
|
+
post<T>(path: string, body: unknown, options?: {
|
|
1429
|
+
headers?: Record<string, string>;
|
|
1430
|
+
}): Promise<{
|
|
1423
1431
|
data: T | null;
|
|
1424
1432
|
error: ErrorResponse | null;
|
|
1425
1433
|
}>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1312,6 +1312,12 @@ type BatchEmailPayload = paths["/v1/emails/batch"]["post"]["requestBody"]["conte
|
|
|
1312
1312
|
* Successful response schema for batch email send.
|
|
1313
1313
|
*/
|
|
1314
1314
|
type BatchEmailResponseSuccess = paths["/v1/emails/batch"]["post"]["responses"]["200"]["content"]["application/json"];
|
|
1315
|
+
/**
|
|
1316
|
+
* Options for email requests.
|
|
1317
|
+
*/
|
|
1318
|
+
type EmailRequestOptions = {
|
|
1319
|
+
idempotencyKey?: string;
|
|
1320
|
+
};
|
|
1315
1321
|
/**
|
|
1316
1322
|
* Response structure for the batch method.
|
|
1317
1323
|
*/
|
|
@@ -1322,15 +1328,15 @@ type BatchEmailResponse = {
|
|
|
1322
1328
|
declare class Emails {
|
|
1323
1329
|
private readonly unsent;
|
|
1324
1330
|
constructor(unsent: unsent);
|
|
1325
|
-
send(payload: SendEmailPayload): Promise<CreateEmailResponse>;
|
|
1326
|
-
create(payload: SendEmailPayload): Promise<CreateEmailResponse>;
|
|
1331
|
+
send(payload: SendEmailPayload, options?: EmailRequestOptions): Promise<CreateEmailResponse>;
|
|
1332
|
+
create(payload: SendEmailPayload, options?: EmailRequestOptions): Promise<CreateEmailResponse>;
|
|
1327
1333
|
/**
|
|
1328
1334
|
* Send up to 100 emails in a single request.
|
|
1329
1335
|
*
|
|
1330
1336
|
* @param payload An array of email payloads. Max 100 emails.
|
|
1331
1337
|
* @returns A promise that resolves to the list of created email IDs or an error.
|
|
1332
1338
|
*/
|
|
1333
|
-
batch(payload: BatchEmailPayload): Promise<BatchEmailResponse>;
|
|
1339
|
+
batch(payload: BatchEmailPayload, options?: EmailRequestOptions): Promise<BatchEmailResponse>;
|
|
1334
1340
|
get(id: string): Promise<GetEmailResponse>;
|
|
1335
1341
|
update(id: string, payload: UpdateEmailPayload): Promise<UpdateEmailResponse>;
|
|
1336
1342
|
cancel(id: string): Promise<CancelEmailResponse>;
|
|
@@ -1419,7 +1425,9 @@ declare class unsent {
|
|
|
1419
1425
|
data: T | null;
|
|
1420
1426
|
error: ErrorResponse | null;
|
|
1421
1427
|
}>;
|
|
1422
|
-
post<T>(path: string, body: unknown
|
|
1428
|
+
post<T>(path: string, body: unknown, options?: {
|
|
1429
|
+
headers?: Record<string, string>;
|
|
1430
|
+
}): Promise<{
|
|
1423
1431
|
data: T | null;
|
|
1424
1432
|
error: ErrorResponse | null;
|
|
1425
1433
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -73,17 +73,18 @@ var Emails = class {
|
|
|
73
73
|
this.unsent = unsent2;
|
|
74
74
|
this.unsent = unsent2;
|
|
75
75
|
}
|
|
76
|
-
async send(payload) {
|
|
77
|
-
return this.create(payload);
|
|
76
|
+
async send(payload, options) {
|
|
77
|
+
return this.create(payload, options);
|
|
78
78
|
}
|
|
79
|
-
async create(payload) {
|
|
79
|
+
async create(payload, options) {
|
|
80
80
|
if (payload.react) {
|
|
81
81
|
payload.html = await (0, import_render.render)(payload.react);
|
|
82
82
|
delete payload.react;
|
|
83
83
|
}
|
|
84
84
|
const data = await this.unsent.post(
|
|
85
85
|
"/emails",
|
|
86
|
-
payload
|
|
86
|
+
payload,
|
|
87
|
+
options?.idempotencyKey ? { headers: { "Idempotency-Key": options.idempotencyKey } } : void 0
|
|
87
88
|
);
|
|
88
89
|
return data;
|
|
89
90
|
}
|
|
@@ -93,10 +94,11 @@ var Emails = class {
|
|
|
93
94
|
* @param payload An array of email payloads. Max 100 emails.
|
|
94
95
|
* @returns A promise that resolves to the list of created email IDs or an error.
|
|
95
96
|
*/
|
|
96
|
-
async batch(payload) {
|
|
97
|
+
async batch(payload, options) {
|
|
97
98
|
const response = await this.unsent.post(
|
|
98
99
|
"/emails/batch",
|
|
99
|
-
payload
|
|
100
|
+
payload,
|
|
101
|
+
options?.idempotencyKey ? { headers: { "Idempotency-Key": options.idempotencyKey } } : void 0
|
|
100
102
|
);
|
|
101
103
|
return {
|
|
102
104
|
data: response.data ? response.data.data : null,
|
|
@@ -220,12 +222,12 @@ var unsent = class {
|
|
|
220
222
|
}
|
|
221
223
|
if (!this.key) {
|
|
222
224
|
throw new Error(
|
|
223
|
-
'Missing API key. Pass it to the constructor `new unsent("
|
|
225
|
+
'Missing API key. Pass it to the constructor `new unsent("un_xxxx")`'
|
|
224
226
|
);
|
|
225
227
|
}
|
|
226
228
|
}
|
|
227
229
|
if (url) {
|
|
228
|
-
this.url = `${url}/
|
|
230
|
+
this.url = `${url}/v1`;
|
|
229
231
|
}
|
|
230
232
|
this.headers = new Headers({
|
|
231
233
|
Authorization: `Bearer ${this.key}`,
|
|
@@ -239,7 +241,8 @@ var unsent = class {
|
|
|
239
241
|
campaigns = new Campaigns(this);
|
|
240
242
|
url = baseUrl;
|
|
241
243
|
async fetchRequest(path, options = {}) {
|
|
242
|
-
const
|
|
244
|
+
const fullUrl = `${this.url}${path}`;
|
|
245
|
+
const response = await fetch(fullUrl, options);
|
|
243
246
|
const defaultError = {
|
|
244
247
|
code: "INTERNAL_SERVER_ERROR",
|
|
245
248
|
message: response.statusText
|
|
@@ -261,13 +264,32 @@ var unsent = class {
|
|
|
261
264
|
return { data: null, error: defaultError };
|
|
262
265
|
}
|
|
263
266
|
}
|
|
264
|
-
|
|
265
|
-
|
|
267
|
+
try {
|
|
268
|
+
const data = await response.json();
|
|
269
|
+
return { data, error: null };
|
|
270
|
+
} catch (err) {
|
|
271
|
+
if (err instanceof Error && err.message.includes("JSON")) {
|
|
272
|
+
return {
|
|
273
|
+
data: null,
|
|
274
|
+
error: {
|
|
275
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
276
|
+
message: "Invalid JSON response from server"
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
266
282
|
}
|
|
267
|
-
async post(path, body) {
|
|
283
|
+
async post(path, body, options) {
|
|
284
|
+
const headers = new Headers(this.headers);
|
|
285
|
+
if (options?.headers) {
|
|
286
|
+
Object.entries(options.headers).forEach(([key, value]) => {
|
|
287
|
+
headers.set(key, value);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
268
290
|
const requestOptions = {
|
|
269
291
|
method: "POST",
|
|
270
|
-
headers
|
|
292
|
+
headers,
|
|
271
293
|
body: JSON.stringify(body)
|
|
272
294
|
};
|
|
273
295
|
return this.fetchRequest(path, requestOptions);
|
package/dist/index.mjs
CHANGED
|
@@ -46,17 +46,18 @@ var Emails = class {
|
|
|
46
46
|
this.unsent = unsent2;
|
|
47
47
|
this.unsent = unsent2;
|
|
48
48
|
}
|
|
49
|
-
async send(payload) {
|
|
50
|
-
return this.create(payload);
|
|
49
|
+
async send(payload, options) {
|
|
50
|
+
return this.create(payload, options);
|
|
51
51
|
}
|
|
52
|
-
async create(payload) {
|
|
52
|
+
async create(payload, options) {
|
|
53
53
|
if (payload.react) {
|
|
54
54
|
payload.html = await render(payload.react);
|
|
55
55
|
delete payload.react;
|
|
56
56
|
}
|
|
57
57
|
const data = await this.unsent.post(
|
|
58
58
|
"/emails",
|
|
59
|
-
payload
|
|
59
|
+
payload,
|
|
60
|
+
options?.idempotencyKey ? { headers: { "Idempotency-Key": options.idempotencyKey } } : void 0
|
|
60
61
|
);
|
|
61
62
|
return data;
|
|
62
63
|
}
|
|
@@ -66,10 +67,11 @@ var Emails = class {
|
|
|
66
67
|
* @param payload An array of email payloads. Max 100 emails.
|
|
67
68
|
* @returns A promise that resolves to the list of created email IDs or an error.
|
|
68
69
|
*/
|
|
69
|
-
async batch(payload) {
|
|
70
|
+
async batch(payload, options) {
|
|
70
71
|
const response = await this.unsent.post(
|
|
71
72
|
"/emails/batch",
|
|
72
|
-
payload
|
|
73
|
+
payload,
|
|
74
|
+
options?.idempotencyKey ? { headers: { "Idempotency-Key": options.idempotencyKey } } : void 0
|
|
73
75
|
);
|
|
74
76
|
return {
|
|
75
77
|
data: response.data ? response.data.data : null,
|
|
@@ -193,12 +195,12 @@ var unsent = class {
|
|
|
193
195
|
}
|
|
194
196
|
if (!this.key) {
|
|
195
197
|
throw new Error(
|
|
196
|
-
'Missing API key. Pass it to the constructor `new unsent("
|
|
198
|
+
'Missing API key. Pass it to the constructor `new unsent("un_xxxx")`'
|
|
197
199
|
);
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
if (url) {
|
|
201
|
-
this.url = `${url}/
|
|
203
|
+
this.url = `${url}/v1`;
|
|
202
204
|
}
|
|
203
205
|
this.headers = new Headers({
|
|
204
206
|
Authorization: `Bearer ${this.key}`,
|
|
@@ -212,7 +214,8 @@ var unsent = class {
|
|
|
212
214
|
campaigns = new Campaigns(this);
|
|
213
215
|
url = baseUrl;
|
|
214
216
|
async fetchRequest(path, options = {}) {
|
|
215
|
-
const
|
|
217
|
+
const fullUrl = `${this.url}${path}`;
|
|
218
|
+
const response = await fetch(fullUrl, options);
|
|
216
219
|
const defaultError = {
|
|
217
220
|
code: "INTERNAL_SERVER_ERROR",
|
|
218
221
|
message: response.statusText
|
|
@@ -234,13 +237,32 @@ var unsent = class {
|
|
|
234
237
|
return { data: null, error: defaultError };
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
try {
|
|
241
|
+
const data = await response.json();
|
|
242
|
+
return { data, error: null };
|
|
243
|
+
} catch (err) {
|
|
244
|
+
if (err instanceof Error && err.message.includes("JSON")) {
|
|
245
|
+
return {
|
|
246
|
+
data: null,
|
|
247
|
+
error: {
|
|
248
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
249
|
+
message: "Invalid JSON response from server"
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
239
255
|
}
|
|
240
|
-
async post(path, body) {
|
|
256
|
+
async post(path, body, options) {
|
|
257
|
+
const headers = new Headers(this.headers);
|
|
258
|
+
if (options?.headers) {
|
|
259
|
+
Object.entries(options.headers).forEach(([key, value]) => {
|
|
260
|
+
headers.set(key, value);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
241
263
|
const requestOptions = {
|
|
242
264
|
method: "POST",
|
|
243
|
-
headers
|
|
265
|
+
headers,
|
|
244
266
|
body: JSON.stringify(body)
|
|
245
267
|
};
|
|
246
268
|
return this.fetchRequest(path, requestOptions);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unsent/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "TypeScript SDK for the Unsent API - Send transactional emails with ease",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@types/node": "^24.7.0",
|
|
32
32
|
"@types/react": "^19.1.2",
|
|
33
33
|
"openapi-typescript": "^7.6.1",
|
|
34
|
-
"tsup": "^8.
|
|
34
|
+
"tsup": "^8.5.0",
|
|
35
35
|
"typescript": "^5.8.3",
|
|
36
36
|
"@unsent/typescript-config": "0.0.0"
|
|
37
37
|
},
|