supersendtx 0.8.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eca4d44a3a130a37fb7055f295cd6f37ed780c145189a1ce40d4318f7fd5d309
4
+ data.tar.gz: 5a4728dd67281d5323c284c32e21ef03943f602987428c2c76452fee6c80e904
5
+ SHA512:
6
+ metadata.gz: 303ae619a3dc9bc631c2938acf710bf25d63639c2612a96fe1229e6e3d3e5d40ef0a9a24d0a4c8ed837963f41814e6012e1f7eef8c160c0c1494adce1f6d4e7e
7
+ data.tar.gz: dda61e8e034f98c653c0ca8adb4f75de2226065a6f9126d416487e3f5d20c48f7d94bfd10bea34229dff32f054c5402673d0326cbb8d9bf2e2378a1397a5a51c
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /spec/reports/
2
+ /.bundle/
3
+ /Gemfile.lock
4
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development, :test do
8
+ gem "minitest", "~> 5.25"
9
+ end
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # SuperSend TX Ruby SDK
2
+
3
+ Official Ruby client for the [SuperSend TX](https://supersendtx.com) transactional email API.
4
+
5
+ ```bash
6
+ gem install supersendtx
7
+ ```
8
+
9
+ ```ruby
10
+ require "supersendtx"
11
+
12
+ tx = SuperSendTX::Client.new("stx_your_key_here")
13
+
14
+ result = tx.emails.send(
15
+ from: "you@yourdomain.com",
16
+ to: "user@example.com",
17
+ subject: "Hello",
18
+ html: "<p>It works.</p>"
19
+ )
20
+
21
+ puts result["id"], result["status"]
22
+ ```
23
+
24
+ Docs: https://docs.supersendtx.com/sdks/ruby
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSendTX
4
+ class Client
5
+ attr_reader :emails, :domains, :webhooks, :templates, :suppressions
6
+
7
+ def initialize(api_key, base_url: HttpClient::DEFAULT_API_BASE_URL, transport: nil)
8
+ http = HttpClient.new(api_key, base_url: base_url, transport: transport)
9
+ @emails = EmailsResource.new(http)
10
+ @domains = DomainsResource.new(http)
11
+ @webhooks = WebhooksResource.new(http)
12
+ @templates = TemplatesResource.new(http)
13
+ @suppressions = SuppressionsResource.new(http)
14
+ @http = http
15
+ end
16
+
17
+ def request(method, path, body: nil, headers: {})
18
+ @http.request(method, path, body: body, headers: headers)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "net/http"
5
+ require "uri"
6
+
7
+ module SuperSendTX
8
+ class Error < StandardError
9
+ attr_reader :status, :details, :error_code, :upgrade_url
10
+
11
+ def initialize(message, status:, details: nil, error_code: nil, upgrade_url: nil)
12
+ super(message)
13
+ @status = status
14
+ @details = details
15
+ @error_code = error_code
16
+ @upgrade_url = upgrade_url
17
+ end
18
+
19
+ def self.from_response(status, body)
20
+ err = body.is_a?(Hash) ? body["error"] : nil
21
+
22
+ case err
23
+ when String
24
+ new(err, status: status)
25
+ when Hash
26
+ new(
27
+ err["message"] || "Request failed with status #{status}",
28
+ status: status,
29
+ details: err["details"],
30
+ error_code: err["code"]&.to_s,
31
+ upgrade_url: err["upgrade_url"]&.to_s
32
+ )
33
+ else
34
+ new("Request failed with status #{status}", status: status)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "net/http"
5
+ require "uri"
6
+
7
+ module SuperSendTX
8
+ class HttpClient
9
+ DEFAULT_API_BASE_URL = "https://api.supersendtx.com"
10
+
11
+ def initialize(api_key, base_url: DEFAULT_API_BASE_URL, transport: nil)
12
+ raise ArgumentError, "SuperSend TX API key must start with stx_" unless api_key.start_with?("stx_")
13
+
14
+ @api_key = api_key
15
+ @base_url = base_url.chomp("/")
16
+ @transport = transport
17
+ end
18
+
19
+ def request(method, path, body: nil, headers: {})
20
+ request_headers = {
21
+ "Authorization" => "Bearer #{@api_key}",
22
+ "Content-Type" => "application/json"
23
+ }.merge(headers)
24
+
25
+ return @transport.call(method, path, body, request_headers) if @transport
26
+
27
+ uri = URI.parse("#{@base_url}#{path}")
28
+ http = Net::HTTP.new(uri.host, uri.port)
29
+ http.use_ssl = uri.scheme == "https"
30
+
31
+ request_class = Net::HTTP.const_get(method.capitalize)
32
+ request = request_class.new(uri.request_uri)
33
+ request_headers.each { |key, value| request[key] = value }
34
+ request.body = JSON.generate(body) if body
35
+
36
+ response = http.request(request)
37
+ parsed = response.body.to_s.empty? ? {} : JSON.parse(response.body)
38
+
39
+ raise Error.from_response(response.code.to_i, parsed) if response.code.to_i >= 400
40
+
41
+ parsed
42
+ end
43
+
44
+ def query(params)
45
+ filtered = params.compact
46
+ return "" if filtered.empty?
47
+
48
+ "?#{URI.encode_www_form(filtered)}"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSendTX
4
+ class EmailsResource
5
+ def initialize(http)
6
+ @http = http
7
+ end
8
+
9
+ def list(limit: nil, cursor: nil)
10
+ @http.request("GET", "/emails#{@http.query({ "limit" => limit, "cursor" => cursor })}")
11
+ end
12
+
13
+ def get(email_id)
14
+ @http.request("GET", "/emails/#{URI.encode_www_form_component(email_id)}")
15
+ end
16
+
17
+ def send(from: nil, to: nil, **params)
18
+ params["from"] = from if from
19
+ params["to"] = to if to
20
+ body = serialize_send_params(params)
21
+ headers = {}
22
+ idempotency_key = params["idempotency_key"] || params["idempotencyKey"]
23
+ headers["Idempotency-Key"] = idempotency_key.to_s if idempotency_key
24
+
25
+ @http.request("POST", "/emails", body: body, headers: headers)
26
+ end
27
+
28
+ def batch(emails)
29
+ serialized = emails.map { |email| serialize_send_params(email) }
30
+ @http.request("POST", "/emails/batch", body: { "emails" => serialized })
31
+ end
32
+
33
+ def cancel(email_id)
34
+ @http.request("PATCH", "/emails/#{URI.encode_www_form_component(email_id)}", body: { "cancel" => true })
35
+ end
36
+
37
+ def resend(email_id)
38
+ @http.request("POST", "/emails/#{URI.encode_www_form_component(email_id)}/resend")
39
+ end
40
+
41
+ def test_webhook(**params)
42
+ @http.request("POST", "/emails/test", body: params)
43
+ end
44
+
45
+ def insights(window: "30d")
46
+ @http.request("GET", "/deliverability#{@http.query({ "window" => window })}")
47
+ end
48
+
49
+ private
50
+
51
+ def serialize_send_params(params)
52
+ body = {
53
+ "from" => params["from"] || params[:from],
54
+ "to" => params["to"] || params[:to]
55
+ }
56
+
57
+ %w[subject html text reply_to replyTo cc bcc tags headers tag template].each do |key|
58
+ value = params[key] || params[key.to_sym]
59
+ next if value.nil?
60
+
61
+ mapped = key == "replyTo" ? "reply_to" : key
62
+ body[mapped] = value
63
+ end
64
+
65
+ body["html"] = params["htmlBody"] || params[:htmlBody] if params["htmlBody"] || params[:htmlBody]
66
+ body["text"] = params["textBody"] || params[:textBody] if params["textBody"] || params[:textBody]
67
+ body["scheduled_at"] = params["scheduled_at"] || params[:scheduled_at] if params["scheduled_at"] || params[:scheduled_at]
68
+ body["scheduled_at"] ||= params["scheduledAt"] || params[:scheduledAt]
69
+ body["unsubscribe"] = params["unsubscribe"] || params[:unsubscribe] if params.key?("unsubscribe") || params.key?(:unsubscribe)
70
+
71
+ body
72
+ end
73
+ end
74
+
75
+ class DomainsResource
76
+ def initialize(http)
77
+ @http = http
78
+ end
79
+
80
+ def list(limit: nil, cursor: nil, inbound_enabled: nil)
81
+ @http.request(
82
+ "GET",
83
+ "/domains#{@http.query({ "limit" => limit, "cursor" => cursor, "inbound_enabled" => inbound_enabled })}"
84
+ )
85
+ end
86
+
87
+ def get(id_or_name)
88
+ @http.request("GET", "/domains/#{URI.encode_www_form_component(id_or_name)}")
89
+ end
90
+
91
+ def create(name, inbound_enabled: nil)
92
+ body = { "name" => name }
93
+ body["inbound_enabled"] = inbound_enabled unless inbound_enabled.nil?
94
+ @http.request("POST", "/domains", body: body)
95
+ end
96
+
97
+ def verify(id_or_name)
98
+ @http.request("POST", "/domains/#{URI.encode_www_form_component(id_or_name)}", body: { "action" => "verify" })
99
+ end
100
+
101
+ def apply(id_or_name, provider: "cloudflare", credentials: nil)
102
+ body = { "action" => "apply", "provider" => provider }
103
+ body["credentials"] = credentials if credentials
104
+ @http.request("POST", "/domains/#{URI.encode_www_form_component(id_or_name)}", body: body)
105
+ end
106
+
107
+ def update(id_or_name, **params)
108
+ @http.request("PATCH", "/domains/#{URI.encode_www_form_component(id_or_name)}", body: params)
109
+ end
110
+
111
+ def delete(id_or_name)
112
+ @http.request("DELETE", "/domains/#{URI.encode_www_form_component(id_or_name)}")
113
+ end
114
+ end
115
+
116
+ class WebhooksResource
117
+ def initialize(http)
118
+ @http = http
119
+ end
120
+
121
+ def list(limit: nil, cursor: nil)
122
+ @http.request("GET", "/webhooks#{@http.query({ "limit" => limit, "cursor" => cursor })}")
123
+ end
124
+
125
+ def get(webhook_id)
126
+ @http.request("GET", "/webhooks/#{URI.encode_www_form_component(webhook_id)}")
127
+ end
128
+
129
+ def create(**params)
130
+ @http.request("POST", "/webhooks", body: params)
131
+ end
132
+
133
+ def update(webhook_id, **params)
134
+ @http.request("PATCH", "/webhooks/#{URI.encode_www_form_component(webhook_id)}", body: params)
135
+ end
136
+
137
+ def delete(webhook_id)
138
+ @http.request("DELETE", "/webhooks/#{URI.encode_www_form_component(webhook_id)}")
139
+ end
140
+ end
141
+
142
+ class TemplatesResource
143
+ def initialize(http)
144
+ @http = http
145
+ end
146
+
147
+ def list(limit: nil, cursor: nil, status: nil)
148
+ @http.request(
149
+ "GET",
150
+ "/templates#{@http.query({ "limit" => limit, "cursor" => cursor, "status" => status })}"
151
+ )
152
+ end
153
+
154
+ def get(id_or_alias)
155
+ @http.request("GET", "/templates/#{URI.encode_www_form_component(id_or_alias)}")
156
+ end
157
+
158
+ def create(**params)
159
+ @http.request("POST", "/templates", body: params)
160
+ end
161
+
162
+ def update(id_or_alias, **params)
163
+ @http.request("PATCH", "/templates/#{URI.encode_www_form_component(id_or_alias)}", body: params)
164
+ end
165
+
166
+ def delete(id_or_alias)
167
+ @http.request("DELETE", "/templates/#{URI.encode_www_form_component(id_or_alias)}")
168
+ end
169
+
170
+ def publish(id_or_alias)
171
+ @http.request("POST", "/templates/#{URI.encode_www_form_component(id_or_alias)}", body: { "action" => "publish" })
172
+ end
173
+ end
174
+
175
+ class SuppressionsResource
176
+ def initialize(http)
177
+ @http = http
178
+ end
179
+
180
+ def list(limit: nil, cursor: nil, email: nil)
181
+ @http.request(
182
+ "GET",
183
+ "/suppressions#{@http.query({ "limit" => limit, "cursor" => cursor, "email" => email })}"
184
+ )
185
+ end
186
+
187
+ def create(**params)
188
+ @http.request("POST", "/suppressions", body: params)
189
+ end
190
+
191
+ def remove(id_or_email)
192
+ if id_or_email.include?("@")
193
+ return @http.request("DELETE", "/suppressions#{@http.query({ "email" => id_or_email })}")
194
+ end
195
+
196
+ @http.request("DELETE", "/suppressions/#{URI.encode_www_form_component(id_or_email)}")
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSendTX
4
+ VERSION = "0.8.2"
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "supersendtx/version"
4
+ require_relative "supersendtx/error"
5
+ require_relative "supersendtx/http"
6
+ require_relative "supersendtx/resources"
7
+ require_relative "supersendtx/client"
8
+
9
+ module SuperSendTX
10
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/supersendtx/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "supersendtx"
7
+ spec.version = SuperSendTX::VERSION
8
+ spec.authors = ["SuperSend TX"]
9
+ spec.email = ["support@supersendtx.com"]
10
+
11
+ spec.summary = "SuperSend TX transactional email API client for Ruby"
12
+ spec.description = "Official Ruby client for the SuperSend TX REST API."
13
+ spec.homepage = "https://supersendtx.com"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.1.0"
16
+
17
+ spec.metadata = {
18
+ "homepage_uri" => spec.homepage,
19
+ "source_code_uri" => "https://github.com/Super-Send/supersendtx-sdks/tree/main/ruby",
20
+ "documentation_uri" => "https://docs.supersendtx.com/sdks/ruby",
21
+ "rubygems_mfa_required" => "true"
22
+ }
23
+
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.start_with?("spec/") }
26
+ rescue StandardError
27
+ Dir.glob("{lib}/**/*", base: __dir__) + ["README.md", "supersendtx.gemspec"]
28
+ end
29
+
30
+ spec.require_paths = ["lib"]
31
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: supersendtx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.2
5
+ platform: ruby
6
+ authors:
7
+ - SuperSend TX
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-07-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Official Ruby client for the SuperSend TX REST API.
14
+ email:
15
+ - support@supersendtx.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - Gemfile
22
+ - README.md
23
+ - lib/supersendtx.rb
24
+ - lib/supersendtx/client.rb
25
+ - lib/supersendtx/error.rb
26
+ - lib/supersendtx/http.rb
27
+ - lib/supersendtx/resources.rb
28
+ - lib/supersendtx/version.rb
29
+ - supersendtx.gemspec
30
+ homepage: https://supersendtx.com
31
+ licenses:
32
+ - MIT
33
+ metadata:
34
+ homepage_uri: https://supersendtx.com
35
+ source_code_uri: https://github.com/Super-Send/supersendtx-sdks/tree/main/ruby
36
+ documentation_uri: https://docs.supersendtx.com/sdks/ruby
37
+ rubygems_mfa_required: 'true'
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.1.0
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubygems_version: 3.5.22
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: SuperSend TX transactional email API client for Ruby
57
+ test_files: []