sendly 1.1.0 → 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f970f1462ecd9facfec70a5ba0b167e5da0c697f1dcb96203874f1325cb1b03
4
- data.tar.gz: d5b5ab964db06976dd6a848d137f5b2b8af07607c795ad4dd96d729ae4c37285
3
+ metadata.gz: 8287e0f6c55aa228285671746446a7eb04577569e56515ed76571da61a0392b0
4
+ data.tar.gz: 62017808d07348e8d6e5809b367300e11976a754e7c3323c8155d2d9140befb4
5
5
  SHA512:
6
- metadata.gz: d110ea4e88716ef5299c77d37d47ac55817f796113e7ebdadb21aed864d1ad1fcb2f082c724b50e20a4c473fd762ae69726e4b1b0907a21a982886a6719a1fc5
7
- data.tar.gz: 31ac699cd4fc8305ba04c3ba829fcc5be63436811a6be8ce9606b69641ee401b9b0d17b2b91afccc3210677d006bd244efc35ef9ad6ce7cc4838f724dbb7fc72
6
+ metadata.gz: 2c3a234e3caa740975c00ad3c1454dbe24f423385f480f180e33f1ac0ef890bbe019f58606ff3f50bb0b508280d9352997dd20b88a6705d277d493235d3ecc57
7
+ data.tar.gz: 5e7cba5b80ec78994f3de8b0dfe5c3f63f6c00cede6f5a98c3d7a679755386ee543477f588c672c248181dbfc279bd8834c5108f1a1071378f9e51b23ea94c40
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sendly (1.1.0)
4
+ sendly (1.5.1)
5
5
  faraday (~> 2.0)
6
6
  faraday-retry (~> 2.0)
7
7
 
data/README.md CHANGED
@@ -4,17 +4,24 @@ Official Ruby SDK for the Sendly SMS API.
4
4
 
5
5
  ## Installation
6
6
 
7
- ```bash
8
- # gem
9
- gem install sendly
7
+ Add to your Gemfile:
10
8
 
11
- # Bundler (add to Gemfile)
9
+ ```ruby
12
10
  gem 'sendly'
11
+ ```
12
+
13
+ Then run:
13
14
 
14
- # then run
15
+ ```bash
15
16
  bundle install
16
17
  ```
17
18
 
19
+ Or install directly:
20
+
21
+ ```bash
22
+ gem install sendly
23
+ ```
24
+
18
25
  ## Quick Start
19
26
 
20
27
  ```ruby
@@ -75,7 +82,7 @@ Sendly.send_message(to: "+15551234567", text: "Hello!")
75
82
  ```ruby
76
83
  client = Sendly::Client.new(
77
84
  "sk_live_v1_xxx",
78
- base_url: "https://sendly.live/api/v1",
85
+ base_url: "https://api.sendly.live/v1",
79
86
  timeout: 60,
80
87
  max_retries: 5
81
88
  )
data/lib/sendly/client.rb CHANGED
@@ -45,20 +45,6 @@ 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
-
62
48
  # Make a GET request
63
49
  #
64
50
  # @param path [String] API path
@@ -77,15 +63,6 @@ module Sendly
77
63
  request(:post, path, body: body)
78
64
  end
79
65
 
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
-
89
66
  # Make a DELETE request
90
67
  #
91
68
  # @param path [String] API path
@@ -160,8 +137,6 @@ module Sendly
160
137
  Net::HTTP::Get.new(uri)
161
138
  when :post
162
139
  Net::HTTP::Post.new(uri)
163
- when :patch
164
- Net::HTTP::Patch.new(uri)
165
140
  when :delete
166
141
  Net::HTTP::Delete.new(uri)
167
142
  else
data/lib/sendly/types.rb CHANGED
@@ -18,9 +18,6 @@ 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
-
24
21
  # @return [String, nil] Error message if failed
25
22
  attr_reader :error
26
23
 
@@ -33,29 +30,14 @@ module Sendly
33
30
  # @return [Boolean] Whether sent in sandbox mode
34
31
  attr_reader :is_sandbox
35
32
 
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
-
48
33
  # @return [Time, nil] Creation timestamp
49
34
  attr_reader :created_at
50
35
 
51
36
  # @return [Time, nil] Delivery timestamp
52
37
  attr_reader :delivered_at
53
38
 
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
39
+ # Message status constants
40
+ STATUSES = %w[queued sending sent delivered failed].freeze
59
41
 
60
42
  def initialize(data)
61
43
  @id = data["id"]
@@ -63,15 +45,10 @@ module Sendly
63
45
  @from = data["from"]
64
46
  @text = data["text"]
65
47
  @status = data["status"]
66
- @direction = data["direction"] || "outbound"
67
48
  @error = data["error"]
68
49
  @segments = data["segments"] || 1
69
50
  @credits_used = data["creditsUsed"] || 0
70
51
  @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"]
75
52
  @created_at = parse_time(data["createdAt"])
76
53
  @delivered_at = parse_time(data["deliveredAt"])
77
54
  end
@@ -103,15 +80,10 @@ module Sendly
103
80
  from: from,
104
81
  text: text,
105
82
  status: status,
106
- direction: direction,
107
83
  error: error,
108
84
  segments: segments,
109
85
  credits_used: credits_used,
110
86
  is_sandbox: is_sandbox,
111
- sender_type: sender_type,
112
- telnyx_message_id: telnyx_message_id,
113
- warning: warning,
114
- sender_note: sender_note,
115
87
  created_at: created_at&.iso8601,
116
88
  delivered_at: delivered_at&.iso8601
117
89
  }.compact
@@ -187,269 +159,4 @@ module Sendly
187
159
  data.last
188
160
  end
189
161
  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
455
162
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sendly
4
- VERSION = "1.1.0"
4
+ VERSION = "1.5.1"
5
5
  end
data/lib/sendly.rb CHANGED
@@ -9,8 +9,6 @@ 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"
14
12
 
15
13
  # Sendly Ruby SDK
16
14
  #
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sendly
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sendly
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-12-22 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -123,14 +122,12 @@ files:
123
122
  - examples/list_messages.rb
124
123
  - examples/send_sms.rb
125
124
  - lib/sendly.rb
126
- - lib/sendly/account_resource.rb
127
125
  - lib/sendly/client.rb
128
126
  - lib/sendly/errors.rb
129
127
  - lib/sendly/messages.rb
130
128
  - lib/sendly/types.rb
131
129
  - lib/sendly/version.rb
132
130
  - lib/sendly/webhooks.rb
133
- - lib/sendly/webhooks_resource.rb
134
131
  homepage: https://github.com/sendly-live/sendly-ruby
135
132
  licenses:
136
133
  - MIT
@@ -139,7 +136,6 @@ metadata:
139
136
  source_code_uri: https://github.com/sendly-live/sendly-ruby
140
137
  changelog_uri: https://github.com/sendly-live/sendly-ruby/blob/main/CHANGELOG.md
141
138
  documentation_uri: https://sendly.live/docs
142
- post_install_message:
143
139
  rdoc_options: []
144
140
  require_paths:
145
141
  - lib
@@ -154,8 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
150
  - !ruby/object:Gem::Version
155
151
  version: '0'
156
152
  requirements: []
157
- rubygems_version: 3.4.19
158
- signing_key:
153
+ rubygems_version: 3.6.9
159
154
  specification_version: 4
160
155
  summary: Official Ruby SDK for the Sendly SMS API
161
156
  test_files: []
@@ -1,70 +0,0 @@
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
@@ -1,149 +0,0 @@
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