sevk 1.0.0 → 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.
@@ -1,55 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
3
+ require_relative 'base'
4
+ require_relative '../markup/renderer'
4
5
 
5
6
  module Sevk
6
7
  module Resources
7
8
  class Emails < Base
8
- # Send an email with optional attachments
9
- #
10
- # @param to [String, Array<String>] Recipient email(s)
11
- # @param subject [String] Email subject
12
- # @param html [String] HTML content
13
- # @param from [String] Sender email
14
- # @param from_name [String, nil] Sender name
15
- # @param reply_to [String, nil] Reply-to address
16
- # @param text [String, nil] Plain text content
17
- # @param headers [Hash, nil] Custom headers
18
- # @param tags [Array, nil] Tags
19
- # @param attachments [Array<Hash>, nil] Attachments (max 10, 10MB total)
20
- # Each attachment: { filename: String, content: String (base64), content_type: String }
21
- # @return [Hash] Response with :id or :ids
22
- def send(to:, subject:, html:, from:, from_name: nil, reply_to: nil, text: nil, headers: nil, tags: nil, attachments: nil)
9
+ # Sends one transactional email, rendering local markup when supplied.
10
+ def send(to:, subject:, from:, html: nil, text: nil, markup: nil, reply_to: nil, cc: nil, bcc: nil,
11
+ attachments: nil, tracking: nil, headers: nil, scheduled_at: nil, options: nil)
23
12
  body = {
24
13
  to: to,
25
14
  subject: subject,
26
- html: html,
27
15
  from: from
28
16
  }
29
- body[:fromName] = from_name if from_name
30
- body[:replyTo] = reply_to if reply_to
31
- body[:text] = text if text
32
- body[:headers] = headers if headers
33
- body[:tags] = tags if tags
34
- body[:attachments] = attachments if attachments
35
- client.post("emails", body)
17
+ body[:html] = html unless html.nil?
18
+ body[:html] = Sevk::Markup::Renderer.new.render(markup) if markup && !markup.empty?
19
+ body[:text] = text unless text.nil?
20
+ body[:reply_to] = reply_to unless reply_to.nil?
21
+ body[:cc] = cc unless cc.nil?
22
+ body[:bcc] = bcc unless bcc.nil?
23
+ body[:attachments] = normalize_attachments(attachments) unless attachments.nil?
24
+ body[:tracking] = tracking unless tracking.nil?
25
+ body[:headers] = headers unless headers.nil?
26
+ body[:scheduledAt] = scheduled_at unless scheduled_at.nil?
27
+
28
+ client.post('/emails', body, headers: idempotency_headers(options))
36
29
  end
37
30
 
38
- # Send multiple emails in bulk
39
- #
40
- # @param emails [Array<Hash>] Array of email parameter hashes (max 100)
41
- # @return [Hash] Response with :success, :failed, :ids, :errors
42
- def send_bulk(emails)
43
- client.post("emails/bulk", { emails: emails })
31
+ # Sends up to 100 transactional emails and returns per-request results.
32
+ def send_bulk(emails, options: nil)
33
+ unless emails.is_a?(Array)
34
+ return client.post('/emails/bulk', { emails: emails }, headers: idempotency_headers(options))
35
+ end
36
+
37
+ wire_emails = emails.map do |email|
38
+ next email unless email.is_a?(Hash)
39
+
40
+ payload = email.dup
41
+ symbol_markup = payload.delete(:markup)
42
+ string_markup = payload.delete('markup')
43
+ mk = symbol_markup.nil? ? string_markup : symbol_markup
44
+ unless mk.nil? || mk.empty?
45
+ payload.delete(:html)
46
+ payload.delete('html')
47
+ payload[:html] = Sevk::Markup::Renderer.new.render(mk)
48
+ end
49
+ attachments = payload[:attachments] || payload['attachments']
50
+ if attachments
51
+ payload.delete('attachments')
52
+ payload[:attachments] = normalize_attachments(attachments)
53
+ end
54
+ payload
55
+ end
56
+
57
+ client.post('/emails/bulk', { emails: wire_emails }, headers: idempotency_headers(options))
44
58
  end
45
59
 
46
- # Get an email by ID
47
- #
48
- # @param id [String] Email ID
49
- # @return [Hash] Email details
60
+ # Retrieves a sent email and its current delivery state.
50
61
  def get(id)
51
62
  client.get("/emails/#{id}")
52
63
  end
64
+
65
+ # moves an unattempted scheduled email to a new delivery time.
66
+ def update(id, scheduled_at:)
67
+ client.patch("/emails/#{id}", { scheduledAt: scheduled_at })
68
+ end
69
+
70
+ # cancels an unattempted scheduled email.
71
+ def cancel(id)
72
+ client.post("/emails/#{id}/cancel")
73
+ end
74
+
75
+ private
76
+
77
+ def normalize_attachments(attachments)
78
+ return attachments unless attachments.is_a?(Array)
79
+
80
+ attachments.map do |attachment|
81
+ next attachment unless attachment.is_a?(Hash)
82
+
83
+ normalized = attachment.dup
84
+ content_type = normalized.delete(:content_type)
85
+ content_type = normalized.delete('content_type') if content_type.nil?
86
+ unless content_type.nil? || normalized.key?(:contentType) || normalized.key?('contentType')
87
+ normalized[:contentType] = content_type
88
+ end
89
+
90
+ normalized
91
+ end
92
+ end
93
+
94
+ # Builds the optional idempotency header used to make retries safe.
95
+ def idempotency_headers(options)
96
+ return nil unless options
97
+
98
+ return nil unless options.key?(:idempotency_key) || options.key?('idempotency_key')
99
+
100
+ key = options.key?(:idempotency_key) ? options[:idempotency_key] : options['idempotency_key']
101
+
102
+ { 'Idempotency-Key' => key.to_s }
103
+ end
53
104
  end
54
105
  end
55
106
  end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Sevk
6
+ module Resources
7
+ class Inbound < Base
8
+ # Lists received email with optional pagination and filtering.
9
+ def list(page: nil, limit: nil, after: nil, before: nil, search: nil, status: nil, domain_id: nil,
10
+ broadcast_id: nil, is_spam: nil, is_virus: nil)
11
+ params = {
12
+ page: page,
13
+ limit: limit,
14
+ after: after,
15
+ before: before,
16
+ search: search,
17
+ status: status,
18
+ domainId: domain_id,
19
+ broadcastId: broadcast_id,
20
+ isSpam: is_spam,
21
+ isVirus: is_virus
22
+ }
23
+
24
+ client.get("/inbound#{build_query_string(params)}")
25
+ end
26
+
27
+ # Retrieves a received email with parsed content and attachment metadata.
28
+ def get(id)
29
+ client.get("/inbound/#{id}")
30
+ end
31
+
32
+ # Deletes a received email and its stored content.
33
+ def delete(id)
34
+ client.delete("/inbound/#{id}")
35
+ end
36
+
37
+ # Returns aggregate inbound processing statistics, optionally for one domain.
38
+ def stats(domain_id: nil)
39
+ params = { domainId: domain_id }
40
+
41
+ client.get("/inbound/stats#{build_query_string(params)}")
42
+ end
43
+
44
+ # Downloads the original content and metadata for an inbound attachment.
45
+ def download_attachment(email_id, attachment_id)
46
+ client.get("/inbound/#{email_id}/attachments/#{attachment_id}/download")
47
+ end
48
+
49
+ # Lists rules used to store or forward matching inbound email.
50
+ def list_routes(**params)
51
+ client.get("/inbound-settings/routes#{build_query_string(params)}")
52
+ end
53
+
54
+ def route_items(**params)
55
+ list_routes(**params).fetch('items')
56
+ end
57
+
58
+ # Retrieves an inbound routing rule by id.
59
+ def get_route(id)
60
+ client.get("/inbound-settings/routes/#{id}")
61
+ end
62
+
63
+ # Creates an inbound routing rule.
64
+ def create_route(**params)
65
+ client.post('/inbound-settings/routes', wire_keys(params))
66
+ end
67
+
68
+ # Updates an inbound routing rule while preserving omitted fields.
69
+ def update_route(id, **params)
70
+ client.put("/inbound-settings/routes/#{id}", wire_keys(params))
71
+ end
72
+
73
+ # Deletes an inbound routing rule without deleting stored email.
74
+ def delete_route(id)
75
+ client.delete("/inbound-settings/routes/#{id}")
76
+ end
77
+
78
+ # Lists blocked addresses with optional offset or cursor pagination.
79
+ def list_blocklist(**params)
80
+ client.get("/inbound-settings/blocklist#{build_query_string(params)}")
81
+ end
82
+
83
+ def blocklist_items(**params)
84
+ list_blocklist(**params).fetch('items')
85
+ end
86
+
87
+ # Adds an address to the inbound blocklist.
88
+ def add_blocklist_entry(**params)
89
+ client.post('/inbound-settings/blocklist', wire_keys(params))
90
+ end
91
+
92
+ # Removes an address from the inbound blocklist.
93
+ def remove_blocklist_entry(id)
94
+ client.delete("/inbound-settings/blocklist/#{id}")
95
+ end
96
+
97
+ # Returns inbound quota settings and current usage counters.
98
+ def get_quota
99
+ client.get('/inbound-settings/quota')
100
+ end
101
+
102
+ # Updates the inbound monthly limit while preserving omitted fields.
103
+ def update_quota(**params)
104
+ client.put('/inbound-settings/quota', wire_keys(params))
105
+ end
106
+
107
+ # Clears a quota pause without resetting current-period usage.
108
+ def reset_quota
109
+ client.post('/inbound-settings/quota/reset', {})
110
+ end
111
+
112
+ # Returns effective inbound security settings for a domain.
113
+ def get_security_config(domain_id)
114
+ client.get("/inbound-settings/security/#{domain_id}")
115
+ end
116
+
117
+ # Updates inbound security settings for a domain.
118
+ def update_security_config(domain_id, **params)
119
+ client.put("/inbound-settings/security/#{domain_id}", wire_keys(params))
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Sevk
6
+ module Resources
7
+ # Outbound resource. Read and adjust the project's monthly outbound
8
+ # sending cap, and pause / resume transactional sending.
9
+ #
10
+ # Pause affects +POST /emails+, +POST /emails/bulk+, and SMTP relay.
11
+ # Broadcasts run on a separate balance check and are NOT affected.
12
+ #
13
+ # Access via +sevk.outbound+. Requires an API key with +outbound:read+
14
+ # for +get_quota+, or +outbound:write+ for the others.
15
+ class Outbound < Base
16
+ # Get outbound quota settings and current usage counters.
17
+ # Requires +outbound:read+.
18
+ def get_quota
19
+ client.get('/outbound-settings/quota')
20
+ end
21
+
22
+ # Set the user-side monthly outbound cap. Pass +monthlyEmailLimit:+
23
+ # as a positive Integer to cap, or +nil+ to clear the user-set cap
24
+ # (admin ceiling still applies). Raising the cap above current
25
+ # usage clears a +monthly_cap_exhausted+ pause; does not clear
26
+ # +manual_suspend+.
27
+ # Requires +outbound:write+.
28
+ def update_quota(**params)
29
+ client.put('/outbound-settings/quota', wire_keys(params))
30
+ end
31
+
32
+ # Preserve the monthly counter and clear any pause (including
33
+ # +manual_suspend+ and +balance_exhausted+). The user-set cap is
34
+ # preserved.
35
+ # Requires +outbound:write+.
36
+ def reset_quota
37
+ client.post('/outbound-settings/quota/reset', {})
38
+ end
39
+
40
+ # Reset custom-limit usage while preserving the cap and monthly usage.
41
+ # Requires +outbound:write+.
42
+ def reset_limit
43
+ client.post('/outbound-settings/quota/reset-limit', {})
44
+ end
45
+
46
+ # Pause outbound transactional sending with
47
+ # +pausedReason: manual_suspend+. Manual pauses do NOT auto-clear;
48
+ # call +reset_quota+ to resume. Does not affect broadcasts.
49
+ # Requires +outbound:write+.
50
+ def pause_quota
51
+ client.post('/outbound-settings/quota/pause', {})
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,42 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
3
+ require_relative 'base'
4
4
 
5
5
  module Sevk
6
6
  module Resources
7
7
  class Segments < Base
8
- def list(audience_id, page: nil, limit: nil)
9
- params = { page: page, limit: limit }
10
- client.get("/audiences/#{audience_id}/segments#{build_query_string(params)}")
11
- end
8
+ # Lists segments belonging to an audience with optional filtering.
9
+ def list(audience_id, page: nil, limit: nil, after: nil, before: nil, search: nil)
10
+ params = { page: page, limit: limit, after: after, before: before, search: search }
12
11
 
13
- def get(audience_id, segment_id)
14
- client.get("/audiences/#{audience_id}/segments/#{segment_id}")
12
+ client.get("/audiences/#{audience_id}/segments#{build_query_string(params)}")
15
13
  end
16
14
 
17
- def create(audience_id, name:, rules:, operator: "AND")
15
+ # Creates a saved contact segment under an audience.
16
+ def create(audience_id, name:, rules:, operator: 'AND', limit_to_topics: nil, groups: nil)
18
17
  body = { name: name, rules: rules, operator: operator }
18
+ body[:limitToTopics] = limit_to_topics unless limit_to_topics.nil?
19
+ body[:groups] = groups unless groups.nil?
20
+
19
21
  client.post("/audiences/#{audience_id}/segments", body)
20
22
  end
21
23
 
22
- def update(audience_id, segment_id, name: nil, rules: nil, operator: nil)
23
- body = {}
24
- body[:name] = name if name
25
- body[:rules] = rules if rules
26
- body[:operator] = operator if operator
27
- client.put("/audiences/#{audience_id}/segments/#{segment_id}", body)
24
+ # Evaluates segment rules and returns matches without saving a segment.
25
+ def preview(audience_id, data)
26
+ client.post("/audiences/#{audience_id}/segments/preview", wire_keys(data))
28
27
  end
29
28
 
30
- def delete(audience_id, segment_id)
31
- client.delete("/audiences/#{audience_id}/segments/#{segment_id}")
29
+ # Retrieves a saved segment by id.
30
+ def get(segment_id)
31
+ client.get("/segments/#{segment_id}")
32
32
  end
33
33
 
34
- def calculate(audience_id, segment_id)
35
- client.get("/audiences/#{audience_id}/segments/#{segment_id}/calculate")
34
+ # Updates a segment while preserving omitted fields.
35
+ def update(segment_id, name: nil, rules: nil, operator: nil, limit_to_topics: nil, groups: nil)
36
+ body = {}
37
+ body[:name] = name unless name.nil?
38
+ body[:rules] = rules unless rules.nil?
39
+ body[:operator] = operator unless operator.nil?
40
+ body[:limitToTopics] = limit_to_topics unless limit_to_topics.nil?
41
+ body[:groups] = groups unless groups.nil?
42
+
43
+ client.put("/segments/#{segment_id}", body)
36
44
  end
37
45
 
38
- def preview(audience_id, data)
39
- client.post("/audiences/#{audience_id}/segments/preview", data)
46
+ # Deletes a segment without deleting any contacts.
47
+ def delete(segment_id)
48
+ client.delete("/segments/#{segment_id}")
49
+ end
50
+
51
+ # Recalculates and returns the current matching contact count.
52
+ def calculate(segment_id)
53
+ client.get("/segments/#{segment_id}/calculate")
40
54
  end
41
55
  end
42
56
  end
@@ -1,22 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
3
+ require_relative 'base'
4
4
 
5
5
  module Sevk
6
6
  module Resources
7
7
  class Subscriptions < Base
8
- def subscribe(email:, audience_id:, topic_ids: nil, metadata: nil)
9
- body = { email: email, audienceId: audience_id }
10
- body[:topicIds] = topic_ids if topic_ids
11
- body[:metadata] = metadata if metadata
12
- client.post("/subscriptions/subscribe", body)
8
+ # Subscribes a contact and optionally adds it to an audience.
9
+ def subscribe(email:, audience_id: nil, data: nil)
10
+ body = { email: email }
11
+ body[:audienceId] = audience_id unless audience_id.nil?
12
+ body[:data] = data unless data.nil?
13
+
14
+ client.post('/subscriptions/subscribe', body)
13
15
  end
14
16
 
15
- def unsubscribe(email:, audience_id: nil, topic_ids: nil)
16
- body = { email: email }
17
- body[:audienceId] = audience_id if audience_id
18
- body[:topicIds] = topic_ids if topic_ids
19
- client.post("/subscriptions/unsubscribe", body)
17
+ # Global opt-out. Provide at least one of email or contact_id (if both are
18
+ # given, contact_id wins). broadcast_id only attributes the opt-out to a
19
+ # broadcast (activity + counters); it does NOT scope the unsubscribe.
20
+ def unsubscribe(email: nil, contact_id: nil, broadcast_id: nil)
21
+ body = {}
22
+ body[:email] = email unless email.nil?
23
+ body[:contactId] = contact_id unless contact_id.nil?
24
+ body[:broadcastId] = broadcast_id unless broadcast_id.nil?
25
+
26
+ client.post('/subscriptions/unsubscribe', body)
20
27
  end
21
28
  end
22
29
  end
@@ -1,34 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
3
+ require_relative 'base'
4
4
 
5
5
  module Sevk
6
6
  module Resources
7
7
  class Templates < Base
8
- def list(page: nil, limit: nil, search: nil)
9
- params = { page: page, limit: limit, search: search }
8
+ # Lists reusable email templates with optional pagination and search.
9
+ def list(page: nil, limit: nil, after: nil, before: nil, search: nil)
10
+ params = { page: page, limit: limit, after: after, before: before, search: search }
11
+
10
12
  client.get("/templates#{build_query_string(params)}")
11
13
  end
12
14
 
15
+ # Retrieves a template by id.
13
16
  def get(id)
14
17
  client.get("/templates/#{id}")
15
18
  end
16
19
 
17
- def create(title:, content:)
18
- client.post("/templates", { title: title, content: content })
20
+ # Creates a reusable email template from markup and optional styles.
21
+ def create(title:, content:, style: nil)
22
+ body = { title: title, content: content }
23
+ body[:style] = style unless style.nil?
24
+
25
+ client.post('/templates', body)
19
26
  end
20
27
 
21
- def update(id, title: nil, content: nil)
28
+ # Updates a template while preserving omitted fields.
29
+ def update(id, title: nil, content: nil, style: nil)
22
30
  body = {}
23
- body[:title] = title if title
24
- body[:content] = content if content
31
+ body[:title] = title unless title.nil?
32
+ body[:content] = content unless content.nil?
33
+ body[:style] = style unless style.nil?
34
+
25
35
  client.put("/templates/#{id}", body)
26
36
  end
27
37
 
38
+ # Deletes a template without affecting email already sent from it.
28
39
  def delete(id)
29
40
  client.delete("/templates/#{id}")
30
41
  end
31
42
 
43
+ # Creates an independent copy of a template for further editing.
32
44
  def duplicate(id)
33
45
  client.post("/templates/#{id}/duplicate", {})
34
46
  end
@@ -1,46 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
3
+ require_relative 'base'
4
4
 
5
5
  module Sevk
6
6
  module Resources
7
7
  class Topics < Base
8
- def list(audience_id, page: nil, limit: nil)
9
- params = { page: page, limit: limit }
10
- client.get("/audiences/#{audience_id}/topics#{build_query_string(params)}")
11
- end
8
+ # Lists topics belonging to an audience with optional filtering.
9
+ def list(audience_id, page: nil, limit: nil, after: nil, before: nil, search: nil)
10
+ params = { page: page, limit: limit, after: after, before: before, search: search }
12
11
 
13
- def get(audience_id, topic_id)
14
- client.get("/audiences/#{audience_id}/topics/#{topic_id}")
12
+ client.get("/audiences/#{audience_id}/topics#{build_query_string(params)}")
15
13
  end
16
14
 
15
+ # Creates a subscription topic under an audience.
17
16
  def create(audience_id, name:, description: nil)
18
17
  body = { name: name }
19
- body[:description] = description if description
18
+ body[:description] = description unless description.nil?
19
+
20
20
  client.post("/audiences/#{audience_id}/topics", body)
21
21
  end
22
22
 
23
- def update(audience_id, topic_id, name: nil, description: nil)
23
+ # Retrieves a topic by id.
24
+ def get(topic_id)
25
+ client.get("/topics/#{topic_id}")
26
+ end
27
+
28
+ # Updates a topic while preserving omitted fields.
29
+ def update(topic_id, name: nil, description: nil)
24
30
  body = {}
25
- body[:name] = name if name
26
- body[:description] = description if description
27
- client.put("/audiences/#{audience_id}/topics/#{topic_id}", body)
31
+ body[:name] = name unless name.nil?
32
+ body[:description] = description unless description.nil?
33
+
34
+ client.put("/topics/#{topic_id}", body)
28
35
  end
29
36
 
30
- def delete(audience_id, topic_id)
31
- client.delete("/audiences/#{audience_id}/topics/#{topic_id}")
37
+ # Deletes a topic without deleting its contacts or parent audience.
38
+ def delete(topic_id)
39
+ client.delete("/topics/#{topic_id}")
32
40
  end
33
41
 
34
- def add_contacts(audience_id, topic_id, contact_ids:)
35
- client.post("/audiences/#{audience_id}/topics/#{topic_id}/contacts", { contactIds: contact_ids })
42
+ # Adds contacts to a topic, ignoring memberships that already exist.
43
+ def add_contacts(topic_id, contact_ids:)
44
+ client.post("/topics/#{topic_id}/contacts", { contactIds: contact_ids })
36
45
  end
37
46
 
38
- def remove_contact(audience_id, topic_id, contact_id)
39
- client.delete("/audiences/#{audience_id}/topics/#{topic_id}/contacts/#{contact_id}")
47
+ # Removes a contact from the topic without deleting the contact.
48
+ def remove_contact(topic_id, contact_id)
49
+ client.delete("/topics/#{topic_id}/contacts/#{contact_id}")
40
50
  end
41
51
 
42
- def list_contacts(audience_id, topic_id, params = {})
43
- client.get("/audiences/#{audience_id}/topics/#{topic_id}/contacts#{build_query_string(params)}")
52
+ # Lists contacts by page, cursor, search text, or subscription state.
53
+ def list_contacts(topic_id, params = {})
54
+ client.get("/topics/#{topic_id}/contacts#{build_query_string(params)}")
44
55
  end
45
56
  end
46
57
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Sevk
6
+ module Resources
7
+ # Reads project limits, resource counts, and current email usage.
8
+ class Usage < Base
9
+ # Returns current limits, resource counts, and inbound and outbound usage.
10
+ def get
11
+ client.get('/limits')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,37 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
3
+ require_relative 'base'
4
4
 
5
5
  module Sevk
6
6
  module Resources
7
7
  class Webhooks < Base
8
- def list(page: nil, limit: nil)
9
- params = { page: page, limit: limit }
8
+ def rotate(id)
9
+ client.post("/webhooks/#{id}/rotate", {})
10
+ end
11
+
12
+ # Lists configured webhooks and the available event catalog.
13
+ def list(page: nil, limit: nil, after: nil, before: nil)
14
+ params = { page: page, limit: limit, after: after, before: before }
15
+
10
16
  client.get("/webhooks#{build_query_string(params)}")
11
17
  end
12
18
 
19
+ # Retrieves a webhook and its recent delivery state. Its secret is the
20
+ # masked placeholder (••••••••), not a usable verification secret.
13
21
  def get(id)
14
22
  client.get("/webhooks/#{id}")
15
23
  end
16
24
 
25
+ # Creates a webhook subscription. The returned Hash always contains a non-nil
26
+ # :secret/"secret" value prefixed with whsec_; this usable secret appears only once.
17
27
  def create(params)
18
- client.post("/webhooks", params)
28
+ client.post('/webhooks', wire_keys(params))
19
29
  end
20
30
 
31
+ # Updates a webhook while preserving omitted fields.
21
32
  def update(id, params)
22
- client.put("/webhooks/#{id}", params)
33
+ client.put("/webhooks/#{id}", wire_keys(params))
23
34
  end
24
35
 
36
+ # Deletes a webhook and stops future deliveries to its URL.
25
37
  def delete(id)
26
38
  client.delete("/webhooks/#{id}")
27
39
  end
28
40
 
41
+ # Sends a test event to a webhook and returns the delivery result.
29
42
  def test(id)
30
43
  client.post("/webhooks/#{id}/test", {})
31
44
  end
32
45
 
33
- def list_events
34
- client.get("/webhooks/events")
46
+ # Lists event names that can be assigned to a webhook.
47
+ def events
48
+ client.get('/webhooks/events')
35
49
  end
36
50
  end
37
51
  end
data/lib/sevk/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sevk
4
- VERSION = "1.0.0"
4
+ VERSION = '1.1.0'
5
5
  end