@notifykit/sdk 1.0.0 → 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
@@ -1,6 +1,6 @@
1
1
  # NotifyKit SDK
2
2
 
3
- Official Node.js/TypeScript SDK for NotifyKit - Send emails and webhooks with ease.
3
+ Official Node.js/TypeScript SDK for NotifyKit send emails and webhooks with automatic retries and delivery tracking.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,20 +8,29 @@ Official Node.js/TypeScript SDK for NotifyKit - Send emails and webhooks with ea
8
8
  npm install @notifykit/sdk
9
9
  ```
10
10
 
11
+ **Requirements:** Node.js 18+
12
+
11
13
  ## Quick Start
12
14
 
13
15
  ```typescript
14
16
  import { NotifyKitClient } from "@notifykit/sdk";
15
17
 
16
- const notifyKit = new NotifyKitClient({
17
- apiKey: "ntfy_sk_your_api_key",
18
+ const client = new NotifyKitClient({
19
+ apiKey: process.env.NOTIFYKIT_API_KEY!, // nh_...
18
20
  });
19
21
 
20
22
  // Send an email
21
- await notifyKit.sendEmail({
23
+ const emailJob = await client.sendEmail({
22
24
  to: "user@example.com",
23
25
  subject: "Welcome!",
24
26
  body: "<h1>Hello World</h1>",
27
+ idempotencyKey: "welcome-user-123",
28
+ });
29
+
30
+ // Send a webhook
31
+ const webhookJob = await client.sendWebhook({
32
+ url: "https://your-app.com/webhooks/events",
33
+ payload: { event: "user.created", userId: "123" },
25
34
  });
26
35
  ```
27
36
 
@@ -29,33 +38,46 @@ await notifyKit.sendEmail({
29
38
 
30
39
  ## Sending Emails
31
40
 
32
- **Important:** Emails are queued immediately (HTTP 202 Accepted) and delivered asynchronously. Check job status to confirm delivery.
41
+ Emails are queued immediately (HTTP 202 Accepted) and delivered asynchronously. Use [getJob](#tracking-jobs) to confirm delivery.
42
+
43
+ > **Note:** On the **Free plan**, emails send from NotifyKit's shared SendGrid account (`noreply@notifykit.dev`). On **Indie/Startup plans**, connect your own SendGrid API key in the dashboard before sending emails.
33
44
 
34
45
  ### Basic Email
35
46
 
36
47
  ```typescript
37
- await notifyKit.sendEmail({
48
+ await client.sendEmail({
38
49
  to: "user@example.com",
39
50
  subject: "Order Confirmed",
40
51
  body: "<h1>Thanks for your order!</h1>",
41
52
  });
42
53
  ```
43
54
 
44
- ### Custom From Address
55
+ ### Custom From Address (Paid Plans)
45
56
 
46
57
  ```typescript
47
- await notifyKit.sendEmail({
58
+ await client.sendEmail({
48
59
  to: "user@example.com",
49
60
  subject: "Welcome",
50
61
  body: "<h1>Hello</h1>",
51
- from: "hello@yourapp.com", // Must be a verified domain
62
+ from: "hello@em.yourapp.com", // Must be a verified domain
63
+ });
64
+ ```
65
+
66
+ ### High Priority
67
+
68
+ ```typescript
69
+ await client.sendEmail({
70
+ to: "user@example.com",
71
+ subject: "Password Reset",
72
+ body: "<p>Click here to reset your password.</p>",
73
+ priority: 1, // 1=high, 5=normal (default), 10=low
52
74
  });
53
75
  ```
54
76
 
55
- ### Prevent Duplicate Sends (Recommended)
77
+ ### Prevent Duplicate Sends
56
78
 
57
79
  ```typescript
58
- await notifyKit.sendEmail({
80
+ await client.sendEmail({
59
81
  to: "user@example.com",
60
82
  subject: "Welcome",
61
83
  body: "<h1>Hello</h1>",
@@ -65,20 +87,20 @@ await notifyKit.sendEmail({
65
87
 
66
88
  **How idempotency works:**
67
89
 
68
- - Same key → Request rejected with 409 Conflict, original job returned
90
+ - Same key → Request rejected with `409 Conflict`, original job returned
69
91
  - Different key → New email sent
70
- - No key → Always sends (not recommended for production)
92
+ - No key → Always sends (not recommended for critical transactional emails)
71
93
 
72
94
  ---
73
95
 
74
96
  ## Sending Webhooks
75
97
 
76
- **Important:** Like emails, webhooks are queued immediately (HTTP 202 Accepted) and delivered asynchronously. Check job status to confirm delivery.
98
+ Webhooks are queued immediately (HTTP 202 Accepted) and delivered asynchronously with automatic retries on failure.
77
99
 
78
100
  ### Basic Webhook
79
101
 
80
102
  ```typescript
81
- await notifyKit.sendWebhook({
103
+ await client.sendWebhook({
82
104
  url: "https://yourapp.com/webhooks/order",
83
105
  payload: {
84
106
  orderId: "12345",
@@ -90,61 +112,64 @@ await notifyKit.sendWebhook({
90
112
  ### With Custom Headers and Method
91
113
 
92
114
  ```typescript
93
- await notifyKit.sendWebhook({
115
+ await client.sendWebhook({
94
116
  url: "https://yourapp.com/webhooks/order",
95
- method: "POST", // GET, POST, PUT, PATCH, DELETE(default is POST)
117
+ method: "POST", // GET, POST, PUT, PATCH, DELETEdefault is POST
96
118
  payload: { orderId: "12345" },
97
119
  headers: {
120
+ "X-Webhook-Secret": process.env.WEBHOOK_SECRET!,
98
121
  "X-Event-Type": "order.created",
99
- "X-Signature": "abc123",
100
122
  },
101
123
  idempotencyKey: "order-12345-webhook",
102
124
  });
103
125
  ```
104
126
 
127
+ **Retry behavior:**
128
+
129
+ - Max 3 attempts, exponential backoff (~2s, ~4s, ~8s)
130
+ - Retried on 5xx errors, network failures, timeouts
131
+ - Not retried on 4xx errors
132
+
105
133
  ---
106
134
 
107
135
  ## Tracking Jobs
108
136
 
109
- All notifications return a job object with a job Id that can be tracked.
137
+ Every notification returns a job ID you can use to track delivery status.
110
138
 
111
139
  ### Check Job Status
112
140
 
113
- **Important:** `sendEmail()` returns immediately after queuing the job (HTTP 202 Accepted). The actual email is sent asynchronously by a background worker. Always check the job status to confirm delivery.
114
-
115
141
  ```typescript
116
- const job = await notifyKit.sendEmail({
142
+ const job = await client.sendEmail({
117
143
  to: "user@example.com",
118
144
  subject: "Test",
119
145
  body: "<h1>Test</h1>",
120
146
  });
121
147
 
122
- console.log(`Job ID: ${job.jobId}`); // Save this for later tracking
148
+ console.log(`Job ID: ${job.jobId}`);
123
149
 
124
150
  // Check status later
125
- const status = await notifyKit.getJob(job.jobId);
151
+ const status = await client.getJob(job.jobId);
126
152
 
127
153
  console.log(status.status); // 'pending' | 'processing' | 'completed' | 'failed'
128
154
 
129
155
  if (status.status === "completed") {
130
- console.log("Email sent successfully!");
156
+ console.log("Delivered successfully!");
131
157
  } else if (status.status === "failed") {
132
- console.error(" Failed:", status.errorMessage);
158
+ console.error("Failed:", status.errorMessage);
133
159
  }
134
160
  ```
135
161
 
136
162
  ### List Jobs with Filters
137
163
 
138
164
  ```typescript
139
- const result = await notifyKit.listJobs({
165
+ const result = await client.listJobs({
140
166
  page: 1,
141
167
  limit: 20,
142
- type: "email", // Filter by type: 'email' or 'webhook'
143
- status: "failed", // Filter by status: 'pending', 'processing', 'completed', 'failed'
168
+ type: "email", // Filter by type: 'email' or 'webhook'
169
+ status: "failed", // Filter by status
144
170
  });
145
171
 
146
172
  console.log(`Total: ${result.pagination.total} jobs`);
147
- console.log(`Pages: ${result.pagination.totalPages}`);
148
173
 
149
174
  result.data.forEach((job) => {
150
175
  console.log(`${job.id}: ${job.status} (${job.attempts} attempts)`);
@@ -154,466 +179,90 @@ result.data.forEach((job) => {
154
179
  ### Retry Failed Jobs
155
180
 
156
181
  ```typescript
157
- const job = await notifyKit.sendEmail({...});
158
-
159
- // Later, if job failed
160
- const status = await notifyKit.getJob(job.jobId);
182
+ const status = await client.getJob(job.jobId);
161
183
 
162
184
  if (status.status === "failed") {
163
- // Retry the job
164
- await notifyKit.retryJob(job.jobId);
165
- console.log("Job queued for retry");
185
+ const message = await client.retryJob(job.jobId);
186
+ console.log(message); // "Job has been re-queued for processing"
166
187
  }
167
188
  ```
168
189
 
169
- **Note:** Only jobs with status `failed` can be retried.
190
+ Only jobs with `failed` status can be retried.
170
191
 
171
192
  ---
172
193
 
173
194
  ## Domain Management
174
195
 
175
- Use your own verified domain to send emails (e.g., `welcome@yourapp.com` instead of `noreply@notifykit.dev`).
176
-
177
- ### Step 1: Request Domain Verification
178
-
179
- ```typescript
180
- const verification = await notifyKit.requestDomainVerification("yourapp.com");
181
-
182
- console.log(`Domain: ${verification.domain}`);
183
- console.log(`Status: ${verification.status}`); // 'pending' or 'verified'
184
- ```
185
-
186
- **Response:**
187
-
188
- ```json
189
- {
190
- "domain": "yourapp.com",
191
- "status": "pending",
192
- "dnsRecords": [
193
- {
194
- "id": 1,
195
- "type": "CNAME",
196
- "host": "em8724.yourapp.com",
197
- "value": "u12345678.wl123.sendgrid.net",
198
- "description": "Mail CNAME - Routes email through SendGrid"
199
- },
200
- {
201
- "id": 2,
202
- "type": "CNAME",
203
- "host": "s1._domainkey.yourapp.com",
204
- "value": "s1.domainkey.u12345678.wl123.sendgrid.net",
205
- "description": "DKIM 1 - Email authentication (prevents spoofing)"
206
- },
207
- {
208
- "id": 3,
209
- "type": "CNAME",
210
- "host": "s2._domainkey.yourapp.com",
211
- "value": "s2.domainkey.u12345678.wl123.sendgrid.net",
212
- "description": "DKIM 2 - Email authentication (backup)"
213
- }
214
- ],
215
- "instructions": {
216
- "message": "Add these DNS records to your domain registrar",
217
- "steps": [
218
- "1. Login to your domain registrar (Namecheap, GoDaddy, Cloudflare, etc.)",
219
- "2. Navigate to DNS settings for your domain",
220
- "3. Add each CNAME record below",
221
- "4. Wait 15-60 minutes for DNS propagation",
222
- "5. Click 'Verify Domain' to check status"
223
- ],
224
- "estimatedTime": "15-60 minutes"
225
- }
226
- }
227
- ```
228
-
229
- ### Understanding DNS Records
230
-
231
- **Record 1: Mail CNAME** (`em8724.yourapp.com`)
232
-
233
- - Routes emails through SendGrid's infrastructure
234
- - Allows sending from `welcome@em.yourapp.com`
235
-
236
- **Record 2 & 3: DKIM Records** (`s1._domainkey` and `s2._domainkey`)
237
-
238
- - Digital signatures that prove emails are from you
239
- - Prevents spoofing and improves deliverability
240
- - Two records provide redundancy during key rotation
241
-
242
- ### Step 2: Add DNS Records to Your Domain
243
-
244
- Go to your domain registrar and add the 3 CNAME records:
245
-
246
- **Example for Cloudflare:**
247
-
248
- ```
249
- Type: CNAME
250
- Name: em8724
251
- Content: u12345678.wl123.sendgrid.net
252
- Proxy status: DNS only (disable proxy)
253
- ```
254
-
255
- **Example for Namecheap:**
256
-
257
- ```
258
- Type: CNAME Record
259
- Host: em8724
260
- Value: u12345678.wl123.sendgrid.net
261
- TTL: Automatic
262
- ```
263
-
264
- **DNS propagation takes 15-60 minutes** (sometimes up to 24 hours).
265
-
266
- ### Step 3: Verify Domain
267
-
268
- After adding DNS records and waiting for propagation:
269
-
270
- ```typescript
271
- const status = await notifyKit.verifyDomain();
272
-
273
- if (status.verified) {
274
- console.log("Domain verified!");
275
- console.log("You can now send from: welcome@em.yourapp.com");
276
- } else {
277
- console.log("DNS still propagating...");
278
- console.log(status.message);
279
-
280
- // Check validation results for issues
281
- if (status.validationResults) {
282
- console.log("Validation details:", status.validationResults);
283
- }
284
- }
285
- ```
286
-
287
- **Response:**
288
-
289
- ```json
290
- {
291
- "domain": "yourapp.com",
292
- "verified": true,
293
- "message": "Domain verified! You can now send emails from this domain."
294
- }
295
- ```
296
-
297
- ### Step 4: Send Emails from Your Domain
298
-
299
- Once verified, emails automatically use `noreply@em.yourapp.com` if you don't specify a `from` address:
300
-
301
- ```typescript
302
- // Automatically uses: noreply@em.yourapp.com
303
- await notifyKit.sendEmail({
304
- to: "user@example.com",
305
- subject: "Welcome!",
306
- body: "Welcome to MyApp!",
307
- // No 'from' specified → Uses noreply@em.yourapp.com
308
- });
309
-
310
- // Or specify a custom sender address on your domain
311
- await notifyKit.sendEmail({
312
- to: "user@example.com",
313
- subject: "Support Response",
314
- body: "We're here to help",
315
- from: "support@em.yourapp.com", // Custom sender
316
- });
317
-
318
- // You can also use different names with the same domain
319
- await notifyKit.sendEmail({
320
- to: "user@example.com",
321
- subject: "Order Shipped",
322
- body: "Your order is on the way!",
323
- from: "orders@em.yourapp.com", // Different sender
324
- });
325
- ```
326
-
327
- **Important: The `em.` Subdomain**
328
-
329
- Emails are sent from `em.yourapp.com` (subdomain), **not** `yourapp.com` (main domain).
330
-
331
- **Examples:**
332
-
333
- - Correct: `welcome@em.yourapp.com`
334
- - Correct: `support@em.yourapp.com`
335
- - Wrong: `welcome@yourapp.com` (will be rejected)
336
-
337
- **Why the subdomain?**
338
-
339
- Using a dedicated subdomain (`em.`) for transactional emails protects your main domain's reputation:
340
-
341
- 1. **Isolation**: If a transactional email is marked as spam, it doesn't hurt your main domain
342
- 2. **Separate reputation**: Your business emails (`team@yourapp.com`) remain trusted
343
-
344
- **What recipients see:**
345
-
346
- ```
347
- From: support@em.yourapp.com
348
- ```
349
-
350
- Most email clients only show `yourapp.com` in the sender name, so users typically see it as coming from your domain.
196
+ Domain verification is managed through the **NotifyKit dashboard** (Settings → Domain). Verified domains let you send emails from your own address (e.g., `support@em.yourapp.com`) instead of `noreply@notifykit.dev`.
351
197
 
352
- **If you try to use the main domain:**
198
+ See [Domain Verification](https://docs.notifykit.dev/docs/guides/domain-verification) for setup instructions.
353
199
 
354
- ```typescript
355
- await notifyKit.sendEmail({
356
- from: "hello@yourapp.com", // Missing "em."
357
- });
358
-
359
- // Error: "Cannot send from hello@yourapp.com. Use em.yourapp.com instead
360
- // (e.g., hello@em.yourapp.com)"
361
- ```
362
-
363
- ### Check Domain Status Anytime
364
-
365
- ```typescript
366
- const info = await notifyKit.getDomainStatus();
367
-
368
- console.log(`Domain: ${info.domain}`); // 'yourapp.com' or null
369
- console.log(`Verified: ${info.verified}`); // true or false
370
- console.log(`Status: ${info.status}`); // 'not_configured', 'pending', 'verified'
371
-
372
- if (info.verified) {
373
- console.log(`Verified at: ${info.verifiedAt}`);
374
- }
375
- ```
376
-
377
- **Response:**
378
-
379
- ```json
380
- {
381
- "domain": "yourapp.com",
382
- "verified": true,
383
- "status": "verified",
384
- "dnsRecords": [...],
385
- "requestedAt": "2026-01-04T10:00:00.000Z",
386
- "verifiedAt": "2026-01-04T10:45:00.000Z"
387
- }
388
- ```
389
-
390
- ### Remove Domain Configuration
391
-
392
- ```typescript
393
- await notifyKit.removeDomain();
394
- console.log(
395
- "Domain removed. Emails will now send from NotifyKit's default domain."
396
- );
397
- ```
398
-
399
- **Note:** You can only have **one verified domain at a time**. Requesting a new domain automatically replaces the old one.
400
-
401
- ---
402
-
403
- ## Troubleshooting Domain Verification
404
-
405
- ### DNS Not Verifying After 1 Hour
406
-
407
- **Check DNS propagation:**
408
-
409
- ```bash
410
- # Check if CNAME exists
411
- dig em8724.yourapp.com CNAME
412
-
413
- # Should return: u12345678.wl123.sendgrid.net
414
- ```
415
-
416
- **Common issues:**
417
-
418
- 1. **Wrong host name** - Some registrars need full hostname (`em8724.yourapp.com`), others just the subdomain (`em8724`)
419
- 2. **Proxy enabled** (Cloudflare) - Disable proxy, use "DNS only"
420
- 3. **TTL too high** - Lower TTL to 300 seconds (5 minutes) for faster propagation
421
- 4. **Old records** - Delete any existing records for the same hostname first
422
-
423
- ### Emails Still Sending from NotifyKit Domain
424
-
425
- **Possible causes:**
426
-
427
- 1. Domain not verified yet - Check status: `await notifyKit.getDomainStatus()`
428
- 2. Free plan - Custom domains only available on paid plans (Indie, Startup)
429
- 3. Verification failed - DNS records may have been removed
430
-
431
- ### Verification Says "Domain Already Verified by Another Customer"
432
-
433
- Each domain can only be verified by one NotifyKit account. If you own this domain:
434
-
435
- 1. Remove it from the other account first
436
- 2. Then verify it on your current account
437
-
438
- ---
439
-
440
- ## Best Practices
441
-
442
- ### Use Subdomain for Transactional Emails
443
-
444
- **Good:** `noreply@em.yourapp.com` (dedicated subdomain)
445
- **Bad:** `noreply@yourapp.com` (main domain)
446
-
447
- **Why?** If transactional emails are marked as spam, it doesn't hurt your main domain's reputation for important business emails.
448
-
449
- ### Set Up DMARC (Optional but Recommended)
450
-
451
- Add a TXT record to improve email deliverability:
452
-
453
- ```
454
- Type: TXT
455
- Name: _dmarc
456
- Value: v=DMARC1; p=none; rua=mailto:dmarc-reports@yourapp.com
457
- ```
458
-
459
- This enables email authentication reporting and protects against spoofing.
460
-
461
- ### Monitor Your Domain Reputation
462
-
463
- - Check SendGrid Activity Feed regularly
464
- - Watch for high bounce rates (>5% is concerning)
465
- - Remove invalid email addresses from your lists
466
- - Never send unsolicited emails (spam)
200
+ **Available on Indie and Startup plans only.**
467
201
 
468
202
  ---
469
203
 
470
- ## Real-World Examples
471
-
472
- ### User Signup Flow
473
-
474
- ```typescript
475
- import { NotifyKitClient } from "@notifykit/sdk";
476
-
477
- const notifyKit = new NotifyKitClient({
478
- apiKey: process.env.NOTIFYKIT_API_KEY,
479
- });
480
-
481
- async function handleUserSignup(user) {
482
- await notifyKit.sendEmail({
483
- to: user.email,
484
- subject: "Welcome to MyApp!",
485
- body: `
486
- <h1>Hi ${user.name}!</h1>
487
- <p>Thanks for signing up. Get started by exploring our features.</p>
488
- <a href="https://myapp.com/get-started">Get Started</a>
489
- `,
490
- idempotencyKey: `user-${user.id}-welcome`,
491
- });
492
- }
493
- ```
494
-
495
- ### Order Confirmation with Multiple Notifications
496
-
497
- ```typescript
498
- async function handleOrderPlaced(order) {
499
- // Send email to customer
500
- await notifyKit.sendEmail({
501
- to: order.customerEmail,
502
- subject: `Order #${order.id} Confirmed`,
503
- body: generateOrderConfirmationEmail(order),
504
- idempotencyKey: `order-${order.id}-customer-email`,
505
- });
506
-
507
- // Notify warehouse system via webhook
508
- await notifyKit.sendWebhook({
509
- url: "https://warehouse-system.com/api/new-order",
510
- method: "POST",
511
- payload: {
512
- orderId: order.id,
513
- items: order.items,
514
- shippingAddress: order.shippingAddress,
515
- priority: order.priority,
516
- },
517
- headers: {
518
- "X-Warehouse-Token": process.env.WAREHOUSE_API_TOKEN,
519
- },
520
- idempotencyKey: `order-${order.id}-warehouse-webhook`,
521
- });
522
-
523
- // Send to analytics
524
- await notifyKit.sendWebhook({
525
- url: "https://analytics.myapp.com/track",
526
- payload: {
527
- event: "order.placed",
528
- orderId: order.id,
529
- revenue: order.total,
530
- customerId: order.customerId,
531
- },
532
- idempotencyKey: `order-${order.id}-analytics`,
533
- });
534
- }
535
- ```
536
-
537
- ### Background Job Status Monitoring
204
+ ## Error Handling
538
205
 
539
206
  ```typescript
540
- async function sendEmailWithMonitoring(
541
- to: string,
542
- subject: string,
543
- body: string
544
- ) {
545
- const job = await notifyKit.sendEmail({ to, subject, body });
546
-
547
- // Check status after 5 seconds
548
- setTimeout(async () => {
549
- const status = await notifyKit.getJob(job.jobId);
550
-
551
- if (status.status === "failed") {
552
- console.error(`Email delivery failed: ${status.errorMessage}`);
553
- await alertAdmin(`Email to ${to} failed`);
554
- }
555
- }, 5000);
556
-
557
- return job;
207
+ import { NotifyKitClient, NotifyKitError } from "@notifykit/sdk";
208
+
209
+ try {
210
+ await client.sendEmail({ to: "bad-email", subject: "Test", body: "Hello" });
211
+ } catch (error) {
212
+ if (error instanceof NotifyKitError) {
213
+ console.error(error.getFullMessage()); // "[400] to must be an email"
214
+
215
+ if (error.isStatus(400)) console.error("Bad request:", error.message);
216
+ if (error.isStatus(401)) console.error("Invalid API key");
217
+ if (error.isStatus(403)) console.error("Quota or permission error:", error.message);
218
+ if (error.isStatus(409)) console.error("Duplicate idempotency key");
219
+ if (error.isStatus(429)) console.error("Rate limit exceeded");
220
+ }
558
221
  }
559
222
  ```
560
223
 
561
224
  ---
562
225
 
563
- ## Configuration
564
-
565
- ### Custom Base URL (Self-Hosted or Staging)
566
-
567
- ```typescript
568
- const notifyKit = new NotifyKitClient({
569
- apiKey: "ntfy_sk_...",
570
- baseUrl: "https://staging-api.notifykit.dev", // Default: https://api.notifykit.dev
571
- });
572
- ```
573
-
574
- ---
575
-
576
226
  ## API Reference
577
227
 
578
- | Method | Description | Returns |
579
- | ----------------------------------- | -------------------------------- | ---------------------------- |
580
- | `sendEmail(options)` | Send an email notification | `JobResponse` |
581
- | `sendWebhook(options)` | Send a webhook notification | `JobResponse` |
582
- | `getJob(jobId)` | Get job status by ID | `JobStatus` |
583
- | `listJobs(options?)` | List jobs with optional filters | `{ data, pagination }` |
584
- | `retryJob(jobId)` | Retry a failed job | `JobResponse` |
585
- | `requestDomainVerification(domain)` | Request domain verification | `DomainVerificationResponse` |
586
- | `verifyDomain()` | Check domain DNS verification | `DomainStatusResponse` |
587
- | `getDomainStatus()` | Get current domain configuration | `DomainInfoResponse` |
588
- | `removeDomain()` | Remove domain configuration | `{ message: string }` |
228
+ | Method | Description | Returns |
229
+ | ---------------------- | ----------------------------------- | ------------------------------ |
230
+ | `sendEmail(options)` | Send an email notification | `Promise<JobResponse>` |
231
+ | `sendWebhook(options)` | Send a webhook notification | `Promise<JobResponse>` |
232
+ | `getJob(jobId)` | Get job status and details | `Promise<JobDetails>` |
233
+ | `listJobs(options?)` | List jobs with optional filters | `Promise<{ data, pagination }>` |
234
+ | `retryJob(jobId)` | Retry a failed job | `Promise<string>` |
235
+ | `ping()` | Test API connection | `Promise<string>` |
236
+ | `getApiInfo()` | Get API version info | `Promise<ApiInfo>` |
589
237
 
590
238
  ### TypeScript Types
591
239
 
592
- All methods are fully typed. Import types for use in your code:
593
-
594
240
  ```typescript
595
- import {
596
- NotifyKitClient,
241
+ import type {
242
+ NotifyKitConfig,
597
243
  SendEmailOptions,
598
244
  SendWebhookOptions,
599
245
  JobResponse,
600
- JobStatus,
601
- NotifyKitError,
246
+ ApiInfo,
602
247
  } from "@notifykit/sdk";
603
248
  ```
604
249
 
605
250
  ---
606
251
 
607
- ## Requirements
252
+ ## Plans
608
253
 
609
- - Node.js 16+ or browser with fetch support
610
- - TypeScript 4.5+ (optional, for type checking)
254
+ | Plan | Price | Webhooks/month | Emails/month |
255
+ | ------- | --------- | -------------- | ------------------------------------- |
256
+ | Free | $0 | 100 (shared) | 100 (shared with webhooks) |
257
+ | Indie | $9/mo | 4,000 | Unlimited (via your SendGrid key) |
258
+ | Startup | $30/mo | 15,000 | Unlimited (via your SendGrid key) |
611
259
 
612
260
  ---
613
261
 
614
262
  ## Support
615
263
 
616
- - 🐛 Issues: [GitHub Issues](https://github.com/brayzonn/notifykit-sdk/issues)
264
+ - Docs: [docs.notifykit.dev](https://docs.notifykit.dev)
265
+ - Issues: [GitHub Issues](https://github.com/brayzonn/notifykit-sdk/issues)
617
266
 
618
267
  ---
619
268
 
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NotifyKitConfig, SendEmailOptions, SendWebhookOptions, JobResponse, JobStatus, ApiInfo, PaginationMeta } from "./types";
1
+ import { NotifyKitConfig, SendEmailOptions, SendWebhookOptions, JobResponse, JobStatus, ApiInfo, PaginationMeta, RetryJobResponse } from './types';
2
2
  export declare class NotifyKitClient {
3
3
  private client;
4
4
  constructor(config: NotifyKitConfig);
@@ -16,12 +16,12 @@ export declare class NotifyKitClient {
16
16
  listJobs(options?: {
17
17
  page?: number;
18
18
  limit?: number;
19
- type?: "email" | "webhook";
20
- status?: "pending" | "processing" | "completed" | "failed";
19
+ type?: 'email' | 'webhook';
20
+ status?: 'pending' | 'processing' | 'completed' | 'failed';
21
21
  }): Promise<{
22
22
  data: JobStatus[];
23
23
  pagination: PaginationMeta;
24
24
  }>;
25
25
  /** Retry a failed job */
26
- retryJob(jobId: string): Promise<JobResponse>;
26
+ retryJob(jobId: string): Promise<RetryJobResponse>;
27
27
  }
package/dist/client.js CHANGED
@@ -9,22 +9,21 @@ const errors_1 = require("./errors");
9
9
  class NotifyKitClient {
10
10
  constructor(config) {
11
11
  if (!config.apiKey) {
12
- throw new Error("API key is required");
12
+ throw new Error('API key is required');
13
13
  }
14
14
  this.client = axios_1.default.create({
15
- baseURL: config.baseUrl || "https://api.notifykit.dev",
15
+ baseURL: config.baseUrl || 'https://api.notifykit.dev',
16
16
  headers: {
17
- "X-API-Key": config.apiKey,
18
- "Content-Type": "application/json",
17
+ 'X-API-Key': config.apiKey,
18
+ 'Content-Type': 'application/json',
19
19
  },
20
20
  });
21
21
  this.client.interceptors.response.use((response) => {
22
- const apiResponse = response.data?.data;
23
- if (!apiResponse)
24
- return response.data;
25
- if (apiResponse.message)
26
- return apiResponse.message;
27
- return apiResponse;
22
+ const apiResponse = response.data;
23
+ if (apiResponse && apiResponse.success === false) {
24
+ throw new errors_1.NotifyKitError(apiResponse.error || apiResponse.message || 'Request failed', response.status, apiResponse);
25
+ }
26
+ return apiResponse?.data ?? apiResponse;
28
27
  }, (error) => {
29
28
  if (error.response) {
30
29
  const data = error.response.data;
@@ -34,7 +33,7 @@ class NotifyKitClient {
34
33
  if (data?.error) {
35
34
  if (Array.isArray(data.error)) {
36
35
  errors = data.error;
37
- message = data.error.join(", ");
36
+ message = data.error.join(', ');
38
37
  }
39
38
  else {
40
39
  message = data.error;
@@ -43,7 +42,7 @@ class NotifyKitClient {
43
42
  else if (data?.message) {
44
43
  if (Array.isArray(data.message)) {
45
44
  errors = data.message;
46
- message = "Validation failed";
45
+ message = 'Validation failed';
47
46
  }
48
47
  else {
49
48
  message = data.message;
@@ -51,7 +50,7 @@ class NotifyKitClient {
51
50
  }
52
51
  throw new errors_1.NotifyKitError(message, statusCode, data, errors);
53
52
  }
54
- throw new errors_1.NotifyKitError(error.message || "Network error occurred");
53
+ throw new errors_1.NotifyKitError(error.message || 'Network error occurred');
55
54
  });
56
55
  }
57
56
  // ================================
@@ -59,22 +58,22 @@ class NotifyKitClient {
59
58
  // ================================
60
59
  /** Test API connection */
61
60
  async ping() {
62
- return await this.client.get("/api/v1/ping");
61
+ return await this.client.get('/api/v1/ping');
63
62
  }
64
63
  /** Get API information */
65
64
  async getApiInfo() {
66
- return await this.client.get("/api/v1/info");
65
+ return await this.client.get('/api/v1/info');
67
66
  }
68
67
  // ================================
69
68
  // NOTIFICATIONS
70
69
  // ================================
71
70
  /** Send an email notification */
72
71
  async sendEmail(options) {
73
- return await this.client.post("/api/v1/notifications/email", options);
72
+ return await this.client.post('/api/v1/notifications/email', options);
74
73
  }
75
74
  /** Send a webhook notification */
76
75
  async sendWebhook(options) {
77
- return await this.client.post("/api/v1/notifications/webhook", options);
76
+ return await this.client.post('/api/v1/notifications/webhook', options);
78
77
  }
79
78
  /** Get job status by ID */
80
79
  async getJob(jobId) {
@@ -82,7 +81,7 @@ class NotifyKitClient {
82
81
  }
83
82
  /** List jobs with optional filters */
84
83
  async listJobs(options) {
85
- return await this.client.get("/api/v1/notifications/jobs", {
84
+ return await this.client.get('/api/v1/notifications/jobs', {
86
85
  params: options,
87
86
  });
88
87
  }
package/dist/types.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface SendEmailOptions {
13
13
  subject: string;
14
14
  body: string;
15
15
  from?: string;
16
+ priority?: number;
16
17
  idempotencyKey?: string;
17
18
  }
18
19
  export interface ApiInfo {
@@ -23,7 +24,7 @@ export interface ApiInfo {
23
24
  }
24
25
  export interface SendWebhookOptions {
25
26
  url: string;
26
- method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
27
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
27
28
  payload?: any;
28
29
  headers?: Record<string, string>;
29
30
  idempotencyKey?: string;
@@ -37,9 +38,14 @@ export interface JobResponse {
37
38
  export interface JobStatus {
38
39
  id: string;
39
40
  type: string;
40
- status: "pending" | "processing" | "completed" | "failed";
41
+ status: 'pending' | 'processing' | 'completed' | 'failed';
41
42
  attempts: number;
42
43
  errorMessage?: string;
43
44
  createdAt: string;
44
45
  completedAt?: string;
45
46
  }
47
+ export interface RetryJobResponse {
48
+ jobId: string;
49
+ status: string;
50
+ message: string;
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notifykit/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Official NotifyKit SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",