@unsent/sdk 0.25.5 → 1.0.1

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
@@ -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 open source product to send emails</p>",
64
- text: "unsent is the best open source product to send emails",
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
@@ -400,3 +454,9 @@ if (result.data) {
400
454
  ## License
401
455
 
402
456
  MIT
457
+
458
+ ## Support
459
+
460
+ - [Documentation](https://docs.unsent.dev)
461
+ - [GitHub Issues](https://github.com/souravsspace/unsent-typescript/issues)
462
+ - [Discord Community](https://discord.gg/unsent)
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): Promise<{
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): Promise<{
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,
@@ -206,9 +208,9 @@ var Campaigns = class {
206
208
  };
207
209
 
208
210
  // src/unsent.ts
209
- var defaultBaseUrl = "https://app.unsent.dev";
210
- var baseUrl = `${process?.env?.UNSENT_BASE_URL ?? process?.env?.UNSENT_BASE_URL ?? defaultBaseUrl}/api/v1`;
211
- function isunsentErrorResponse(error) {
211
+ var defaultBaseUrl = "https://api.unsent.dev";
212
+ var baseUrl = `${process?.env?.UNSENT_BASE_URL ?? process?.env?.UNSENT_BASE_URL ?? defaultBaseUrl}/v1`;
213
+ function isUnsentErrorResponse(error) {
212
214
  return error.error.code !== void 0;
213
215
  }
214
216
  var unsent = class {
@@ -216,7 +218,7 @@ var unsent = class {
216
218
  this.key = key;
217
219
  if (!key) {
218
220
  if (typeof process !== "undefined" && process.env) {
219
- this.key = process.env.unsent_API_KEY ?? process.env.UNSENT_API_KEY;
221
+ this.key = process.env.UNSENT_API_KEY ?? process.env.UNSENT_API_KEY;
220
222
  }
221
223
  if (!this.key) {
222
224
  throw new Error(
@@ -247,7 +249,7 @@ var unsent = class {
247
249
  if (!response.ok) {
248
250
  try {
249
251
  const resp = await response.json();
250
- if (isunsentErrorResponse(resp)) {
252
+ if (isUnsentErrorResponse(resp)) {
251
253
  return { data: null, error: resp };
252
254
  }
253
255
  return { data: null, error: resp.error };
@@ -264,10 +266,16 @@ var unsent = class {
264
266
  const data = await response.json();
265
267
  return { data, error: null };
266
268
  }
267
- async post(path, body) {
269
+ async post(path, body, options) {
270
+ const headers = new Headers(this.headers);
271
+ if (options?.headers) {
272
+ Object.entries(options.headers).forEach(([key, value]) => {
273
+ headers.set(key, value);
274
+ });
275
+ }
268
276
  const requestOptions = {
269
277
  method: "POST",
270
- headers: this.headers,
278
+ headers,
271
279
  body: JSON.stringify(body)
272
280
  };
273
281
  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,
@@ -179,9 +181,9 @@ var Campaigns = class {
179
181
  };
180
182
 
181
183
  // src/unsent.ts
182
- var defaultBaseUrl = "https://app.unsent.dev";
183
- var baseUrl = `${process?.env?.UNSENT_BASE_URL ?? process?.env?.UNSENT_BASE_URL ?? defaultBaseUrl}/api/v1`;
184
- function isunsentErrorResponse(error) {
184
+ var defaultBaseUrl = "https://api.unsent.dev";
185
+ var baseUrl = `${process?.env?.UNSENT_BASE_URL ?? process?.env?.UNSENT_BASE_URL ?? defaultBaseUrl}/v1`;
186
+ function isUnsentErrorResponse(error) {
185
187
  return error.error.code !== void 0;
186
188
  }
187
189
  var unsent = class {
@@ -189,7 +191,7 @@ var unsent = class {
189
191
  this.key = key;
190
192
  if (!key) {
191
193
  if (typeof process !== "undefined" && process.env) {
192
- this.key = process.env.unsent_API_KEY ?? process.env.UNSENT_API_KEY;
194
+ this.key = process.env.UNSENT_API_KEY ?? process.env.UNSENT_API_KEY;
193
195
  }
194
196
  if (!this.key) {
195
197
  throw new Error(
@@ -220,7 +222,7 @@ var unsent = class {
220
222
  if (!response.ok) {
221
223
  try {
222
224
  const resp = await response.json();
223
- if (isunsentErrorResponse(resp)) {
225
+ if (isUnsentErrorResponse(resp)) {
224
226
  return { data: null, error: resp };
225
227
  }
226
228
  return { data: null, error: resp.error };
@@ -237,10 +239,16 @@ var unsent = class {
237
239
  const data = await response.json();
238
240
  return { data, error: null };
239
241
  }
240
- async post(path, body) {
242
+ async post(path, body, options) {
243
+ const headers = new Headers(this.headers);
244
+ if (options?.headers) {
245
+ Object.entries(options.headers).forEach(([key, value]) => {
246
+ headers.set(key, value);
247
+ });
248
+ }
241
249
  const requestOptions = {
242
250
  method: "POST",
243
- headers: this.headers,
251
+ headers,
244
252
  body: JSON.stringify(body)
245
253
  };
246
254
  return this.fetchRequest(path, requestOptions);
package/package.json CHANGED
@@ -1,20 +1,39 @@
1
1
  {
2
2
  "name": "@unsent/sdk",
3
- "version": "0.25.5",
4
- "description": "",
3
+ "version": "1.0.1",
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",
7
7
  "types": "./dist/index.d.ts",
8
- "keywords": [],
9
- "author": "",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/souravsspace/unsent-typescript.git"
11
+ },
12
+ "homepage": "https://unsent.dev",
13
+ "bugs": {
14
+ "url": "https://github.com/souravsspace/unsent-typescript/issues"
15
+ },
16
+ "keywords": [
17
+ "unsent",
18
+ "email",
19
+ "transactional-email",
20
+ "api",
21
+ "sdk",
22
+ "typescript",
23
+ "react-email"
24
+ ],
25
+ "author": "sourav",
10
26
  "license": "MIT",
27
+ "files": [
28
+ "dist"
29
+ ],
11
30
  "devDependencies": {
12
31
  "@types/node": "^24.7.0",
13
32
  "@types/react": "^19.1.2",
14
33
  "openapi-typescript": "^7.6.1",
15
34
  "tsup": "^8.4.0",
16
35
  "typescript": "^5.8.3",
17
- "@workspace/typescript-config": "0.0.0"
36
+ "@unsent/typescript-config": "0.0.0"
18
37
  },
19
38
  "dependencies": {
20
39
  "@react-email/render": "^1.0.6",