unsent 1.0.1 → 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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +258 -2
  4. data/lib/unsent/analytics.rb +30 -0
  5. data/lib/unsent/api_keys.rb +21 -0
  6. data/lib/unsent/campaigns.rb +4 -0
  7. data/lib/unsent/client.rb +8 -1
  8. data/lib/unsent/contact_books.rb +29 -0
  9. data/lib/unsent/contacts.rb +11 -0
  10. data/lib/unsent/emails.rb +47 -0
  11. data/lib/unsent/errors.rb +2 -0
  12. data/lib/unsent/models/add_suppression_request.rb +223 -0
  13. data/lib/unsent/models/create_api_key_request.rb +218 -0
  14. data/lib/unsent/models/create_campaign200_response.rb +750 -0
  15. data/lib/unsent/models/create_campaign_request.rb +426 -0
  16. data/lib/unsent/models/create_campaign_request_reply_to.rb +103 -0
  17. data/lib/unsent/models/create_contact200_response.rb +147 -0
  18. data/lib/unsent/models/create_contact_book200_response.rb +304 -0
  19. data/lib/unsent/models/create_contact_book_request.rb +193 -0
  20. data/lib/unsent/models/create_contact_request.rb +202 -0
  21. data/lib/unsent/models/create_domain_request.rb +190 -0
  22. data/lib/unsent/models/create_template200_response.rb +164 -0
  23. data/lib/unsent/models/create_template_request.rb +226 -0
  24. data/lib/unsent/models/delete_contact_book200_response.rb +164 -0
  25. data/lib/unsent/models/get_api_keys200_response_inner.rb +278 -0
  26. data/lib/unsent/models/get_campaigns200_response_inner.rb +296 -0
  27. data/lib/unsent/models/get_contact_book200_response.rb +330 -0
  28. data/lib/unsent/models/get_contact_book200_response_details.rb +218 -0
  29. data/lib/unsent/models/get_domains200_response_inner.rb +482 -0
  30. data/lib/unsent/models/get_domains200_response_inner_dns_records_inner.rb +318 -0
  31. data/lib/unsent/models/get_health200_response.rb +216 -0
  32. data/lib/unsent/models/get_templates200_response_inner.rb +314 -0
  33. data/lib/unsent/models/list_emails_domain_id_parameter.rb +103 -0
  34. data/lib/unsent/models/schedule_campaign_request.rb +185 -0
  35. data/lib/unsent/models/send_email_request.rb +378 -0
  36. data/lib/unsent/models/send_email_request_to.rb +103 -0
  37. data/lib/unsent/models/update_contact_book200_response.rb +190 -0
  38. data/lib/unsent/models/update_contact_book_request.rb +167 -0
  39. data/lib/unsent/models/update_contact_request.rb +176 -0
  40. data/lib/unsent/models/update_template_request.rb +174 -0
  41. data/lib/unsent/settings.rb +13 -0
  42. data/lib/unsent/suppressions.rb +28 -0
  43. data/lib/unsent/templates.rb +29 -0
  44. data/lib/unsent/version.rb +1 -1
  45. data/lib/unsent/webhooks.rb +25 -0
  46. data/lib/unsent.rb +7 -0
  47. metadata +38 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3654dd5977b62530b4cde2e028eb70aa08a29dbc09c0ff41d59127a831c61db0
4
- data.tar.gz: d90d594fdb4db827a8cffc65f8373f53d1ccb4bf6e11e580da858a6689271775
3
+ metadata.gz: aa505d7132d95fc8d1d1081eacdc6dc1c152d7265775437c7841a63f0cfbab0b
4
+ data.tar.gz: d173d3f42bb1b159f2a4d59070d7f1c6db41bb8af2771c29d1808f742541394a
5
5
  SHA512:
6
- metadata.gz: f32d82928ae27864e81968b6d95e508e1c5ffc0eb8601a9456ea73f7be70fee4292e7fc134b0c5a8c2db774ea4babddcf90e67a91773c8ec2a43ba8a71e7d212
7
- data.tar.gz: 97a1c68b5e3845cc052c863e6cbdcc1b5203adb187c801106c37c3ea70bfb6e81d970a25eaa5122726732b6150e721805da1c1f2f1d8396decebd291e1fcb454
6
+ metadata.gz: 22ae152f36d21cc5f7dda582594d1b0cb0e667fba6765e9dbf3e14f23e9cf2f65f15f4ff937a09a36303d04d0d4208c5dfd5aa31121bd6c86b759dbc6a40dffd
7
+ data.tar.gz: 12f06ce781b6c75e5cc7985bd9eaa5dd1f48754715a0115cca022ed3467de14daf716284a373d17a2ea7d8beb7cb5d661b5c8b95ecf5ef0b6be18efd2daec831
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2025 TODO: Write your name
3
+ Copyright (c) 2025 Sourav Ukil
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -35,7 +35,7 @@ gem install unsent
35
35
  ```ruby
36
36
  require 'unsent'
37
37
 
38
- client = Unsent::Client.new('us_xxx')
38
+ client = Unsent::Client.new('un_xxx')
39
39
  ```
40
40
 
41
41
  ### Environment Variables
@@ -267,7 +267,7 @@ end
267
267
  To handle errors as return values instead:
268
268
 
269
269
  ```ruby
270
- client = Unsent::Client.new('us_xxx', raise_on_error: false)
270
+ client = Unsent::Client.new('un_xxx', raise_on_error: false)
271
271
 
272
272
  data, error = client.emails.get('email_123')
273
273
  if error
@@ -277,6 +277,262 @@ else
277
277
  end
278
278
  ```
279
279
 
280
+ ## Email Management
281
+
282
+ ### List Emails
283
+
284
+ Retrieve a paginated list of emails with optional filters:
285
+
286
+ ```ruby
287
+ data, error = client.emails.list(
288
+ page: 1,
289
+ limit: 10,
290
+ startDate: '2024-01-01',
291
+ endDate: '2024-01-31',
292
+ domainId: 'domain_123'
293
+ )
294
+
295
+ # Support for multiple domain IDs
296
+ data, error = client.emails.list(domainId: ['domain_1', 'domain_2'])
297
+ ```
298
+
299
+ ### Email Statistics
300
+
301
+ ```ruby
302
+ # Get complaints
303
+ data, error = client.emails.get_complaints(page: 1, limit: 10)
304
+
305
+ # Get bounces
306
+ data, error = client.emails.get_bounces(page: 1, limit: 10)
307
+
308
+ # Get unsubscribes
309
+ data, error = client.emails.get_unsubscribes(page: 1, limit: 10)
310
+ ```
311
+
312
+ ## Contact Books
313
+
314
+ Organize your contacts into separate books.
315
+
316
+ ### Create Contact Book
317
+
318
+ ```ruby
319
+ data, error = client.contact_books.create(
320
+ name: 'Newsletter Subscribers',
321
+ emoji: '📧'
322
+ )
323
+ ```
324
+
325
+ ### List Contact Books
326
+
327
+ ```ruby
328
+ books, error = client.contact_books.list
329
+ books.each { |book| puts book['name'] }
330
+ ```
331
+
332
+ ### Get Contact Book
333
+
334
+ ```ruby
335
+ book, error = client.contact_books.get('book_123')
336
+ ```
337
+
338
+ ### Update Contact Book
339
+
340
+ ```ruby
341
+ data, error = client.contact_books.update('book_123', name: 'Updated Name')
342
+ ```
343
+
344
+ ### Delete Contact Book
345
+
346
+ ```ruby
347
+ data, error = client.contact_books.delete('book_123')
348
+ ```
349
+
350
+ ## Contacts
351
+
352
+ ### List Contacts
353
+
354
+ ```ruby
355
+ data, error = client.contacts.list('book_123',
356
+ page: 1,
357
+ limit: 10,
358
+ search: 'john@example.com'
359
+ )
360
+ ```
361
+
362
+ ## Campaigns
363
+
364
+ ### List Campaigns
365
+
366
+ ```ruby
367
+ campaigns, error = client.campaigns.list
368
+ ```
369
+
370
+ ## Analytics
371
+
372
+ Get insights into your email sending performance.
373
+
374
+ ### Overall Analytics
375
+
376
+ ```ruby
377
+ data, error = client.analytics.get
378
+ puts "Sent: #{data['sent']}, Delivered: #{data['delivered']}"
379
+ ```
380
+
381
+ ### Time Series Data
382
+
383
+ ```ruby
384
+ data, error = client.analytics.get_time_series(
385
+ days: 30,
386
+ domain: 'yourdomain.com'
387
+ )
388
+ ```
389
+
390
+ ### Reputation Score
391
+
392
+ ```ruby
393
+ data, error = client.analytics.get_reputation(domain: 'yourdomain.com')
394
+ puts "Reputation Score: #{data['score']}"
395
+ ```
396
+
397
+ ## Templates
398
+
399
+ Manage reusable email templates.
400
+
401
+ ### Create Template
402
+
403
+ ```ruby
404
+ data, error = client.templates.create(
405
+ name: 'Welcome Email',
406
+ subject: 'Welcome to {{companyName}}!',
407
+ html: '<h1>Welcome {{firstName}}!</h1>'
408
+ )
409
+ ```
410
+
411
+ ### List Templates
412
+
413
+ ```ruby
414
+ templates, error = client.templates.list
415
+ ```
416
+
417
+ ### Get Template
418
+
419
+ ```ruby
420
+ template, error = client.templates.get('template_123')
421
+ ```
422
+
423
+ ### Update Template
424
+
425
+ ```ruby
426
+ data, error = client.templates.update('template_123',
427
+ subject: 'Updated Subject'
428
+ )
429
+ ```
430
+
431
+ ### Delete Template
432
+
433
+ ```ruby
434
+ data, error = client.templates.delete('template_123')
435
+ ```
436
+
437
+ ## Suppressions
438
+
439
+ Manage your email suppression list.
440
+
441
+ ### List Suppressions
442
+
443
+ ```ruby
444
+ data, error = client.suppressions.list(
445
+ page: 1,
446
+ limit: 10,
447
+ reason: 'MANUAL',
448
+ search: 'user@'
449
+ )
450
+ ```
451
+
452
+ ### Add to Suppression List
453
+
454
+ ```ruby
455
+ data, error = client.suppressions.add(
456
+ email: 'blocked@example.com',
457
+ reason: 'MANUAL'
458
+ )
459
+ ```
460
+
461
+ ### Remove from Suppression List
462
+
463
+ ```ruby
464
+ data, error = client.suppressions.delete('blocked@example.com')
465
+ ```
466
+
467
+ ## API Keys
468
+
469
+ Manage your API keys programmatically.
470
+
471
+ ### List API Keys
472
+
473
+ ```ruby
474
+ keys, error = client.api_keys.list
475
+ ```
476
+
477
+ ### Create API Key
478
+
479
+ ```ruby
480
+ data, error = client.api_keys.create(
481
+ name: 'Production Key',
482
+ permission: 'SENDING'
483
+ )
484
+ puts "New key: #{data['key']}"
485
+ ```
486
+
487
+ ### Delete API Key
488
+
489
+ ```ruby
490
+ data, error = client.api_keys.delete('key_123')
491
+ ```
492
+
493
+ ## Webhooks
494
+
495
+ > **Note**: Webhooks are currently a future feature and are documented here for reference.
496
+
497
+ Configure webhooks to receive real-time notifications.
498
+
499
+ ### List Webhooks
500
+
501
+ ```ruby
502
+ webhooks, error = client.webhooks.list
503
+ ```
504
+
505
+ ### Create Webhook
506
+
507
+ ```ruby
508
+ data, error = client.webhooks.create(
509
+ url: 'https://yourdomain.com/webhooks',
510
+ events: ['email.sent', 'email.delivered', 'email.bounced']
511
+ )
512
+ ```
513
+
514
+ ### Update Webhook
515
+
516
+ ```ruby
517
+ data, error = client.webhooks.update('webhook_123',
518
+ url: 'https://yourdomain.com/updated-webhook'
519
+ )
520
+ ```
521
+
522
+ ### Delete Webhook
523
+
524
+ ```ruby
525
+ data, error = client.webhooks.delete('webhook_123')
526
+ ```
527
+
528
+ ## Settings
529
+
530
+ Retrieve account settings.
531
+
532
+ ```ruby
533
+ settings, error = client.settings.get
534
+ ```
535
+
280
536
  ## Development
281
537
 
282
538
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unsent
4
+ class Analytics
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def get
10
+ @client.get('/analytics')
11
+ end
12
+
13
+ def get_time_series(query = {})
14
+ params = []
15
+ params << "days=#{query[:days]}" if query[:days]
16
+ params << "domain=#{query[:domain]}" if query[:domain]
17
+
18
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
19
+ @client.get("/analytics/time-series#{query_string}")
20
+ end
21
+
22
+ def get_reputation(query = {})
23
+ params = []
24
+ params << "domain=#{query[:domain]}" if query[:domain]
25
+
26
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
27
+ @client.get("/analytics/reputation#{query_string}")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unsent
4
+ class ApiKeys
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def list
10
+ @client.get('/api-keys')
11
+ end
12
+
13
+ def create(payload)
14
+ @client.post('/api-keys', payload)
15
+ end
16
+
17
+ def delete(id)
18
+ @client.delete("/api-keys/#{id}")
19
+ end
20
+ end
21
+ end
@@ -6,6 +6,10 @@ module Unsent
6
6
  @client = client
7
7
  end
8
8
 
9
+ def list
10
+ @client.get('/campaigns')
11
+ end
12
+
9
13
  def create(payload)
10
14
  @client.post("/campaigns", payload)
11
15
  end
data/lib/unsent/client.rb CHANGED
@@ -9,7 +9,7 @@ module Unsent
9
9
  DEFAULT_BASE_URL = "https://api.unsent.dev"
10
10
 
11
11
  attr_reader :key, :url, :raise_on_error
12
- attr_accessor :emails, :contacts, :campaigns, :domains
12
+ attr_accessor :emails, :contacts, :campaigns, :domains, :analytics, :api_keys, :contact_books, :settings, :suppressions, :templates, :webhooks
13
13
 
14
14
  def initialize(key = nil, url: nil, raise_on_error: true)
15
15
  @key = key || ENV["UNSENT_API_KEY"] || ENV["UNSENT_API_KEY"]
@@ -24,6 +24,13 @@ module Unsent
24
24
  @contacts = Contacts.new(self)
25
25
  @campaigns = Campaigns.new(self)
26
26
  @domains = Domains.new(self)
27
+ @analytics = Analytics.new(self)
28
+ @api_keys = ApiKeys.new(self)
29
+ @contact_books = ContactBooks.new(self)
30
+ @settings = Settings.new(self)
31
+ @suppressions = Suppressions.new(self)
32
+ @templates = Templates.new(self)
33
+ @webhooks = Webhooks.new(self)
27
34
  end
28
35
 
29
36
  def request(method, path, body = nil, headers = {})
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unsent
4
+ class ContactBooks
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def list
10
+ @client.get('/contactBooks')
11
+ end
12
+
13
+ def create(payload)
14
+ @client.post('/contactBooks', payload)
15
+ end
16
+
17
+ def get(id)
18
+ @client.get("/contactBooks/#{id}")
19
+ end
20
+
21
+ def update(id, payload)
22
+ @client.patch("/contactBooks/#{id}", payload)
23
+ end
24
+
25
+ def delete(id)
26
+ @client.delete("/contactBooks/#{id}")
27
+ end
28
+ end
29
+ end
@@ -6,6 +6,17 @@ module Unsent
6
6
  @client = client
7
7
  end
8
8
 
9
+ def list(book_id, query = {})
10
+ params = []
11
+ params << "emails=#{query[:emails]}" if query[:emails]
12
+ params << "page=#{query[:page]}" if query[:page]
13
+ params << "limit=#{query[:limit]}" if query[:limit]
14
+ params << "ids=#{query[:ids]}" if query[:ids]
15
+
16
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
17
+ @client.get("/contactBooks/#{book_id}/contacts#{query_string}")
18
+ end
19
+
9
20
  def create(book_id, payload)
10
21
  @client.post("/contactBooks/#{book_id}/contacts", payload)
11
22
  end
data/lib/unsent/emails.rb CHANGED
@@ -53,5 +53,52 @@ module Unsent
53
53
  def cancel(email_id)
54
54
  @client.post("/emails/#{email_id}/cancel", {})
55
55
  end
56
+
57
+ def list(query = {})
58
+ params = []
59
+ params << "page=#{query[:page]}" if query[:page]
60
+ params << "limit=#{query[:limit]}" if query[:limit]
61
+ params << "startDate=#{query[:startDate]}" if query[:startDate]
62
+ params << "endDate=#{query[:endDate]}" if query[:endDate]
63
+
64
+ # Handle domainId as both string and array
65
+ if query[:domainId]
66
+ if query[:domainId].is_a?(Array)
67
+ query[:domainId].each { |id| params << "domainId=#{id}" }
68
+ else
69
+ params << "domainId=#{query[:domainId]}"
70
+ end
71
+ end
72
+
73
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
74
+ @client.get("/emails#{query_string}")
75
+ end
76
+
77
+ def get_complaints(query = {})
78
+ params = []
79
+ params << "page=#{query[:page]}" if query[:page]
80
+ params << "limit=#{query[:limit]}" if query[:limit]
81
+
82
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
83
+ @client.get("/emails/complaints#{query_string}")
84
+ end
85
+
86
+ def get_bounces(query = {})
87
+ params = []
88
+ params << "page=#{query[:page]}" if query[:page]
89
+ params << "limit=#{query[:limit]}" if query[:limit]
90
+
91
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
92
+ @client.get("/emails/bounces#{query_string}")
93
+ end
94
+
95
+ def get_unsubscribes(query = {})
96
+ params = []
97
+ params << "page=#{query[:page]}" if query[:page]
98
+ params << "limit=#{query[:limit]}" if query[:limit]
99
+
100
+ query_string = params.empty? ? '' : "?#{params.join('&')}"
101
+ @client.get("/emails/unsubscribes#{query_string}")
102
+ end
56
103
  end
57
104
  end
data/lib/unsent/errors.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Unsent
4
+ class Error < StandardError; end
5
+
4
6
  class HTTPError < Error
5
7
  attr_reader :status_code, :error, :method, :path
6
8