emailit 2.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +597 -0
  4. data/lib/emailit/api_resource.rb +22 -0
  5. data/lib/emailit/api_response.rb +18 -0
  6. data/lib/emailit/base_client.rb +115 -0
  7. data/lib/emailit/collection.rb +25 -0
  8. data/lib/emailit/delivery_method.rb +74 -0
  9. data/lib/emailit/emailit_client.rb +53 -0
  10. data/lib/emailit/emailit_object.rb +57 -0
  11. data/lib/emailit/errors.rb +36 -0
  12. data/lib/emailit/events/webhook_event.rb +215 -0
  13. data/lib/emailit/railtie.rb +13 -0
  14. data/lib/emailit/resources/api_key.rb +7 -0
  15. data/lib/emailit/resources/audience.rb +7 -0
  16. data/lib/emailit/resources/contact.rb +7 -0
  17. data/lib/emailit/resources/domain.rb +7 -0
  18. data/lib/emailit/resources/email.rb +7 -0
  19. data/lib/emailit/resources/email_verification.rb +7 -0
  20. data/lib/emailit/resources/email_verification_list.rb +7 -0
  21. data/lib/emailit/resources/event.rb +7 -0
  22. data/lib/emailit/resources/subscriber.rb +7 -0
  23. data/lib/emailit/resources/suppression.rb +7 -0
  24. data/lib/emailit/resources/template.rb +7 -0
  25. data/lib/emailit/resources/webhook.rb +7 -0
  26. data/lib/emailit/services/api_key_service.rb +27 -0
  27. data/lib/emailit/services/audience_service.rb +27 -0
  28. data/lib/emailit/services/base_service.rb +50 -0
  29. data/lib/emailit/services/contact_service.rb +27 -0
  30. data/lib/emailit/services/domain_service.rb +31 -0
  31. data/lib/emailit/services/email_service.rb +47 -0
  32. data/lib/emailit/services/email_verification_list_service.rb +27 -0
  33. data/lib/emailit/services/email_verification_service.rb +11 -0
  34. data/lib/emailit/services/event_service.rb +15 -0
  35. data/lib/emailit/services/subscriber_service.rb +27 -0
  36. data/lib/emailit/services/suppression_service.rb +27 -0
  37. data/lib/emailit/services/template_service.rb +31 -0
  38. data/lib/emailit/services/webhook_service.rb +27 -0
  39. data/lib/emailit/util.rb +35 -0
  40. data/lib/emailit/version.rb +5 -0
  41. data/lib/emailit/webhook_signature.rb +59 -0
  42. data/lib/emailit.rb +66 -0
  43. metadata +144 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fc40658f338dd2cc6a1dfc86a17d8db107893a15d9091dad28c27fabbcd17140
4
+ data.tar.gz: 9f43b54e4f0d413317ac3257222b42756bd699fd5b938aa01d11017062611dbd
5
+ SHA512:
6
+ metadata.gz: aac6bc7aeb78bbdefaf8d693911f35760ee146f047ce4b0f7dfdce042249d35f96cf4c182c2690b00f0d95f5ec8f6e8a5a8456f832c4770b78c8ddc5adef6404
7
+ data.tar.gz: b3d52849c286a6d39914ec61fb03ab8c94139055e2954d4938ab5a6c7e799a8292b206db8a4b9b1b8c364efe74f0403d8ffb88377beb2a4b0a755ce8754ca430
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Emailit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,597 @@
1
+ # Emailit Ruby
2
+
3
+ [![Tests](https://img.shields.io/github/actions/workflow/status/emailit/emailit-ruby/tests.yml?label=tests&style=for-the-badge&labelColor=111827)](https://github.com/emailit/emailit-ruby/actions)
4
+ [![Gem Version](https://img.shields.io/gem/v/emailit?style=for-the-badge&labelColor=111827)](https://rubygems.org/gems/emailit)
5
+ [![License](https://img.shields.io/github/license/emailit/emailit-ruby?style=for-the-badge&labelColor=111827)](https://github.com/emailit/emailit-ruby/blob/main/LICENSE)
6
+
7
+ The official Ruby and Rails SDK for the [Emailit](https://emailit.com) Email API.
8
+
9
+ ## Requirements
10
+
11
+ - Ruby 3.0+
12
+ - No external dependencies (uses `net/http` from stdlib)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ gem install emailit
18
+ ```
19
+
20
+ Or add to your Gemfile:
21
+
22
+ ```ruby
23
+ gem "emailit"
24
+ ```
25
+
26
+ ## Getting Started
27
+
28
+ ```ruby
29
+ require "emailit"
30
+
31
+ client = Emailit::EmailitClient.new("your_api_key")
32
+
33
+ email = client.emails.send(
34
+ from: "hello@yourdomain.com",
35
+ to: ["user@example.com"],
36
+ subject: "Hello from Emailit",
37
+ html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
38
+ )
39
+
40
+ puts email.id # em_abc123...
41
+ puts email.status # pending
42
+ ```
43
+
44
+ All service methods return typed resource objects (`Email`, `Domain`, `Contact`, etc.) with direct property access -- just like the Stripe SDK.
45
+
46
+ ## Available Services
47
+
48
+ | Service | Method | Description |
49
+ |---------|--------|-------------|
50
+ | Emails | `client.emails` | Send, list, get, cancel, retry emails |
51
+ | Domains | `client.domains` | Create, verify, list, manage sending domains |
52
+ | API Keys | `client.api_keys` | Create, list, manage API keys |
53
+ | Audiences | `client.audiences` | Create, list, manage audiences |
54
+ | Subscribers | `client.subscribers` | Add, list, manage subscribers in audiences |
55
+ | Templates | `client.templates` | Create, list, publish email templates |
56
+ | Suppressions | `client.suppressions` | Create, list, manage suppressed addresses |
57
+ | Email Verifications | `client.email_verifications` | Verify email addresses |
58
+ | Email Verification Lists | `client.email_verification_lists` | Create, list, get results, export |
59
+ | Webhooks | `client.webhooks` | Create, list, manage webhooks |
60
+ | Contacts | `client.contacts` | Create, list, manage contacts |
61
+ | Events | `client.events` | List and retrieve events |
62
+
63
+ ## Usage
64
+
65
+ ### Emails
66
+
67
+ #### Send an email
68
+
69
+ ```ruby
70
+ email = client.emails.send(
71
+ from: "hello@yourdomain.com",
72
+ to: ["user@example.com"],
73
+ subject: "Hello from Emailit",
74
+ html: "<h1>Welcome!</h1>"
75
+ )
76
+
77
+ puts email.id
78
+ puts email.status
79
+ ```
80
+
81
+ #### Send with a template
82
+
83
+ ```ruby
84
+ email = client.emails.send(
85
+ from: "hello@yourdomain.com",
86
+ to: "user@example.com",
87
+ template: "welcome_email",
88
+ variables: {
89
+ name: "John Doe",
90
+ company: "Acme Inc"
91
+ }
92
+ )
93
+ ```
94
+
95
+ #### Send with attachments
96
+
97
+ ```ruby
98
+ email = client.emails.send(
99
+ from: "invoices@yourdomain.com",
100
+ to: "customer@example.com",
101
+ subject: "Your Invoice #12345",
102
+ html: "<p>Please find your invoice attached.</p>",
103
+ attachments: [
104
+ {
105
+ filename: "invoice.pdf",
106
+ content: Base64.strict_encode64(File.read("invoice.pdf")),
107
+ content_type: "application/pdf"
108
+ }
109
+ ]
110
+ )
111
+ ```
112
+
113
+ #### Schedule an email
114
+
115
+ ```ruby
116
+ email = client.emails.send(
117
+ from: "reminders@yourdomain.com",
118
+ to: "user@example.com",
119
+ subject: "Appointment Reminder",
120
+ html: "<p>Your appointment is tomorrow at 2 PM.</p>",
121
+ scheduled_at: "2026-01-10T09:00:00Z"
122
+ )
123
+
124
+ puts email.status # scheduled
125
+ puts email.scheduled_at # 2026-01-10T09:00:00Z
126
+ ```
127
+
128
+ #### List emails
129
+
130
+ ```ruby
131
+ emails = client.emails.list(page: 1, limit: 10)
132
+
133
+ emails.each do |email|
134
+ puts "#{email.id} — #{email.status}"
135
+ end
136
+
137
+ if emails.has_more?
138
+ # fetch next page
139
+ end
140
+ ```
141
+
142
+ #### Cancel / Retry
143
+
144
+ ```ruby
145
+ client.emails.cancel("em_abc123")
146
+ client.emails.retry("em_abc123")
147
+ ```
148
+
149
+ ---
150
+
151
+ ### Domains
152
+
153
+ ```ruby
154
+ # Create a domain
155
+ domain = client.domains.create(
156
+ name: "example.com",
157
+ track_loads: true,
158
+ track_clicks: true
159
+ )
160
+ puts domain.id
161
+
162
+ # Verify DNS
163
+ domain = client.domains.verify("sd_123")
164
+
165
+ # List all domains
166
+ domains = client.domains.list
167
+
168
+ # Get a domain
169
+ domain = client.domains.get("sd_123")
170
+
171
+ # Update a domain
172
+ domain = client.domains.update("sd_123", track_clicks: false)
173
+
174
+ # Delete a domain
175
+ client.domains.delete("sd_123")
176
+ ```
177
+
178
+ ---
179
+
180
+ ### API Keys
181
+
182
+ ```ruby
183
+ # Create an API key
184
+ key = client.api_keys.create(
185
+ name: "Production Key",
186
+ scope: "full"
187
+ )
188
+ puts key.key # only available on create
189
+
190
+ # List all API keys
191
+ keys = client.api_keys.list
192
+
193
+ # Get an API key
194
+ key = client.api_keys.get("ak_123")
195
+
196
+ # Update an API key
197
+ client.api_keys.update("ak_123", name: "Renamed Key")
198
+
199
+ # Delete an API key
200
+ client.api_keys.delete("ak_123")
201
+ ```
202
+
203
+ ---
204
+
205
+ ### Audiences
206
+
207
+ ```ruby
208
+ # Create an audience
209
+ audience = client.audiences.create(name: "Newsletter")
210
+ puts audience.id
211
+ puts audience.token
212
+
213
+ # List audiences
214
+ audiences = client.audiences.list
215
+
216
+ # Get an audience
217
+ audience = client.audiences.get("aud_123")
218
+
219
+ # Update an audience
220
+ client.audiences.update("aud_123", name: "Updated Newsletter")
221
+
222
+ # Delete an audience
223
+ client.audiences.delete("aud_123")
224
+ ```
225
+
226
+ ---
227
+
228
+ ### Subscribers
229
+
230
+ Subscribers belong to an audience, so the audience ID is always the first argument.
231
+
232
+ ```ruby
233
+ # Add a subscriber
234
+ subscriber = client.subscribers.create("aud_123",
235
+ email: "user@example.com",
236
+ first_name: "John",
237
+ last_name: "Doe"
238
+ )
239
+
240
+ # List subscribers in an audience
241
+ subscribers = client.subscribers.list("aud_123")
242
+
243
+ # Get a subscriber
244
+ subscriber = client.subscribers.get("aud_123", "sub_456")
245
+
246
+ # Update a subscriber
247
+ client.subscribers.update("aud_123", "sub_456", first_name: "Jane")
248
+
249
+ # Delete a subscriber
250
+ client.subscribers.delete("aud_123", "sub_456")
251
+ ```
252
+
253
+ ---
254
+
255
+ ### Templates
256
+
257
+ ```ruby
258
+ # Create a template
259
+ result = client.templates.create(
260
+ name: "Welcome",
261
+ subject: "Welcome!",
262
+ html: "<h1>Hi {{name}}</h1>"
263
+ )
264
+
265
+ # List templates
266
+ templates = client.templates.list
267
+
268
+ # Get a template
269
+ template = client.templates.get("tem_123")
270
+
271
+ # Update a template
272
+ client.templates.update("tem_123", subject: "New Subject")
273
+
274
+ # Publish a template
275
+ client.templates.publish("tem_123")
276
+
277
+ # Delete a template
278
+ client.templates.delete("tem_123")
279
+ ```
280
+
281
+ ---
282
+
283
+ ### Suppressions
284
+
285
+ ```ruby
286
+ # Create a suppression
287
+ suppression = client.suppressions.create(
288
+ email: "spam@example.com",
289
+ type: "hard_bounce",
290
+ reason: "Manual suppression"
291
+ )
292
+
293
+ # List suppressions
294
+ suppressions = client.suppressions.list
295
+
296
+ # Get a suppression
297
+ suppression = client.suppressions.get("sup_123")
298
+
299
+ # Update a suppression
300
+ client.suppressions.update("sup_123", reason: "Updated")
301
+
302
+ # Delete a suppression
303
+ client.suppressions.delete("sup_123")
304
+ ```
305
+
306
+ ---
307
+
308
+ ### Email Verifications
309
+
310
+ ```ruby
311
+ result = client.email_verifications.verify(email: "test@example.com")
312
+
313
+ puts result.status # valid
314
+ puts result.score # 0.95
315
+ puts result.risk # low
316
+ ```
317
+
318
+ ---
319
+
320
+ ### Email Verification Lists
321
+
322
+ ```ruby
323
+ # Create a verification list
324
+ list = client.email_verification_lists.create(
325
+ name: "Marketing List Q1",
326
+ emails: [
327
+ "user1@example.com",
328
+ "user2@example.com",
329
+ "user3@example.com"
330
+ ]
331
+ )
332
+ puts list.id # evl_abc123...
333
+ puts list.status # pending
334
+
335
+ # List all verification lists
336
+ lists = client.email_verification_lists.list
337
+
338
+ # Get a verification list
339
+ list = client.email_verification_lists.get("evl_abc123")
340
+ puts list.stats["successful_verifications"]
341
+
342
+ # Get verification results
343
+ results = client.email_verification_lists.results("evl_abc123", page: 1, limit: 50)
344
+
345
+ results.each do |result|
346
+ puts "#{result.email} — #{result.result}"
347
+ end
348
+
349
+ # Export results as XLSX
350
+ response = client.email_verification_lists.export("evl_abc123")
351
+ File.write("results.xlsx", response.body)
352
+ ```
353
+
354
+ ---
355
+
356
+ ### Webhooks
357
+
358
+ ```ruby
359
+ # Create a webhook
360
+ webhook = client.webhooks.create(
361
+ name: "My Webhook",
362
+ url: "https://example.com/hook",
363
+ all_events: true,
364
+ enabled: true
365
+ )
366
+ puts webhook.id
367
+
368
+ # List webhooks
369
+ webhooks = client.webhooks.list
370
+
371
+ # Get a webhook
372
+ webhook = client.webhooks.get("wh_123")
373
+
374
+ # Update a webhook
375
+ client.webhooks.update("wh_123", enabled: false)
376
+
377
+ # Delete a webhook
378
+ client.webhooks.delete("wh_123")
379
+ ```
380
+
381
+ ---
382
+
383
+ ### Contacts
384
+
385
+ ```ruby
386
+ # Create a contact
387
+ contact = client.contacts.create(
388
+ email: "user@example.com",
389
+ first_name: "John",
390
+ last_name: "Doe"
391
+ )
392
+ puts contact.id
393
+
394
+ # List contacts
395
+ contacts = client.contacts.list
396
+
397
+ # Get a contact
398
+ contact = client.contacts.get("con_123")
399
+
400
+ # Update a contact
401
+ client.contacts.update("con_123", first_name: "Jane")
402
+
403
+ # Delete a contact
404
+ client.contacts.delete("con_123")
405
+ ```
406
+
407
+ ---
408
+
409
+ ### Events
410
+
411
+ ```ruby
412
+ # List events
413
+ events = client.events.list(type: "email.delivered")
414
+
415
+ events.each do |event|
416
+ puts event.type
417
+ end
418
+
419
+ # Get an event
420
+ event = client.events.get("evt_123")
421
+ puts event.type
422
+ puts event.data["email_id"]
423
+ ```
424
+
425
+ ## Webhook Events
426
+
427
+ The SDK provides typed event classes for all Emailit webhook event types under the `Emailit::Events` namespace, plus a `WebhookSignature` class for verifying webhook request signatures.
428
+
429
+ ### Verifying Webhook Signatures
430
+
431
+ ```ruby
432
+ require "emailit"
433
+
434
+ raw_body = request.body.read
435
+ signature = request.headers["X-Emailit-Signature"]
436
+ timestamp = request.headers["X-Emailit-Timestamp"]
437
+ secret = "your_webhook_signing_secret"
438
+
439
+ begin
440
+ event = Emailit::WebhookSignature.verify(raw_body, signature, timestamp, secret)
441
+
442
+ # event is automatically typed based on the event type
443
+ puts event.type # e.g. "email.delivered"
444
+ puts event.event_id # e.g. "evt_abc123"
445
+
446
+ # Access the event data
447
+ data = event.event_data
448
+
449
+ case event
450
+ when Emailit::Events::EmailDelivered
451
+ handle_delivered(event)
452
+ when Emailit::Events::EmailBounced
453
+ handle_bounce(event)
454
+ when Emailit::Events::ContactCreated
455
+ handle_new_contact(event)
456
+ else
457
+ puts "Unhandled: #{event.type}"
458
+ end
459
+ rescue Emailit::ApiError => e
460
+ render plain: e.message, status: 401
461
+ end
462
+ ```
463
+
464
+ You can disable replay protection by passing `tolerance: nil`, or set a custom tolerance in seconds:
465
+
466
+ ```ruby
467
+ # Skip replay check
468
+ event = Emailit::WebhookSignature.verify(raw_body, signature, timestamp, secret, tolerance: nil)
469
+
470
+ # Custom 10-minute tolerance
471
+ event = Emailit::WebhookSignature.verify(raw_body, signature, timestamp, secret, tolerance: 600)
472
+ ```
473
+
474
+ ### Available Event Types
475
+
476
+ **Emails:** `email.accepted`, `email.scheduled`, `email.delivered`, `email.bounced`, `email.attempted`, `email.failed`, `email.rejected`, `email.suppressed`, `email.received`, `email.complained`, `email.clicked`, `email.loaded`
477
+
478
+ **Domains:** `domain.created`, `domain.updated`, `domain.deleted`
479
+
480
+ **Audiences:** `audience.created`, `audience.updated`, `audience.deleted`
481
+
482
+ **Subscribers:** `subscriber.created`, `subscriber.updated`, `subscriber.deleted`
483
+
484
+ **Contacts:** `contact.created`, `contact.updated`, `contact.deleted`
485
+
486
+ **Templates:** `template.created`, `template.updated`, `template.deleted`
487
+
488
+ **Suppressions:** `suppression.created`, `suppression.updated`, `suppression.deleted`
489
+
490
+ **Email Verifications:** `email_verification.created`, `email_verification.updated`, `email_verification.deleted`
491
+
492
+ **Email Verification Lists:** `email_verification_list.created`, `email_verification_list.updated`, `email_verification_list.deleted`
493
+
494
+ Each event type has a corresponding class under `Emailit::Events::` (e.g. `Emailit::Events::EmailDelivered`, `Emailit::Events::DomainCreated`). You can use `is_a?` checks or the `EVENT_TYPE` constant for routing:
495
+
496
+ ```ruby
497
+ case event
498
+ when Emailit::Events::EmailDelivered
499
+ handle_delivered(event)
500
+ when Emailit::Events::EmailBounced
501
+ handle_bounce(event)
502
+ when Emailit::Events::ContactCreated
503
+ handle_new_contact(event)
504
+ else
505
+ puts "Unhandled: #{event.type}"
506
+ end
507
+ ```
508
+
509
+ ## Ruby on Rails (ActionMailer)
510
+
511
+ The SDK natively supports Ruby on Rails via ActionMailer. When Rails is detected, a `:emailit` delivery method is automatically registered.
512
+
513
+ ### Configuration
514
+
515
+ ```ruby
516
+ # config/environments/production.rb
517
+ config.action_mailer.delivery_method = :emailit
518
+ config.action_mailer.emailit_settings = { api_key: ENV["EMAILIT_API_KEY"] }
519
+ ```
520
+
521
+ ### Usage
522
+
523
+ Once configured, all your existing mailers work with Emailit automatically:
524
+
525
+ ```ruby
526
+ class UserMailer < ApplicationMailer
527
+ def welcome(user)
528
+ @user = user
529
+ mail(
530
+ to: @user.email,
531
+ subject: "Welcome to our app!"
532
+ )
533
+ end
534
+ end
535
+
536
+ # Send it
537
+ UserMailer.welcome(user).deliver_now
538
+ UserMailer.welcome(user).deliver_later
539
+ ```
540
+
541
+ The delivery method automatically converts `Mail::Message` objects to Emailit API params, including:
542
+
543
+ - `from`, `to`, `cc`, `bcc`, `reply_to`
544
+ - `subject`
545
+ - HTML and plain-text bodies (multipart supported)
546
+ - File attachments (base64-encoded)
547
+
548
+
549
+ ```ruby
550
+ class NotificationMailer < ApplicationMailer
551
+ def invoice(user, pdf_data)
552
+ attachments["invoice.pdf"] = {
553
+ mime_type: "application/pdf",
554
+ content: pdf_data
555
+ }
556
+
557
+ mail(
558
+ to: user.email,
559
+ subject: "Your Invoice"
560
+ )
561
+ end
562
+ end
563
+ ```
564
+
565
+ ## Error Handling
566
+
567
+ The SDK raises typed exceptions for API errors:
568
+
569
+ ```ruby
570
+ begin
571
+ client.emails.send(
572
+ from: "hello@yourdomain.com",
573
+ to: "user@example.com",
574
+ subject: "Hello",
575
+ html: "<h1>Hi</h1>"
576
+ )
577
+ rescue Emailit::AuthenticationError => e
578
+ # Invalid API key (401)
579
+ rescue Emailit::InvalidRequestError => e
580
+ # Bad request or not found (400, 404)
581
+ rescue Emailit::RateLimitError => e
582
+ # Too many requests (429)
583
+ rescue Emailit::UnprocessableEntityError => e
584
+ # Validation failed (422)
585
+ rescue Emailit::ConnectionError => e
586
+ # Network error
587
+ rescue Emailit::ApiError => e
588
+ # Any other API error
589
+ puts e.http_status
590
+ puts e.http_body
591
+ puts e.json_body
592
+ end
593
+ ```
594
+
595
+ ## License
596
+
597
+ MIT -- see [LICENSE](LICENSE) for details.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ class ApiResource < EmailitObject
5
+ OBJECT_NAME = ""
6
+
7
+ def self.class_url
8
+ "/v2/#{self::OBJECT_NAME}s"
9
+ end
10
+
11
+ def self.resource_url(id)
12
+ "#{class_url}/#{CGI.escape(id)}"
13
+ end
14
+
15
+ def instance_url
16
+ id = self["id"]
17
+ raise "Could not determine instance URL: #{self.class} has no 'id'." unless id
18
+
19
+ self.class.resource_url(id)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ class ApiResponse
5
+ attr_reader :status_code, :headers, :body, :json
6
+
7
+ def initialize(status_code, headers, body)
8
+ @status_code = status_code
9
+ @headers = headers
10
+ @body = body
11
+ @json = begin
12
+ JSON.parse(body)
13
+ rescue JSON::ParserError
14
+ nil
15
+ end
16
+ end
17
+ end
18
+ end