keplars 1.0.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 +7 -0
- data/Gemfile +3 -0
- data/README.md +179 -0
- data/lib/keplars/audiences.rb +28 -0
- data/lib/keplars/automations.rb +26 -0
- data/lib/keplars/client.rb +154 -0
- data/lib/keplars/contacts.rb +31 -0
- data/lib/keplars/domains.rb +29 -0
- data/lib/keplars/errors.rb +65 -0
- data/lib/keplars/resources.rb +41 -0
- data/lib/keplars/version.rb +3 -0
- data/lib/keplars.rb +15 -0
- metadata +128 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 68ef4b129e532343682259d772000266f0ee10b38d22a6520f1991922652ab84
|
|
4
|
+
data.tar.gz: 2e18e8eb8fe9a28f9e751251db6ff64e29c6f067e7c55e3ba44d7e5d740b8936
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 749cd3e2041498a1b263d2908959d67b65c7b45c09afeaf7f369bf18d546dfc3add1b034b87f19c4201b2c4f3a6d38da1d1bd372bac16c1f3e5a6bb8130ef64b
|
|
7
|
+
data.tar.gz: b6e1ae77b8959db16134f74e5b146051bfff8536f028e7a063de01f79276702592fbe8294707fb38d36327ad2907891d49f4c243d288f5b7d0ae5050d8beecad
|
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Keplars Email SDK for Ruby
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for the Keplars Email API - modern transactional email service with priority-based delivery.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'keplars'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem install keplars
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
require 'keplars'
|
|
29
|
+
|
|
30
|
+
client = Keplars::Client.new(api_key: 'kms_<workspaceId>.live_<secret>')
|
|
31
|
+
|
|
32
|
+
result = client.emails.send_instant(
|
|
33
|
+
from: 'noreply@yourdomain.com',
|
|
34
|
+
to: 'user@example.com',
|
|
35
|
+
subject: 'Your verification code is 123456',
|
|
36
|
+
html: '<p>Your verification code is <strong>123456</strong></p>'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
puts result[:data][:job_id]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### API Key Types
|
|
43
|
+
|
|
44
|
+
| Type | Format | Used for |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| Regular | `kms_<id>.live_<secret>` | Email sending |
|
|
47
|
+
| Admin | `kms_<id>.adm_<secret>` | Contacts, audiences, automations, domains |
|
|
48
|
+
|
|
49
|
+
## Email Sending
|
|
50
|
+
|
|
51
|
+
### Priority Levels
|
|
52
|
+
|
|
53
|
+
| Method | Delivery | Use case |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `send_instant` | 0–5 sec | OTPs, login codes, critical alerts |
|
|
56
|
+
| `send_high` | 0–30 sec | Transactional, notifications |
|
|
57
|
+
| `send_async` / `send` | 0–5 min | General transactional |
|
|
58
|
+
| `send_bulk` | Idle | Newsletters, marketing |
|
|
59
|
+
|
|
60
|
+
### Send with Recipients
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
result = client.emails.send_high(
|
|
64
|
+
from: 'noreply@yourdomain.com',
|
|
65
|
+
to: [{ email: 'user@example.com', name: 'John Doe' }],
|
|
66
|
+
cc: [{ email: 'manager@example.com' }],
|
|
67
|
+
subject: 'Order Confirmation',
|
|
68
|
+
html: '<p>Your order has been confirmed</p>'
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Response Shape
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
result[:success] # true
|
|
76
|
+
result[:message] # 'Email queued'
|
|
77
|
+
result[:data][:job_id] # 'job_abc123'
|
|
78
|
+
result[:data][:priority] # 'high'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Send with Template
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
result = client.emails.send(
|
|
85
|
+
from: 'noreply@yourdomain.com',
|
|
86
|
+
to: 'user@example.com',
|
|
87
|
+
subject: 'Password Reset',
|
|
88
|
+
template_id: 'tpl_reset_password',
|
|
89
|
+
template_data: {
|
|
90
|
+
name: 'John',
|
|
91
|
+
reset_link: 'https://example.com/reset/abc'
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Schedule Email
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
result = client.emails.schedule(
|
|
100
|
+
from: 'newsletter@yourdomain.com',
|
|
101
|
+
to: 'user@example.com',
|
|
102
|
+
subject: 'Your weekly digest',
|
|
103
|
+
html: '<p>Here is your weekly digest...</p>',
|
|
104
|
+
scheduled_for: '2026-06-01T09:00:00Z',
|
|
105
|
+
priority: 'bulk'
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Contacts (Admin API Key Required)
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
admin_client = Keplars::Client.new(api_key: 'kms_<workspaceId>.adm_<secret>')
|
|
113
|
+
|
|
114
|
+
admin_client.contacts.add(email: 'user@example.com', name: 'John Doe', audience_id: 'aud_abc123')
|
|
115
|
+
|
|
116
|
+
contact = admin_client.contacts.get('user@example.com')
|
|
117
|
+
|
|
118
|
+
contacts = admin_client.contacts.list(audience_id: 'aud_abc123', page: 1, limit: 20)
|
|
119
|
+
|
|
120
|
+
admin_client.contacts.update('user@example.com', name: 'Jane Doe')
|
|
121
|
+
|
|
122
|
+
admin_client.contacts.delete('user@example.com')
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Audiences (Admin API Key Required)
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
audience = admin_client.audiences.create(name: 'Newsletter Subscribers', description: 'Main list')
|
|
129
|
+
|
|
130
|
+
audiences = admin_client.audiences.list(page: 1, limit: 20)
|
|
131
|
+
|
|
132
|
+
audience = admin_client.audiences.get('aud_abc123')
|
|
133
|
+
|
|
134
|
+
admin_client.audiences.delete('aud_abc123')
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Automations (Admin API Key Required)
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
automations = admin_client.automations.list
|
|
141
|
+
|
|
142
|
+
automation = admin_client.automations.get('auto_abc123')
|
|
143
|
+
|
|
144
|
+
admin_client.automations.enroll('auto_abc123', email: 'user@example.com')
|
|
145
|
+
|
|
146
|
+
admin_client.automations.unenroll('auto_abc123', email: 'user@example.com')
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Domains (Admin API Key Required)
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
domain = admin_client.domains.add('mail.yourcompany.com')
|
|
153
|
+
|
|
154
|
+
domains = admin_client.domains.list
|
|
155
|
+
|
|
156
|
+
status = admin_client.domains.get_status('dom_abc123')
|
|
157
|
+
|
|
158
|
+
result = admin_client.domains.verify('dom_abc123')
|
|
159
|
+
|
|
160
|
+
api_key = admin_client.domains.create_api_key(domain_id: 'dom_abc123', name: 'Production Key')
|
|
161
|
+
|
|
162
|
+
admin_client.domains.delete('dom_abc123')
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Error Handling
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
require 'keplars'
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
result = client.emails.send_instant(...)
|
|
172
|
+
rescue Keplars::AuthenticationError => e
|
|
173
|
+
puts "Invalid API key: #{e.message}"
|
|
174
|
+
rescue Keplars::RateLimitError => e
|
|
175
|
+
puts "Rate limited"
|
|
176
|
+
rescue Keplars::ValidationError => e
|
|
177
|
+
puts "Validation error: #{e.message}"
|
|
178
|
+
end
|
|
179
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Keplars
|
|
2
|
+
module Resources
|
|
3
|
+
class Audiences < Base
|
|
4
|
+
def create(name:, description: nil)
|
|
5
|
+
body = { name: name }
|
|
6
|
+
body[:description] = description if description
|
|
7
|
+
@client.request('POST', '/api/v1/public/audiences/add-audience', body: body)[:data]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def list(page: nil, limit: nil)
|
|
11
|
+
params = {}
|
|
12
|
+
params[:page] = page if page
|
|
13
|
+
params[:limit] = limit if limit
|
|
14
|
+
|
|
15
|
+
query = @client.send(:build_query_string, params)
|
|
16
|
+
@client.request('GET', "/api/v1/public/audiences/get-audiences#{query}")[:data]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get(id)
|
|
20
|
+
@client.request('GET', "/api/v1/public/audiences/get-audience?id=#{URI.encode_www_form_component(id)}")[:data]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def delete(id)
|
|
24
|
+
@client.request('DELETE', "/api/v1/public/audiences/delete-audience?id=#{URI.encode_www_form_component(id)}")[:data]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Keplars
|
|
2
|
+
module Resources
|
|
3
|
+
class Automations < Base
|
|
4
|
+
def list(page: nil, limit: nil)
|
|
5
|
+
params = {}
|
|
6
|
+
params[:page] = page if page
|
|
7
|
+
params[:limit] = limit if limit
|
|
8
|
+
|
|
9
|
+
query = @client.send(:build_query_string, params)
|
|
10
|
+
@client.request('GET', "/api/v1/public/automations/get-all#{query}")[:data]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get(id)
|
|
14
|
+
@client.request('GET', "/api/v1/public/automations/get-automation/#{id}")[:data]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def enroll(id, email:)
|
|
18
|
+
@client.request('POST', "/api/v1/public/automations/add-automation/#{id}/enroll", body: { email: email })[:data]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def unenroll(id, email:)
|
|
22
|
+
@client.request('DELETE', "/api/v1/public/automations/delete-automation/#{id}/subscribers", body: { email: email })[:data]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module Keplars
|
|
6
|
+
class Client
|
|
7
|
+
DEFAULT_BASE_URL = 'https://api.keplars.com'
|
|
8
|
+
DEFAULT_TIMEOUT = 30
|
|
9
|
+
DEFAULT_MAX_RETRIES = 3
|
|
10
|
+
DEFAULT_RETRY_DELAY = 1
|
|
11
|
+
|
|
12
|
+
attr_reader :emails, :contacts, :audiences, :automations, :domains
|
|
13
|
+
|
|
14
|
+
def initialize(api_key: nil, base_url: nil, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES, retry_delay: DEFAULT_RETRY_DELAY)
|
|
15
|
+
@api_key = api_key || ENV['KEPLARS_API_KEY']
|
|
16
|
+
raise ArgumentError, 'API key is required. Set KEPLARS_API_KEY or pass api_key parameter' if @api_key.nil? || @api_key.empty?
|
|
17
|
+
|
|
18
|
+
unless validate_api_key(@api_key)
|
|
19
|
+
raise ArgumentError, 'Invalid API key format. Expected: kms_<id>.live_<secret> or kms_<id>.adm_<secret>'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
resolved_base_url = base_url || ENV['KEPLARS_BASE_URL'] || DEFAULT_BASE_URL
|
|
23
|
+
@base_url = resolved_base_url.sub(/\/$/, '')
|
|
24
|
+
@timeout = timeout
|
|
25
|
+
@max_retries = max_retries
|
|
26
|
+
@retry_delay = retry_delay
|
|
27
|
+
|
|
28
|
+
@emails = Resources::Emails.new(self)
|
|
29
|
+
@contacts = Resources::Contacts.new(self)
|
|
30
|
+
@audiences = Resources::Audiences.new(self)
|
|
31
|
+
@automations = Resources::Automations.new(self)
|
|
32
|
+
@domains = Resources::Domains.new(self)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def request(method, path, body: nil, retry_count: 0)
|
|
36
|
+
uri = URI("#{@base_url}#{path}")
|
|
37
|
+
|
|
38
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
39
|
+
http.use_ssl = uri.scheme == 'https'
|
|
40
|
+
http.read_timeout = @timeout
|
|
41
|
+
http.open_timeout = @timeout
|
|
42
|
+
|
|
43
|
+
request_class = case method.upcase
|
|
44
|
+
when 'GET' then Net::HTTP::Get
|
|
45
|
+
when 'POST' then Net::HTTP::Post
|
|
46
|
+
when 'PUT' then Net::HTTP::Put
|
|
47
|
+
when 'PATCH' then Net::HTTP::Patch
|
|
48
|
+
when 'DELETE' then Net::HTTP::Delete
|
|
49
|
+
else raise ArgumentError, "Unsupported HTTP method: #{method}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
request = request_class.new(uri.request_uri)
|
|
53
|
+
request['Authorization'] = "Bearer #{@api_key}"
|
|
54
|
+
request['Content-Type'] = 'application/json'
|
|
55
|
+
request['User-Agent'] = "keplars-ruby/#{VERSION}"
|
|
56
|
+
|
|
57
|
+
request.body = body.to_json if body
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
response = http.request(request)
|
|
61
|
+
rate_limit_info = extract_rate_limit_info(response)
|
|
62
|
+
|
|
63
|
+
if response.code.to_i >= 400
|
|
64
|
+
handle_error_response(response, retry_count, method, path, body)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
result = response.body && !response.body.empty? ? JSON.parse(response.body, symbolize_names: true) : nil
|
|
68
|
+
{ data: result, rate_limit_info: rate_limit_info }
|
|
69
|
+
rescue StandardError => e
|
|
70
|
+
if retryable_error?(e) && retry_count < @max_retries
|
|
71
|
+
delay = calculate_backoff(retry_count)
|
|
72
|
+
sleep(delay)
|
|
73
|
+
request(method, path, body: body, retry_count: retry_count + 1)
|
|
74
|
+
else
|
|
75
|
+
raise NetworkError.new("Request failed: #{e.message}", original_error: e)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def extract_rate_limit_info(response)
|
|
83
|
+
limit = response['X-RateLimit-Limit']
|
|
84
|
+
remaining = response['X-RateLimit-Remaining']
|
|
85
|
+
reset = response['X-RateLimit-Reset']
|
|
86
|
+
|
|
87
|
+
return nil unless limit && remaining && reset
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
limit: limit.to_i,
|
|
91
|
+
remaining: remaining.to_i,
|
|
92
|
+
reset: reset.to_i
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def handle_error_response(response, retry_count, method, path, body)
|
|
97
|
+
error_data = JSON.parse(response.body, symbolize_names: true) rescue {}
|
|
98
|
+
status_code = response.code.to_i
|
|
99
|
+
message = error_data[:message] || "HTTP #{status_code}"
|
|
100
|
+
|
|
101
|
+
case status_code
|
|
102
|
+
when 400
|
|
103
|
+
raise ValidationError.new(message, details: nil, request_id: nil)
|
|
104
|
+
when 401
|
|
105
|
+
raise AuthenticationError.new(message, request_id: nil)
|
|
106
|
+
when 403
|
|
107
|
+
raise AuthorizationError.new(message, request_id: nil)
|
|
108
|
+
when 404
|
|
109
|
+
raise KeplarsError.new(message, code: 'NOT_FOUND', status_code: status_code)
|
|
110
|
+
when 409
|
|
111
|
+
raise KeplarsError.new(message, code: 'CONFLICT', status_code: status_code)
|
|
112
|
+
when 429
|
|
113
|
+
raise RateLimitError.new(message, retry_after: 60, request_id: nil)
|
|
114
|
+
else
|
|
115
|
+
if status_code >= 500
|
|
116
|
+
if retry_count < @max_retries
|
|
117
|
+
delay = calculate_backoff(retry_count)
|
|
118
|
+
sleep(delay)
|
|
119
|
+
end
|
|
120
|
+
raise InternalError.new(message, request_id: nil)
|
|
121
|
+
else
|
|
122
|
+
raise KeplarsError.new(message, code: 'UNKNOWN_ERROR', status_code: status_code)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def calculate_backoff(retry_count)
|
|
128
|
+
exponential_delay = @retry_delay * (2**retry_count)
|
|
129
|
+
jitter = rand * 0.3 * exponential_delay
|
|
130
|
+
[exponential_delay + jitter, 30].min
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def retryable_error?(error)
|
|
134
|
+
error.is_a?(Timeout::Error) ||
|
|
135
|
+
error.is_a?(Errno::ECONNRESET) ||
|
|
136
|
+
error.is_a?(Errno::ETIMEDOUT) ||
|
|
137
|
+
error.is_a?(Errno::ECONNREFUSED)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def build_query_string(params)
|
|
141
|
+
return '' if params.nil? || params.empty?
|
|
142
|
+
|
|
143
|
+
filtered = params.reject { |_, v| v.nil? || v == '' }
|
|
144
|
+
return '' if filtered.empty?
|
|
145
|
+
|
|
146
|
+
query = filtered.map { |k, v| "#{k}=#{URI.encode_www_form_component(v)}" }.join('&')
|
|
147
|
+
"?#{query}"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def validate_api_key(api_key)
|
|
151
|
+
api_key.match?(/\Akms_[a-f0-9]+\.(live|adm)_[a-f0-9]+\z/)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Keplars
|
|
2
|
+
module Resources
|
|
3
|
+
class Contacts < Base
|
|
4
|
+
def add(**params)
|
|
5
|
+
@client.request('POST', '/api/v1/public/contacts/add-contact', body: params.compact)[:data]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def get(email)
|
|
9
|
+
@client.request('GET', "/api/v1/public/contacts/get-contact?email=#{URI.encode_www_form_component(email)}")[:data]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def list(audience_id: nil, page: nil, limit: nil)
|
|
13
|
+
params = {}
|
|
14
|
+
params[:audience_id] = audience_id if audience_id
|
|
15
|
+
params[:page] = page if page
|
|
16
|
+
params[:limit] = limit if limit
|
|
17
|
+
|
|
18
|
+
query = @client.send(:build_query_string, params)
|
|
19
|
+
@client.request('GET', "/api/v1/public/contacts/get-contacts#{query}")[:data]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update(email, **params)
|
|
23
|
+
@client.request('PATCH', "/api/v1/public/contacts/update-contact?email=#{URI.encode_www_form_component(email)}", body: params.compact)[:data]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete(email)
|
|
27
|
+
@client.request('DELETE', "/api/v1/public/contacts/delete-contact?email=#{URI.encode_www_form_component(email)}")[:data]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Keplars
|
|
2
|
+
module Resources
|
|
3
|
+
class Domains < Base
|
|
4
|
+
def add(domain)
|
|
5
|
+
@client.request('POST', '/api/v1/public/domains/add-domain', body: { domain: domain })[:data]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def list
|
|
9
|
+
@client.request('GET', '/api/v1/public/domains/get-domains')[:data]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def get_status(domain_id)
|
|
13
|
+
@client.request('GET', "/api/v1/public/domains/domain-status/#{domain_id}")[:data]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def verify(domain_id)
|
|
17
|
+
@client.request('POST', "/api/v1/public/domains/verify-domain/#{domain_id}")[:data]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def delete(domain_id)
|
|
21
|
+
@client.request('DELETE', "/api/v1/public/domains/delete-domain/#{domain_id}")[:data]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create_api_key(**params)
|
|
25
|
+
@client.request('POST', '/api/v1/public/domains/api-keys/create', body: params.compact)[:data]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Keplars
|
|
2
|
+
class KeplarsError < StandardError
|
|
3
|
+
attr_reader :code, :request_id, :status_code
|
|
4
|
+
|
|
5
|
+
def initialize(message, code: nil, request_id: nil, status_code: nil)
|
|
6
|
+
super(message)
|
|
7
|
+
@code = code
|
|
8
|
+
@request_id = request_id
|
|
9
|
+
@status_code = status_code
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ValidationError < KeplarsError
|
|
14
|
+
attr_reader :details
|
|
15
|
+
|
|
16
|
+
def initialize(message, details: nil, request_id: nil)
|
|
17
|
+
super(message, code: 'VALIDATION_ERROR', request_id: request_id)
|
|
18
|
+
@details = details
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class AuthenticationError < KeplarsError
|
|
23
|
+
def initialize(message, request_id: nil)
|
|
24
|
+
super(message, code: 'AUTHENTICATION_ERROR', request_id: request_id)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class AuthorizationError < KeplarsError
|
|
29
|
+
def initialize(message, request_id: nil)
|
|
30
|
+
super(message, code: 'AUTHORIZATION_ERROR', request_id: request_id)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class DomainNotVerifiedError < KeplarsError
|
|
35
|
+
attr_reader :domain, :verification_status
|
|
36
|
+
|
|
37
|
+
def initialize(message, domain:, verification_status:, request_id: nil)
|
|
38
|
+
super(message, code: 'DOMAIN_NOT_VERIFIED', request_id: request_id)
|
|
39
|
+
@domain = domain
|
|
40
|
+
@verification_status = verification_status
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class RateLimitError < KeplarsError
|
|
45
|
+
attr_reader :retry_after
|
|
46
|
+
|
|
47
|
+
def initialize(message, retry_after:, request_id: nil)
|
|
48
|
+
super(message, code: 'RATE_LIMIT_EXCEEDED', request_id: request_id)
|
|
49
|
+
@retry_after = retry_after
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class InternalError < KeplarsError
|
|
54
|
+
def initialize(message, request_id: nil)
|
|
55
|
+
super(message, code: 'INTERNAL_ERROR', request_id: request_id)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class NetworkError < KeplarsError
|
|
60
|
+
def initialize(message, original_error: nil)
|
|
61
|
+
super(message, code: 'NETWORK_ERROR')
|
|
62
|
+
@original_error = original_error
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Keplars
|
|
2
|
+
module Resources
|
|
3
|
+
class Base
|
|
4
|
+
def initialize(client)
|
|
5
|
+
@client = client
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class Emails < Base
|
|
10
|
+
def send_instant(**args)
|
|
11
|
+
body = args.compact
|
|
12
|
+
@client.request('POST', '/api/v1/public/send-email/instant', body: body)[:data]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def send_high(**args)
|
|
16
|
+
body = args.compact
|
|
17
|
+
@client.request('POST', '/api/v1/public/send-email/high', body: body)[:data]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def send_async(**args)
|
|
21
|
+
body = args.compact
|
|
22
|
+
@client.request('POST', '/api/v1/public/send-email/async', body: body)[:data]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send_bulk(**args)
|
|
26
|
+
body = args.compact
|
|
27
|
+
@client.request('POST', '/api/v1/public/send-email/bulk', body: body)[:data]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def send(**args)
|
|
31
|
+
send_async(**args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def schedule(**args)
|
|
35
|
+
body = args.compact
|
|
36
|
+
body[:priority] ||= 'async'
|
|
37
|
+
@client.request('POST', '/api/v1/public/send-email/schedule', body: body)[:data]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/keplars.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
require_relative 'keplars/version'
|
|
6
|
+
require_relative 'keplars/errors'
|
|
7
|
+
require_relative 'keplars/resources'
|
|
8
|
+
require_relative 'keplars/contacts'
|
|
9
|
+
require_relative 'keplars/audiences'
|
|
10
|
+
require_relative 'keplars/automations'
|
|
11
|
+
require_relative 'keplars/domains'
|
|
12
|
+
require_relative 'keplars/client'
|
|
13
|
+
|
|
14
|
+
module Keplars
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: keplars
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Keplars
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-17 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: faraday
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.8'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.8'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: faraday-retry
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.2'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.2'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.12'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.12'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: webmock
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.19'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.19'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rubocop
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '1.59'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '1.59'
|
|
83
|
+
description: Modern transactional email service SDK with priority-based delivery for
|
|
84
|
+
Ruby
|
|
85
|
+
email:
|
|
86
|
+
- support@keplars.com
|
|
87
|
+
executables: []
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- Gemfile
|
|
92
|
+
- README.md
|
|
93
|
+
- lib/keplars.rb
|
|
94
|
+
- lib/keplars/audiences.rb
|
|
95
|
+
- lib/keplars/automations.rb
|
|
96
|
+
- lib/keplars/client.rb
|
|
97
|
+
- lib/keplars/contacts.rb
|
|
98
|
+
- lib/keplars/domains.rb
|
|
99
|
+
- lib/keplars/errors.rb
|
|
100
|
+
- lib/keplars/resources.rb
|
|
101
|
+
- lib/keplars/version.rb
|
|
102
|
+
homepage: https://keplars.com
|
|
103
|
+
licenses:
|
|
104
|
+
- MIT
|
|
105
|
+
metadata:
|
|
106
|
+
homepage_uri: https://keplars.com
|
|
107
|
+
source_code_uri: https://github.com/Swing-Technologies/keplars-email-sdk
|
|
108
|
+
changelog_uri: https://github.com/Swing-Technologies/keplars-email-sdk/blob/main/CHANGELOG.md
|
|
109
|
+
post_install_message:
|
|
110
|
+
rdoc_options: []
|
|
111
|
+
require_paths:
|
|
112
|
+
- lib
|
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: 3.0.0
|
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - ">="
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '0'
|
|
123
|
+
requirements: []
|
|
124
|
+
rubygems_version: 3.4.19
|
|
125
|
+
signing_key:
|
|
126
|
+
specification_version: 4
|
|
127
|
+
summary: Official Ruby SDK for Keplars Email API
|
|
128
|
+
test_files: []
|