sendly 1.0.8 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34504d5c13852809e988bb03d13a1a0fce105fc1034854a8a1f6665316ea346e
4
- data.tar.gz: dd2474f813d01734670a9a2bc09b2094afc379df5ab3f77abcc4d9adb44c4f89
3
+ metadata.gz: 5f970f1462ecd9facfec70a5ba0b167e5da0c697f1dcb96203874f1325cb1b03
4
+ data.tar.gz: d5b5ab964db06976dd6a848d137f5b2b8af07607c795ad4dd96d729ae4c37285
5
5
  SHA512:
6
- metadata.gz: e0bccfd75de393fd5cd22edd80b6fb07781f63825a01245c489cedf2439ee907e8c51e6d8748bf557a5839aed3fccc43e5d6b85ca190a57127744d8273aad339
7
- data.tar.gz: 6bb92db07cc8bb89c983270b4d05bfc2872fb2dde25e6fb099fdfc98b28de9d950e86aaddd32f3e0b42d437517e7df041838caa5f22d1074aee7864f679d33fa
6
+ metadata.gz: d110ea4e88716ef5299c77d37d47ac55817f796113e7ebdadb21aed864d1ad1fcb2f082c724b50e20a4c473fd762ae69726e4b1b0907a21a982886a6719a1fc5
7
+ data.tar.gz: 31ac699cd4fc8305ba04c3ba829fcc5be63436811a6be8ce9606b69641ee401b9b0d17b2b91afccc3210677d006bd244efc35ef9ad6ce7cc4838f724dbb7fc72
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sendly (1.0.8)
4
+ sendly (1.1.0)
5
5
  faraday (~> 2.0)
6
6
  faraday-retry (~> 2.0)
7
7
 
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sendly
4
+ # Account resource for accessing account information, credits, and API keys
5
+ class AccountResource
6
+ # @param client [Sendly::Client] The API client
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # Get account information
12
+ #
13
+ # @return [Sendly::Account]
14
+ def get
15
+ response = @client.get("/account")
16
+ Account.new(response)
17
+ end
18
+
19
+ # Get credit balance
20
+ #
21
+ # @return [Sendly::Credits]
22
+ #
23
+ # @example
24
+ # credits = client.account.credits
25
+ # puts "Available: #{credits.available_balance} credits"
26
+ def credits
27
+ response = @client.get("/credits")
28
+ Credits.new(response)
29
+ end
30
+
31
+ # Get credit transaction history
32
+ #
33
+ # @param limit [Integer, nil] Maximum number of transactions to return
34
+ # @param offset [Integer, nil] Number of transactions to skip
35
+ # @return [Array<Sendly::CreditTransaction>]
36
+ def transactions(limit: nil, offset: nil)
37
+ params = {}
38
+ params[:limit] = limit if limit
39
+ params[:offset] = offset if offset
40
+
41
+ response = @client.get("/credits/transactions", params)
42
+ response.map { |data| CreditTransaction.new(data) }
43
+ end
44
+
45
+ # List API keys for the account
46
+ #
47
+ # @return [Array<Sendly::ApiKey>]
48
+ def api_keys
49
+ response = @client.get("/keys")
50
+ response.map { |data| ApiKey.new(data) }
51
+ end
52
+
53
+ # Get a specific API key by ID
54
+ #
55
+ # @param key_id [String] API key ID
56
+ # @return [Sendly::ApiKey]
57
+ def api_key(key_id)
58
+ response = @client.get("/keys/#{key_id}")
59
+ ApiKey.new(response)
60
+ end
61
+
62
+ # Get usage statistics for an API key
63
+ #
64
+ # @param key_id [String] API key ID
65
+ # @return [Hash] Usage statistics
66
+ def api_key_usage(key_id)
67
+ @client.get("/keys/#{key_id}/usage")
68
+ end
69
+ end
70
+ end
data/lib/sendly/client.rb CHANGED
@@ -45,6 +45,20 @@ module Sendly
45
45
  @messages ||= Messages.new(self)
46
46
  end
47
47
 
48
+ # Access the Webhooks resource
49
+ #
50
+ # @return [Sendly::WebhooksResource]
51
+ def webhooks
52
+ @webhooks ||= WebhooksResource.new(self)
53
+ end
54
+
55
+ # Access the Account resource
56
+ #
57
+ # @return [Sendly::AccountResource]
58
+ def account
59
+ @account ||= AccountResource.new(self)
60
+ end
61
+
48
62
  # Make a GET request
49
63
  #
50
64
  # @param path [String] API path
@@ -63,6 +77,15 @@ module Sendly
63
77
  request(:post, path, body: body)
64
78
  end
65
79
 
80
+ # Make a PATCH request
81
+ #
82
+ # @param path [String] API path
83
+ # @param body [Hash] Request body
84
+ # @return [Hash] Response body
85
+ def patch(path, body = {})
86
+ request(:patch, path, body: body)
87
+ end
88
+
66
89
  # Make a DELETE request
67
90
  #
68
91
  # @param path [String] API path
@@ -137,6 +160,8 @@ module Sendly
137
160
  Net::HTTP::Get.new(uri)
138
161
  when :post
139
162
  Net::HTTP::Post.new(uri)
163
+ when :patch
164
+ Net::HTTP::Patch.new(uri)
140
165
  when :delete
141
166
  Net::HTTP::Delete.new(uri)
142
167
  else
data/lib/sendly/types.rb CHANGED
@@ -18,6 +18,9 @@ module Sendly
18
18
  # @return [String] Delivery status
19
19
  attr_reader :status
20
20
 
21
+ # @return [String] Message direction (outbound or inbound)
22
+ attr_reader :direction
23
+
21
24
  # @return [String, nil] Error message if failed
22
25
  attr_reader :error
23
26
 
@@ -30,14 +33,29 @@ module Sendly
30
33
  # @return [Boolean] Whether sent in sandbox mode
31
34
  attr_reader :is_sandbox
32
35
 
36
+ # @return [String, nil] How the message was sent (number_pool, alphanumeric, sandbox)
37
+ attr_reader :sender_type
38
+
39
+ # @return [String, nil] Telnyx message ID for tracking
40
+ attr_reader :telnyx_message_id
41
+
42
+ # @return [String, nil] Warning message
43
+ attr_reader :warning
44
+
45
+ # @return [String, nil] Note about sender behavior
46
+ attr_reader :sender_note
47
+
33
48
  # @return [Time, nil] Creation timestamp
34
49
  attr_reader :created_at
35
50
 
36
51
  # @return [Time, nil] Delivery timestamp
37
52
  attr_reader :delivered_at
38
53
 
39
- # Message status constants
40
- STATUSES = %w[queued sending sent delivered failed].freeze
54
+ # Message status constants (sending removed - doesn't exist in database)
55
+ STATUSES = %w[queued sent delivered failed].freeze
56
+
57
+ # Sender type constants
58
+ SENDER_TYPES = %w[number_pool alphanumeric sandbox].freeze
41
59
 
42
60
  def initialize(data)
43
61
  @id = data["id"]
@@ -45,10 +63,15 @@ module Sendly
45
63
  @from = data["from"]
46
64
  @text = data["text"]
47
65
  @status = data["status"]
66
+ @direction = data["direction"] || "outbound"
48
67
  @error = data["error"]
49
68
  @segments = data["segments"] || 1
50
69
  @credits_used = data["creditsUsed"] || 0
51
70
  @is_sandbox = data["isSandbox"] || false
71
+ @sender_type = data["senderType"]
72
+ @telnyx_message_id = data["telnyxMessageId"]
73
+ @warning = data["warning"]
74
+ @sender_note = data["senderNote"]
52
75
  @created_at = parse_time(data["createdAt"])
53
76
  @delivered_at = parse_time(data["deliveredAt"])
54
77
  end
@@ -80,10 +103,15 @@ module Sendly
80
103
  from: from,
81
104
  text: text,
82
105
  status: status,
106
+ direction: direction,
83
107
  error: error,
84
108
  segments: segments,
85
109
  credits_used: credits_used,
86
110
  is_sandbox: is_sandbox,
111
+ sender_type: sender_type,
112
+ telnyx_message_id: telnyx_message_id,
113
+ warning: warning,
114
+ sender_note: sender_note,
87
115
  created_at: created_at&.iso8601,
88
116
  delivered_at: delivered_at&.iso8601
89
117
  }.compact
@@ -159,4 +187,269 @@ module Sendly
159
187
  data.last
160
188
  end
161
189
  end
190
+
191
+ # ============================================================================
192
+ # Webhooks
193
+ # ============================================================================
194
+
195
+ # Represents a configured webhook endpoint
196
+ class Webhook
197
+ attr_reader :id, :url, :events, :description, :is_active, :failure_count,
198
+ :last_failure_at, :circuit_state, :circuit_opened_at, :api_version,
199
+ :metadata, :created_at, :updated_at, :total_deliveries,
200
+ :successful_deliveries, :success_rate, :last_delivery_at
201
+
202
+ # Circuit state constants
203
+ CIRCUIT_STATES = %w[closed open half_open].freeze
204
+
205
+ def initialize(data)
206
+ @id = data["id"]
207
+ @url = data["url"]
208
+ @events = data["events"] || []
209
+ @description = data["description"]
210
+ # Handle both snake_case API response and camelCase
211
+ @is_active = data["is_active"] || data["isActive"] || false
212
+ @failure_count = data["failure_count"] || data["failureCount"] || 0
213
+ @last_failure_at = parse_time(data["last_failure_at"] || data["lastFailureAt"])
214
+ @circuit_state = data["circuit_state"] || data["circuitState"] || "closed"
215
+ @circuit_opened_at = parse_time(data["circuit_opened_at"] || data["circuitOpenedAt"])
216
+ @api_version = data["api_version"] || data["apiVersion"] || "2024-01"
217
+ @metadata = data["metadata"] || {}
218
+ @created_at = parse_time(data["created_at"] || data["createdAt"])
219
+ @updated_at = parse_time(data["updated_at"] || data["updatedAt"])
220
+ @total_deliveries = data["total_deliveries"] || data["totalDeliveries"] || 0
221
+ @successful_deliveries = data["successful_deliveries"] || data["successfulDeliveries"] || 0
222
+ @success_rate = data["success_rate"] || data["successRate"] || 0
223
+ @last_delivery_at = parse_time(data["last_delivery_at"] || data["lastDeliveryAt"])
224
+ end
225
+
226
+ def active?
227
+ is_active
228
+ end
229
+
230
+ def circuit_open?
231
+ circuit_state == "open"
232
+ end
233
+
234
+ def to_h
235
+ {
236
+ id: id, url: url, events: events, description: description,
237
+ is_active: is_active, failure_count: failure_count,
238
+ circuit_state: circuit_state, api_version: api_version,
239
+ metadata: metadata, total_deliveries: total_deliveries,
240
+ successful_deliveries: successful_deliveries, success_rate: success_rate
241
+ }.compact
242
+ end
243
+
244
+ private
245
+
246
+ def parse_time(value)
247
+ return nil if value.nil?
248
+ Time.parse(value)
249
+ rescue ArgumentError
250
+ nil
251
+ end
252
+ end
253
+
254
+ # Webhook with secret (returned on creation)
255
+ class WebhookCreatedResponse < Webhook
256
+ attr_reader :secret
257
+
258
+ def initialize(data)
259
+ super(data)
260
+ @secret = data["secret"]
261
+ end
262
+ end
263
+
264
+ # Represents a webhook delivery attempt
265
+ class WebhookDelivery
266
+ attr_reader :id, :webhook_id, :event_id, :event_type, :attempt_number,
267
+ :max_attempts, :status, :response_status_code, :response_time_ms,
268
+ :error_message, :error_code, :next_retry_at, :created_at, :delivered_at
269
+
270
+ # Delivery status constants
271
+ STATUSES = %w[pending delivered failed cancelled].freeze
272
+
273
+ def initialize(data)
274
+ @id = data["id"]
275
+ @webhook_id = data["webhook_id"] || data["webhookId"]
276
+ @event_id = data["event_id"] || data["eventId"]
277
+ @event_type = data["event_type"] || data["eventType"]
278
+ @attempt_number = data["attempt_number"] || data["attemptNumber"] || 1
279
+ @max_attempts = data["max_attempts"] || data["maxAttempts"] || 6
280
+ @status = data["status"]
281
+ @response_status_code = data["response_status_code"] || data["responseStatusCode"]
282
+ @response_time_ms = data["response_time_ms"] || data["responseTimeMs"]
283
+ @error_message = data["error_message"] || data["errorMessage"]
284
+ @error_code = data["error_code"] || data["errorCode"]
285
+ @next_retry_at = parse_time(data["next_retry_at"] || data["nextRetryAt"])
286
+ @created_at = parse_time(data["created_at"] || data["createdAt"])
287
+ @delivered_at = parse_time(data["delivered_at"] || data["deliveredAt"])
288
+ end
289
+
290
+ def delivered?
291
+ status == "delivered"
292
+ end
293
+
294
+ def failed?
295
+ status == "failed"
296
+ end
297
+
298
+ private
299
+
300
+ def parse_time(value)
301
+ return nil if value.nil?
302
+ Time.parse(value)
303
+ rescue ArgumentError
304
+ nil
305
+ end
306
+ end
307
+
308
+ # Result of testing a webhook
309
+ class WebhookTestResult
310
+ attr_reader :success, :status_code, :response_time_ms, :error
311
+
312
+ def initialize(data)
313
+ @success = data["success"]
314
+ @status_code = data["status_code"] || data["statusCode"]
315
+ @response_time_ms = data["response_time_ms"] || data["responseTimeMs"]
316
+ @error = data["error"]
317
+ end
318
+
319
+ def success?
320
+ success
321
+ end
322
+ end
323
+
324
+ # Result of rotating webhook secret
325
+ class WebhookSecretRotation
326
+ attr_reader :webhook, :new_secret, :old_secret_expires_at, :message
327
+
328
+ def initialize(data)
329
+ @webhook = Webhook.new(data["webhook"])
330
+ @new_secret = data["new_secret"] || data["newSecret"]
331
+ @old_secret_expires_at = parse_time(data["old_secret_expires_at"] || data["oldSecretExpiresAt"])
332
+ @message = data["message"]
333
+ end
334
+
335
+ private
336
+
337
+ def parse_time(value)
338
+ return nil if value.nil?
339
+ Time.parse(value)
340
+ rescue ArgumentError
341
+ nil
342
+ end
343
+ end
344
+
345
+ # ============================================================================
346
+ # Account & Credits
347
+ # ============================================================================
348
+
349
+ # Represents account information
350
+ class Account
351
+ attr_reader :id, :email, :name, :created_at
352
+
353
+ def initialize(data)
354
+ @id = data["id"]
355
+ @email = data["email"]
356
+ @name = data["name"]
357
+ @created_at = parse_time(data["created_at"] || data["createdAt"])
358
+ end
359
+
360
+ private
361
+
362
+ def parse_time(value)
363
+ return nil if value.nil?
364
+ Time.parse(value)
365
+ rescue ArgumentError
366
+ nil
367
+ end
368
+ end
369
+
370
+ # Represents credit balance information
371
+ class Credits
372
+ attr_reader :balance, :reserved_balance, :available_balance
373
+
374
+ def initialize(data)
375
+ @balance = data["balance"] || 0
376
+ @reserved_balance = data["reserved_balance"] || data["reservedBalance"] || 0
377
+ @available_balance = data["available_balance"] || data["availableBalance"] || 0
378
+ end
379
+ end
380
+
381
+ # Represents a credit transaction
382
+ class CreditTransaction
383
+ attr_reader :id, :type, :amount, :balance_after, :description, :message_id, :created_at
384
+
385
+ # Transaction type constants
386
+ TYPES = %w[purchase usage refund adjustment bonus].freeze
387
+
388
+ def initialize(data)
389
+ @id = data["id"]
390
+ @type = data["type"]
391
+ @amount = data["amount"] || 0
392
+ @balance_after = data["balance_after"] || data["balanceAfter"] || 0
393
+ @description = data["description"]
394
+ @message_id = data["message_id"] || data["messageId"]
395
+ @created_at = parse_time(data["created_at"] || data["createdAt"])
396
+ end
397
+
398
+ def credit?
399
+ amount > 0
400
+ end
401
+
402
+ def debit?
403
+ amount < 0
404
+ end
405
+
406
+ private
407
+
408
+ def parse_time(value)
409
+ return nil if value.nil?
410
+ Time.parse(value)
411
+ rescue ArgumentError
412
+ nil
413
+ end
414
+ end
415
+
416
+ # Represents an API key
417
+ class ApiKey
418
+ attr_reader :id, :name, :type, :prefix, :last_four, :permissions,
419
+ :created_at, :last_used_at, :expires_at, :is_revoked
420
+
421
+ def initialize(data)
422
+ @id = data["id"]
423
+ @name = data["name"]
424
+ @type = data["type"]
425
+ @prefix = data["prefix"]
426
+ @last_four = data["last_four"] || data["lastFour"]
427
+ @permissions = data["permissions"] || []
428
+ @created_at = parse_time(data["created_at"] || data["createdAt"])
429
+ @last_used_at = parse_time(data["last_used_at"] || data["lastUsedAt"])
430
+ @expires_at = parse_time(data["expires_at"] || data["expiresAt"])
431
+ @is_revoked = data["is_revoked"] || data["isRevoked"] || false
432
+ end
433
+
434
+ def test?
435
+ type == "test"
436
+ end
437
+
438
+ def live?
439
+ type == "live"
440
+ end
441
+
442
+ def revoked?
443
+ is_revoked
444
+ end
445
+
446
+ private
447
+
448
+ def parse_time(value)
449
+ return nil if value.nil?
450
+ Time.parse(value)
451
+ rescue ArgumentError
452
+ nil
453
+ end
454
+ end
162
455
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sendly
4
- VERSION = "1.0.8"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sendly
4
+ # Webhooks resource for managing webhook endpoints
5
+ class WebhooksResource
6
+ # @param client [Sendly::Client] The API client
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # Create a new webhook endpoint
12
+ #
13
+ # @param url [String] HTTPS endpoint URL
14
+ # @param events [Array<String>] Event types to subscribe to
15
+ # @param description [String, nil] Optional description
16
+ # @param metadata [Hash, nil] Custom metadata
17
+ # @return [Sendly::WebhookCreatedResponse]
18
+ #
19
+ # @example
20
+ # webhook = client.webhooks.create(
21
+ # url: "https://example.com/webhooks",
22
+ # events: ["message.delivered", "message.failed"]
23
+ # )
24
+ # puts "Secret: #{webhook.secret}" # Save this - only shown once!
25
+ def create(url:, events:, description: nil, metadata: nil)
26
+ raise ArgumentError, "Webhook URL must be HTTPS" unless url&.start_with?("https://")
27
+ raise ArgumentError, "At least one event type is required" if events.nil? || events.empty?
28
+
29
+ body = { url: url, events: events }
30
+ body[:description] = description if description
31
+ body[:metadata] = metadata if metadata
32
+
33
+ response = @client.post("/webhooks", body)
34
+ WebhookCreatedResponse.new(response)
35
+ end
36
+
37
+ # List all webhooks
38
+ #
39
+ # @return [Array<Sendly::Webhook>]
40
+ def list
41
+ response = @client.get("/webhooks")
42
+ response.map { |data| Webhook.new(data) }
43
+ end
44
+
45
+ # Get a specific webhook by ID
46
+ #
47
+ # @param webhook_id [String] Webhook ID (whk_xxx)
48
+ # @return [Sendly::Webhook]
49
+ def get(webhook_id)
50
+ validate_webhook_id!(webhook_id)
51
+ response = @client.get("/webhooks/#{webhook_id}")
52
+ Webhook.new(response)
53
+ end
54
+
55
+ # Update a webhook configuration
56
+ #
57
+ # @param webhook_id [String] Webhook ID
58
+ # @param url [String, nil] New URL
59
+ # @param events [Array<String>, nil] New event subscriptions
60
+ # @param description [String, nil] New description
61
+ # @param is_active [Boolean, nil] Enable/disable webhook
62
+ # @param metadata [Hash, nil] Custom metadata
63
+ # @return [Sendly::Webhook]
64
+ def update(webhook_id, url: nil, events: nil, description: nil, is_active: nil, metadata: nil)
65
+ validate_webhook_id!(webhook_id)
66
+ raise ArgumentError, "Webhook URL must be HTTPS" if url && !url.start_with?("https://")
67
+
68
+ body = {}
69
+ body[:url] = url unless url.nil?
70
+ body[:events] = events unless events.nil?
71
+ body[:description] = description unless description.nil?
72
+ body[:is_active] = is_active unless is_active.nil?
73
+ body[:metadata] = metadata unless metadata.nil?
74
+
75
+ response = @client.patch("/webhooks/#{webhook_id}", body)
76
+ Webhook.new(response)
77
+ end
78
+
79
+ # Delete a webhook
80
+ #
81
+ # @param webhook_id [String] Webhook ID
82
+ # @return [void]
83
+ def delete(webhook_id)
84
+ validate_webhook_id!(webhook_id)
85
+ @client.delete("/webhooks/#{webhook_id}")
86
+ nil
87
+ end
88
+
89
+ # Test a webhook endpoint
90
+ #
91
+ # @param webhook_id [String] Webhook ID
92
+ # @return [Sendly::WebhookTestResult]
93
+ def test(webhook_id)
94
+ validate_webhook_id!(webhook_id)
95
+ response = @client.post("/webhooks/#{webhook_id}/test")
96
+ WebhookTestResult.new(response)
97
+ end
98
+
99
+ # Rotate the webhook signing secret
100
+ #
101
+ # @param webhook_id [String] Webhook ID
102
+ # @return [Sendly::WebhookSecretRotation]
103
+ def rotate_secret(webhook_id)
104
+ validate_webhook_id!(webhook_id)
105
+ response = @client.post("/webhooks/#{webhook_id}/rotate-secret")
106
+ WebhookSecretRotation.new(response)
107
+ end
108
+
109
+ # Get delivery history for a webhook
110
+ #
111
+ # @param webhook_id [String] Webhook ID
112
+ # @return [Array<Sendly::WebhookDelivery>]
113
+ def deliveries(webhook_id)
114
+ validate_webhook_id!(webhook_id)
115
+ response = @client.get("/webhooks/#{webhook_id}/deliveries")
116
+ response.map { |data| WebhookDelivery.new(data) }
117
+ end
118
+
119
+ # Retry a failed delivery
120
+ #
121
+ # @param webhook_id [String] Webhook ID
122
+ # @param delivery_id [String] Delivery ID
123
+ # @return [void]
124
+ def retry_delivery(webhook_id, delivery_id)
125
+ validate_webhook_id!(webhook_id)
126
+ validate_delivery_id!(delivery_id)
127
+ @client.post("/webhooks/#{webhook_id}/deliveries/#{delivery_id}/retry")
128
+ nil
129
+ end
130
+
131
+ # List available event types
132
+ #
133
+ # @return [Array<String>]
134
+ def event_types
135
+ response = @client.get("/webhooks/event-types")
136
+ (response["events"] || []).map { |e| e["type"] }
137
+ end
138
+
139
+ private
140
+
141
+ def validate_webhook_id!(webhook_id)
142
+ raise ArgumentError, "Invalid webhook ID format" unless webhook_id&.start_with?("whk_")
143
+ end
144
+
145
+ def validate_delivery_id!(delivery_id)
146
+ raise ArgumentError, "Invalid delivery ID format" unless delivery_id&.start_with?("del_")
147
+ end
148
+ end
149
+ end
data/lib/sendly.rb CHANGED
@@ -9,6 +9,8 @@ require_relative "sendly/types"
9
9
  require_relative "sendly/client"
10
10
  require_relative "sendly/messages"
11
11
  require_relative "sendly/webhooks"
12
+ require_relative "sendly/webhooks_resource"
13
+ require_relative "sendly/account_resource"
12
14
 
13
15
  # Sendly Ruby SDK
14
16
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sendly
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sendly
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-12-20 00:00:00.000000000 Z
11
+ date: 2025-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -123,12 +123,14 @@ files:
123
123
  - examples/list_messages.rb
124
124
  - examples/send_sms.rb
125
125
  - lib/sendly.rb
126
+ - lib/sendly/account_resource.rb
126
127
  - lib/sendly/client.rb
127
128
  - lib/sendly/errors.rb
128
129
  - lib/sendly/messages.rb
129
130
  - lib/sendly/types.rb
130
131
  - lib/sendly/version.rb
131
132
  - lib/sendly/webhooks.rb
133
+ - lib/sendly/webhooks_resource.rb
132
134
  homepage: https://github.com/sendly-live/sendly-ruby
133
135
  licenses:
134
136
  - MIT