mailx-sdk 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c58bfcde392e7ef326898f019436fd782aefae0b92236adf2a9d479bbe77fd3a
4
+ data.tar.gz: 30f3b32add0be50f4b1f4f8f2d401f6a049d612f728dbbcea923f269f13afcea
5
+ SHA512:
6
+ metadata.gz: 6c3ff61e82a0fd646f61c2c19d1be51efdb5a04f5d168c466166c541dbf479826a150a03764af75c42977b9cb4ababe70d6c1a48f656edf2c276ce9bbd5a9f13
7
+ data.tar.gz: 6c8eaa67ccc907907669a260c970b213def7c1ff2623d4f1f363f97f7ed2029ef32287da773b5d9cb8feeddd22ac623a2e698938ef893beac118dc9f91f91c4c
@@ -0,0 +1,153 @@
1
+ require "net/http"
2
+ require "json"
3
+ require "uri"
4
+
5
+ require_relative "error"
6
+
7
+ module MailX
8
+ class Client
9
+ DEFAULT_BASE_URL = "https://api.mailx.dev"
10
+
11
+ def initialize(api_key, base_url: DEFAULT_BASE_URL, max_retries: 3, http_client: nil)
12
+ @api_key = api_key
13
+ @base_url = base_url.chomp("/")
14
+ @max_retries = max_retries
15
+ @http_client = http_client
16
+ end
17
+
18
+ def send_email(body)
19
+ request(:post, "/v1/emails", body)
20
+ end
21
+
22
+ def send_batch(body)
23
+ request(:post, "/v1/emails/batch", body)
24
+ end
25
+
26
+ def get_email(id)
27
+ request(:get, "/v1/emails/#{id}")
28
+ end
29
+
30
+ def list_emails(query = "")
31
+ request(:get, "/v1/emails#{query}")
32
+ end
33
+
34
+ def list_events(query = "")
35
+ request(:get, "/v1/events#{query}")
36
+ end
37
+
38
+ def create_domain(body); request(:post, "/v1/domains", body); end
39
+ def get_domain(id); request(:get, "/v1/domains/#{id}"); end
40
+ def list_domains; request(:get, "/v1/domains"); end
41
+ def delete_domain(id); request(:delete, "/v1/domains/#{id}"); end
42
+ def verify_dkim(domain_id); request(:post, "/v1/domains/#{domain_id}/dkim/verify"); end
43
+ def get_spf(domain_id); request(:get, "/v1/domains/#{domain_id}/spf"); end
44
+ def get_dmarc(domain_id); request(:get, "/v1/domains/#{domain_id}/dmarc"); end
45
+ def get_bimi(domain_id); request(:get, "/v1/domains/#{domain_id}/bimi"); end
46
+ def verify_bimi(domain_id); request(:post, "/v1/domains/#{domain_id}/bimi/verify"); end
47
+
48
+ def create_template(body); request(:post, "/v1/templates", body); end
49
+ def get_template(id); request(:get, "/v1/templates/#{id}"); end
50
+ def list_templates; request(:get, "/v1/templates"); end
51
+ def update_template(id, body); request(:patch, "/v1/templates/#{id}", body); end
52
+ def delete_template(id); request(:delete, "/v1/templates/#{id}"); end
53
+
54
+ def create_contact(body); request(:post, "/v1/contacts", body); end
55
+ def get_contact(id); request(:get, "/v1/contacts/#{id}"); end
56
+ def list_contacts; request(:get, "/v1/contacts"); end
57
+ def update_contact(id, body); request(:patch, "/v1/contacts/#{id}", body); end
58
+ def delete_contact(id); request(:delete, "/v1/contacts/#{id}"); end
59
+
60
+ def create_audience(body); request(:post, "/v1/audiences", body); end
61
+ def get_audience(id); request(:get, "/v1/audiences/#{id}"); end
62
+ def list_audiences; request(:get, "/v1/audiences"); end
63
+ def delete_audience(id); request(:delete, "/v1/audiences/#{id}"); end
64
+
65
+ def create_broadcast(body); request(:post, "/v1/broadcasts", body); end
66
+ def get_broadcast(id); request(:get, "/v1/broadcasts/#{id}"); end
67
+ def list_broadcasts; request(:get, "/v1/broadcasts"); end
68
+
69
+ def get_analytics(query = ""); request(:get, "/v1/analytics#{query}"); end
70
+
71
+ def list_suppressions; request(:get, "/v1/suppressions"); end
72
+ def create_suppression(body); request(:post, "/v1/suppressions", body); end
73
+ def delete_suppression(id); request(:delete, "/v1/suppressions/#{id}"); end
74
+
75
+ def create_webhook(body); request(:post, "/v1/webhooks", body); end
76
+ def get_webhook(id); request(:get, "/v1/webhooks/#{id}"); end
77
+ def list_webhooks; request(:get, "/v1/webhooks"); end
78
+ def update_webhook(id, body); request(:patch, "/v1/webhooks/#{id}", body); end
79
+ def delete_webhook(id); request(:delete, "/v1/webhooks/#{id}"); end
80
+
81
+ private
82
+
83
+ def request(method, path, body = nil)
84
+ payload = body ? JSON.generate(body) : nil
85
+ attempt = 0
86
+ last_retry_after = nil
87
+
88
+ loop do
89
+ sleep(backoff_seconds(attempt, last_retry_after)) if attempt > 0
90
+
91
+ uri = URI.join(@base_url, path)
92
+ req = build_request(method, uri, payload)
93
+
94
+ status, headers, resp_body = perform(uri, req)
95
+
96
+ if status >= 200 && status < 300
97
+ return resp_body.nil? || resp_body.empty? ? {} : JSON.parse(resp_body)
98
+ end
99
+
100
+ # Every MailX error response is {"error": {type, code, message,
101
+ # request_id}} - see internal/api/errors.go's errorBody.
102
+ body = resp_body && !resp_body.empty? ? (JSON.parse(resp_body) rescue {}) : {}
103
+ parsed = body["error"] || {}
104
+ retry_after = headers["retry-after"] ? headers["retry-after"].to_i : nil
105
+ error = APIError.new(
106
+ status: status,
107
+ type: parsed["type"] || "",
108
+ code: parsed["code"] || "",
109
+ message: parsed["message"] || "request failed with status #{status}",
110
+ request_id: parsed["request_id"],
111
+ retry_after: retry_after
112
+ )
113
+
114
+ retryable = status == 429 || status >= 500
115
+ if retryable && attempt < @max_retries
116
+ attempt += 1
117
+ last_retry_after = retry_after
118
+ next
119
+ end
120
+ raise error
121
+ end
122
+ end
123
+
124
+ def build_request(method, uri, payload)
125
+ klass = { get: Net::HTTP::Get, post: Net::HTTP::Post, put: Net::HTTP::Put,
126
+ patch: Net::HTTP::Patch, delete: Net::HTTP::Delete }.fetch(method)
127
+ req = klass.new(uri)
128
+ req["Authorization"] = "Bearer #{@api_key}"
129
+ if payload
130
+ req["Content-Type"] = "application/json"
131
+ req.body = payload
132
+ end
133
+ req
134
+ end
135
+
136
+ def perform(uri, req)
137
+ if @http_client
138
+ return @http_client.call(uri, req)
139
+ end
140
+ res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
141
+ http.request(req)
142
+ end
143
+ [res.code.to_i, res.each_header.to_h, res.body]
144
+ end
145
+
146
+ def backoff_seconds(attempt, retry_after)
147
+ return retry_after.to_f if retry_after && retry_after > 0
148
+ base_ms = [1000 * (2 ** attempt), 10000].min
149
+ jitter_ms = rand(250)
150
+ (base_ms + jitter_ms) / 1000.0
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,14 @@
1
+ module MailX
2
+ class APIError < StandardError
3
+ attr_reader :status, :type, :code, :request_id, :retry_after
4
+
5
+ def initialize(status:, type:, code:, message:, request_id: nil, retry_after: nil)
6
+ super(message)
7
+ @status = status
8
+ @type = type
9
+ @code = code
10
+ @request_id = request_id
11
+ @retry_after = retry_after
12
+ end
13
+ end
14
+ end
data/lib/mailx.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative "mailx/client"
2
+ require_relative "mailx/error"
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mailx-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - MailX
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-09-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Official Ruby client for the MailX transactional email API.
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/mailx.rb
20
+ - lib/mailx/client.rb
21
+ - lib/mailx/error.rb
22
+ homepage: https://github.com/UseMailx/mailx-ruby
23
+ licenses:
24
+ - MIT
25
+ metadata:
26
+ source_code_uri: https://github.com/UseMailx/mailx-ruby
27
+ bug_tracker_uri: https://github.com/UseMailx/mailx-ruby/issues
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '2.6'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubygems_version: 3.4.19
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: Official Ruby SDK for MailX
47
+ test_files: []