mailblastr 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/LICENSE +21 -0
- data/README.md +332 -0
- data/lib/mailblastr/api_keys.rb +23 -0
- data/lib/mailblastr/audiences.rb +39 -0
- data/lib/mailblastr/automations.rb +62 -0
- data/lib/mailblastr/campaigns.rb +57 -0
- data/lib/mailblastr/client.rb +208 -0
- data/lib/mailblastr/contact_properties.rb +33 -0
- data/lib/mailblastr/contacts.rb +144 -0
- data/lib/mailblastr/domains.rb +78 -0
- data/lib/mailblastr/emails.rb +113 -0
- data/lib/mailblastr/error.rb +28 -0
- data/lib/mailblastr/events.rb +35 -0
- data/lib/mailblastr/logs.rb +24 -0
- data/lib/mailblastr/polls.rb +18 -0
- data/lib/mailblastr/segments.rb +44 -0
- data/lib/mailblastr/templates.rb +42 -0
- data/lib/mailblastr/topics.rb +38 -0
- data/lib/mailblastr/version.rb +5 -0
- data/lib/mailblastr/webhooks.rb +130 -0
- data/lib/mailblastr.rb +76 -0
- metadata +83 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bace5e9375b668304c40339acea2334895be7f7af3221f628ad4f7fa21762752
|
|
4
|
+
data.tar.gz: a68a14501aab6cf50309f0bb0d938dad58a5b4a96b31cb09cb9b5285f63979eb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ac99877d9dc0970341b2c1b16e075dfb1f32832d6f10d6fbc220243ccd02a73597cbda5534f5918b5f28f577a2090b5becfa51f23d2e22e90f901446d76a3d1f
|
|
7
|
+
data.tar.gz: 7e0e7c62751f5042054e502964595e6ffbc6f732469dca2076bdb748031449efb0023bc5fbd1ae5d1901a4f2ce0aed999957dc82ea0b4b8d693c6416543d5225
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MailBlastr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# mailblastr
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for the [MailBlastr](https://www.mailblastr.com) email API — send transactional and marketing email from your own verified domain.
|
|
4
|
+
|
|
5
|
+
Zero runtime dependencies: the gem uses only Ruby's standard library (`Net::HTTP`, `JSON`, `OpenSSL`).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gem install mailblastr
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or in your Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem "mailblastr"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require "mailblastr"
|
|
23
|
+
|
|
24
|
+
Mailblastr.api_key = "mb_xxxxxxxxx"
|
|
25
|
+
|
|
26
|
+
# or
|
|
27
|
+
Mailblastr.configure do |config|
|
|
28
|
+
config.api_key = ENV["MAILBLASTR_API_KEY"]
|
|
29
|
+
# config.base_url = "https://www.mailblastr.com/api" # override your API host
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
sent = Mailblastr::Emails.send({
|
|
37
|
+
from: "Acme <hello@yourdomain.com>",
|
|
38
|
+
to: ["user@example.com"],
|
|
39
|
+
subject: "Hello from MailBlastr",
|
|
40
|
+
html: "<p>Your first email 🎉</p>"
|
|
41
|
+
})
|
|
42
|
+
puts sent["id"]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Params are plain hashes with snake_case keys, passed through as JSON. Successful calls return the parsed response (a Hash, or a raw String for binary downloads). Any non-2xx response raises `Mailblastr::Error`:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
begin
|
|
49
|
+
Mailblastr::Emails.send(params)
|
|
50
|
+
rescue Mailblastr::Error => e
|
|
51
|
+
puts e.status_code # => 422
|
|
52
|
+
puts e.name # => "validation_error"
|
|
53
|
+
puts e.message # => human-readable explanation
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Domain-first model
|
|
58
|
+
|
|
59
|
+
MailBlastr is **domain-first**: each sending domain has its own pool of contacts. The same email address on two domains is two records with separate consent, so unsubscribes on one product never leak into another.
|
|
60
|
+
|
|
61
|
+
That means `domain` (the sending domain, e.g. `"yourdomain.com"` — one of your verified domains) is **required** on:
|
|
62
|
+
|
|
63
|
+
- `Contacts.create` / `Contacts.list` (the flat `/contacts` API — pass `audience_id:` to use the nested audience routes instead)
|
|
64
|
+
- `Segments.create` / `Segments.list`
|
|
65
|
+
- `Topics.create` / `Topics.list`
|
|
66
|
+
- `Campaigns.create` (picks the contact pool the campaign targets; `from` may be a different verified domain)
|
|
67
|
+
- `Automations.create` and `Events.send` (only automations belonging to that domain are triggered)
|
|
68
|
+
|
|
69
|
+
## Emails
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
Mailblastr::Emails.send({ from: from, to: to, subject: subject, html: html })
|
|
73
|
+
Mailblastr::Emails.list({ limit: 20, after: cursor }) # cursor pagination
|
|
74
|
+
Mailblastr::Emails.get(email_id)
|
|
75
|
+
Mailblastr::Emails.list_attachments(email_id)
|
|
76
|
+
Mailblastr::Emails.get_attachment(email_id, attachment_id)
|
|
77
|
+
Mailblastr::Emails.update(email_id, { scheduled_at: "2026-08-01T09:00:00Z" }) # reschedule
|
|
78
|
+
Mailblastr::Emails.cancel(email_id)
|
|
79
|
+
|
|
80
|
+
# Batch send — up to 100 emails in one request
|
|
81
|
+
Mailblastr::Batch.send([
|
|
82
|
+
{ from: from, to: ["a@example.com"], subject: "Hi A", html: "<p>A</p>" },
|
|
83
|
+
{ from: from, to: ["b@example.com"], subject: "Hi B", html: "<p>B</p>" }
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
# Attachments: hosted URL (path) or inline base64 (content)
|
|
87
|
+
Mailblastr::Emails.send({
|
|
88
|
+
from: from, to: to, subject: "Your invoice", html: "<p>Attached.</p>",
|
|
89
|
+
attachments: [
|
|
90
|
+
{ filename: "invoice.pdf", path: "https://yourdomain.com/invoices/invoice.pdf" },
|
|
91
|
+
{ filename: "report.csv", content: base64_content, content_type: "text/csv" }
|
|
92
|
+
]
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Inbound email
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
Mailblastr::Emails::Receiving.list
|
|
100
|
+
Mailblastr::Emails::Receiving.get(id)
|
|
101
|
+
Mailblastr::Emails::Receiving.list_attachments(id)
|
|
102
|
+
Mailblastr::Emails::Receiving.get_attachment(id, attachment_id) # => raw bytes (String)
|
|
103
|
+
Mailblastr::Emails::Receiving.raw(id) # => original RFC822 message
|
|
104
|
+
Mailblastr::Emails::Receiving.forward(id, { from: "you@yourdomain.com", to: "team@you.com" })
|
|
105
|
+
Mailblastr::Emails::Receiving.reply(id, { from: "you@yourdomain.com", html: "<p>Thanks!</p>" })
|
|
106
|
+
Mailblastr::Emails::Receiving.delete(id)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Domains
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
Mailblastr::Domains.create({ name: "yourdomain.com" })
|
|
113
|
+
Mailblastr::Domains.get(id)
|
|
114
|
+
Mailblastr::Domains.list
|
|
115
|
+
Mailblastr::Domains.update(id, { click_tracking: true })
|
|
116
|
+
Mailblastr::Domains.verify(id)
|
|
117
|
+
Mailblastr::Domains.delete(id)
|
|
118
|
+
|
|
119
|
+
# Claim a domain verified in another account
|
|
120
|
+
Mailblastr::Domains.claim({ name: "yourdomain.com" })
|
|
121
|
+
Mailblastr::Domains.get_claim(id)
|
|
122
|
+
Mailblastr::Domains.verify_claim(id)
|
|
123
|
+
|
|
124
|
+
# One-click DNS setup
|
|
125
|
+
Mailblastr::Domains.detect_dns(id)
|
|
126
|
+
Mailblastr::Domains.apply_cloudflare_dns(id, { token: cf_token })
|
|
127
|
+
Mailblastr::Domains.apply_godaddy_dns(id, { key: key, secret: secret })
|
|
128
|
+
Mailblastr::Domains.apply_namecheap_dns(id, { apiUser: user, apiKey: key })
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Contacts (domain-first)
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
Mailblastr::Contacts.create({ domain: "yourdomain.com", email: "user@example.com", first_name: "Ada" })
|
|
135
|
+
Mailblastr::Contacts.list({ domain: "yourdomain.com" })
|
|
136
|
+
Mailblastr::Contacts.get({ id: contact_id }) # by id (exact) …
|
|
137
|
+
Mailblastr::Contacts.get({ id: "user@example.com", domain: "yourdomain.com" }) # … or email + domain
|
|
138
|
+
Mailblastr::Contacts.update({ id: contact_id, unsubscribed: true })
|
|
139
|
+
Mailblastr::Contacts.delete({ id: contact_id })
|
|
140
|
+
|
|
141
|
+
# Nested audience variants
|
|
142
|
+
Mailblastr::Contacts.create({ audience_id: aud_id, email: "user@example.com" })
|
|
143
|
+
Mailblastr::Contacts.list({ audience_id: aud_id, segment_id: seg_id })
|
|
144
|
+
|
|
145
|
+
# Bulk import
|
|
146
|
+
Mailblastr::Contacts.batch({ audience_id: aud_id, contacts: [{ email: "a@b.com" }], on_conflict: "skip" })
|
|
147
|
+
Mailblastr::Contacts.import({ audience_id: aud_id, csv: "email,company\na@b.com,Acme" })
|
|
148
|
+
|
|
149
|
+
# Segments & topics per contact
|
|
150
|
+
Mailblastr::Contacts.add_to_segment(contact_id, segment_id)
|
|
151
|
+
Mailblastr::Contacts.remove_from_segment(contact_id, segment_id)
|
|
152
|
+
Mailblastr::Contacts.list_segments(contact_id)
|
|
153
|
+
Mailblastr::Contacts.get_topics(contact_id)
|
|
154
|
+
Mailblastr::Contacts.update_topics(contact_id, { topics: [{ id: topic_id, subscription: "opt_in" }] })
|
|
155
|
+
|
|
156
|
+
# Custom contact properties ({{merge_tags}})
|
|
157
|
+
Mailblastr::ContactProperties.create({ key: "plan", type: "string", fallback_value: "free" })
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Audiences
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
Mailblastr::Audiences.create({ name: "Newsletter" })
|
|
164
|
+
Mailblastr::Audiences.get(id)
|
|
165
|
+
Mailblastr::Audiences.list
|
|
166
|
+
Mailblastr::Audiences.update(id, { name: "Weekly newsletter" })
|
|
167
|
+
Mailblastr::Audiences.delete(id)
|
|
168
|
+
|
|
169
|
+
# Import from a link-shared Google Sheet
|
|
170
|
+
Mailblastr::Audiences.import_sheet(id, { url: sheet_url, segment_name: "June leads" })
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Segments & Topics (domain-first)
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
Mailblastr::Segments.create({ domain: "yourdomain.com", name: "VIP", filter: { status: "subscribed" } })
|
|
177
|
+
Mailblastr::Segments.list({ domain: "yourdomain.com" })
|
|
178
|
+
Mailblastr::Segments.get(id)
|
|
179
|
+
Mailblastr::Segments.contacts(id) # preview who matches
|
|
180
|
+
Mailblastr::Segments.update(id, { name: "VIP customers" })
|
|
181
|
+
Mailblastr::Segments.delete(id)
|
|
182
|
+
|
|
183
|
+
Mailblastr::Topics.create({ domain: "yourdomain.com", name: "Product updates", default_subscription: "opt_in" })
|
|
184
|
+
Mailblastr::Topics.list({ domain: "yourdomain.com" })
|
|
185
|
+
Mailblastr::Topics.update(id, { description: "New features" })
|
|
186
|
+
Mailblastr::Topics.delete(id)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Campaigns (domain-first)
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
campaign = Mailblastr::Campaigns.create({
|
|
193
|
+
domain: "yourdomain.com", # REQUIRED — the contact pool this campaign targets
|
|
194
|
+
from: "Acme <hello@yourdomain.com>",
|
|
195
|
+
subject: "Big news",
|
|
196
|
+
html: "<p>Hello {{first_name}}</p>",
|
|
197
|
+
segment_id: seg_id # optional — subset instead of everyone
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
Mailblastr::Campaigns.send(campaign["id"]) # send now
|
|
201
|
+
Mailblastr::Campaigns.send(campaign["id"], { scheduled_at: "2026-08-01T09:00:00Z" }) # or schedule
|
|
202
|
+
Mailblastr::Campaigns.cancel(campaign["id"])
|
|
203
|
+
Mailblastr::Campaigns.stats(campaign["id"])
|
|
204
|
+
Mailblastr::Campaigns.ab(campaign["id"]) # A/B winner evaluation
|
|
205
|
+
Mailblastr::Campaigns.get(campaign["id"])
|
|
206
|
+
Mailblastr::Campaigns.list({ limit: 25 })
|
|
207
|
+
Mailblastr::Campaigns.update(campaign["id"], { subject: "Bigger news" })
|
|
208
|
+
Mailblastr::Campaigns.delete(campaign["id"])
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Templates
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
tmpl = Mailblastr::Templates.create({ name: "Welcome", subject: "Welcome!", html: "<p>Hi {{first_name}}</p>" })
|
|
215
|
+
Mailblastr::Templates.publish(tmpl["id"])
|
|
216
|
+
Mailblastr::Templates.duplicate(tmpl["id"], { name: "Welcome v2" })
|
|
217
|
+
Mailblastr::Templates.get(tmpl["id"])
|
|
218
|
+
Mailblastr::Templates.list
|
|
219
|
+
Mailblastr::Templates.update(tmpl["id"], { subject: "Welcome aboard!" })
|
|
220
|
+
Mailblastr::Templates.delete(tmpl["id"])
|
|
221
|
+
|
|
222
|
+
# Send with a template
|
|
223
|
+
Mailblastr::Emails.send({ from: from, to: to, template_id: tmpl["id"], variables: { first_name: "Ada" } })
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Automations & Events (domain-first)
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
automation = Mailblastr::Automations.create({
|
|
230
|
+
name: "Welcome series",
|
|
231
|
+
domain: "yourdomain.com", # REQUIRED
|
|
232
|
+
trigger: "contact.created"
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
Mailblastr::Automations.add_step(automation["id"], { type: "send_email", config: { template_id: tmpl_id } })
|
|
236
|
+
Mailblastr::Automations.update(automation["id"], { status: "enabled" })
|
|
237
|
+
|
|
238
|
+
# Fire a custom event — only yourdomain.com's automations are triggered
|
|
239
|
+
Mailblastr::Events.send({
|
|
240
|
+
event: "signup.completed",
|
|
241
|
+
domain: "yourdomain.com", # REQUIRED
|
|
242
|
+
email: "user@example.com",
|
|
243
|
+
payload: { plan: "pro" }
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
# Event definitions
|
|
247
|
+
Mailblastr::Events.create({ name: "signup.completed", schema: { plan: "string" } })
|
|
248
|
+
Mailblastr::Events.list
|
|
249
|
+
Mailblastr::Events.delete(event_id)
|
|
250
|
+
|
|
251
|
+
# Inspect execution
|
|
252
|
+
runs = Mailblastr::Automations.runs(automation["id"], { limit: 25 })
|
|
253
|
+
Mailblastr::Automations.get_run(automation["id"], runs["data"].first["id"])
|
|
254
|
+
Mailblastr::Automations.delete_step(automation["id"], step_id)
|
|
255
|
+
Mailblastr::Automations.stop(automation["id"])
|
|
256
|
+
Mailblastr::Automations.delete(automation["id"])
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Webhooks
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
hook = Mailblastr::Webhooks.create({
|
|
263
|
+
endpoint: "https://yourapp.com/hooks/mailblastr",
|
|
264
|
+
events: ["email.delivered", "email.bounced", "contact.unsubscribed"]
|
|
265
|
+
})
|
|
266
|
+
hook["signing_secret"] # shown ONCE — store it
|
|
267
|
+
|
|
268
|
+
Mailblastr::Webhooks.list
|
|
269
|
+
Mailblastr::Webhooks.update(hook["id"], { status: "disabled" })
|
|
270
|
+
Mailblastr::Webhooks.rotate(hook["id"]) # new secret returned once
|
|
271
|
+
Mailblastr::Webhooks.test(hook["id"])
|
|
272
|
+
Mailblastr::Webhooks.delete(hook["id"])
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Verifying deliveries
|
|
276
|
+
|
|
277
|
+
`verify_signature` checks the Svix-style HMAC-SHA256 signature locally (no HTTP request). Pass the **exact raw request body** — re-serializing parsed JSON breaks the signature.
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
result = Mailblastr::Webhooks.verify_signature(
|
|
281
|
+
request.raw_post, # raw body string
|
|
282
|
+
{
|
|
283
|
+
"svix-id" => request.headers["svix-id"],
|
|
284
|
+
"svix-timestamp" => request.headers["svix-timestamp"],
|
|
285
|
+
"svix-signature" => request.headers["svix-signature"]
|
|
286
|
+
},
|
|
287
|
+
signing_secret # the whsec_... secret from create/rotate
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
head :unauthorized unless result[:valid]
|
|
291
|
+
# result => { valid: true } or { valid: false, reason: "no_match" | "timestamp_out_of_tolerance" | ... }
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Pass `tolerance: 0` to skip the timestamp freshness check (default 300 seconds).
|
|
295
|
+
|
|
296
|
+
## API keys, Logs & Polls
|
|
297
|
+
|
|
298
|
+
```ruby
|
|
299
|
+
Mailblastr::ApiKeys.create({ name: "CI", permission: "sending_access" })
|
|
300
|
+
Mailblastr::ApiKeys.list
|
|
301
|
+
Mailblastr::ApiKeys.delete(key_id)
|
|
302
|
+
|
|
303
|
+
Mailblastr::Logs.list({ limit: 100, method: "POST", status: 429 })
|
|
304
|
+
Mailblastr::Logs.get(log_id)
|
|
305
|
+
|
|
306
|
+
Mailblastr::Polls.list
|
|
307
|
+
Mailblastr::Polls.get(email_id) # aggregated answer breakdown
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Pagination
|
|
311
|
+
|
|
312
|
+
`list` methods accept cursor pagination — `{ limit:, after:, before: }` — appended as a query string:
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
Mailblastr::Campaigns.list({ limit: 25, after: "cursor_abc" })
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Idempotency
|
|
319
|
+
|
|
320
|
+
Pass an idempotency key to safely retry a create (24h window):
|
|
321
|
+
|
|
322
|
+
```ruby
|
|
323
|
+
Mailblastr::Emails.send(payload, { idempotency_key: "order-123" })
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Documentation
|
|
327
|
+
|
|
328
|
+
Full docs: <https://www.mailblastr.com/docs>
|
|
329
|
+
|
|
330
|
+
## License
|
|
331
|
+
|
|
332
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailblastr
|
|
4
|
+
module ApiKeys
|
|
5
|
+
class << self
|
|
6
|
+
# Create an API key — the full token is returned only once, here.
|
|
7
|
+
# POST /api-keys — params: { name:, permission: "full_access"|"sending_access", domain_id: }
|
|
8
|
+
def create(params)
|
|
9
|
+
Client.request(:post, "/api-keys", body: params)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# GET /api-keys
|
|
13
|
+
def list
|
|
14
|
+
Client.request(:get, "/api-keys")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# DELETE /api-keys/:id
|
|
18
|
+
def delete(api_key_id)
|
|
19
|
+
Client.request(:delete, "/api-keys/#{Client.path_escape(api_key_id)}")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailblastr
|
|
4
|
+
module Audiences
|
|
5
|
+
class << self
|
|
6
|
+
# POST /audiences — params: { name: "..." }
|
|
7
|
+
def create(params)
|
|
8
|
+
Client.request(:post, "/audiences", body: params)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# GET /audiences/:id
|
|
12
|
+
def get(audience_id)
|
|
13
|
+
Client.request(:get, "/audiences/#{Client.path_escape(audience_id)}")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# GET /audiences
|
|
17
|
+
def list(params = {})
|
|
18
|
+
Client.request(:get, "/audiences", query: Client.pagination(params))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Rename an audience. PATCH /audiences/:id
|
|
22
|
+
def update(audience_id, params)
|
|
23
|
+
Client.request(:patch, "/audiences/#{Client.path_escape(audience_id)}", body: params)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# DELETE /audiences/:id
|
|
27
|
+
def delete(audience_id)
|
|
28
|
+
Client.request(:delete, "/audiences/#{Client.path_escape(audience_id)}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Import contacts from a link-shared Google Sheet; header columns become
|
|
32
|
+
# contact properties and rows land in a fresh segment.
|
|
33
|
+
# POST /audiences/:id/contacts/import-sheet — params: { url:, segment_name: }
|
|
34
|
+
def import_sheet(audience_id, params)
|
|
35
|
+
Client.request(:post, "/audiences/#{Client.path_escape(audience_id)}/contacts/import-sheet", body: params)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailblastr
|
|
4
|
+
# Automations are DOMAIN-FIRST: `domain` is required on create, and only
|
|
5
|
+
# Events.send calls naming the same domain trigger them.
|
|
6
|
+
module Automations
|
|
7
|
+
class << self
|
|
8
|
+
# POST /automations
|
|
9
|
+
# Mailblastr::Automations.create({ name: "Welcome series", domain: "yourdomain.com",
|
|
10
|
+
# trigger: "contact.created" })
|
|
11
|
+
def create(params)
|
|
12
|
+
Client.require_domain!(params, "Automations.create")
|
|
13
|
+
Client.request(:post, "/automations", body: params)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# GET /automations/:id
|
|
17
|
+
def get(automation_id)
|
|
18
|
+
Client.request(:get, "/automations/#{Client.path_escape(automation_id)}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# GET /automations
|
|
22
|
+
def list(params = {})
|
|
23
|
+
Client.request(:get, "/automations", query: Client.pagination(params))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# PATCH /automations/:id — params: { name:, status: "enabled"|"disabled", ... }
|
|
27
|
+
def update(automation_id, params)
|
|
28
|
+
Client.request(:patch, "/automations/#{Client.path_escape(automation_id)}", body: params)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Append a step. POST /automations/:id/steps — params: { type:, config:, key: }
|
|
32
|
+
def add_step(automation_id, params)
|
|
33
|
+
Client.request(:post, "/automations/#{Client.path_escape(automation_id)}/steps", body: params)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Delete a step. DELETE /automations/:id/steps/:step_id
|
|
37
|
+
def delete_step(automation_id, step_id)
|
|
38
|
+
Client.request(:delete, "/automations/#{Client.path_escape(automation_id)}/steps/#{Client.path_escape(step_id)}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# List an automation's runs. GET /automations/:id/runs
|
|
42
|
+
def runs(automation_id, params = {})
|
|
43
|
+
Client.request(:get, "/automations/#{Client.path_escape(automation_id)}/runs", query: Client.pagination(params))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Retrieve a single run with its step trace. GET /automations/:id/runs/:run_id
|
|
47
|
+
def get_run(automation_id, run_id)
|
|
48
|
+
Client.request(:get, "/automations/#{Client.path_escape(automation_id)}/runs/#{Client.path_escape(run_id)}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Stop an automation — no new runs; in-progress runs finish. POST /automations/:id/stop
|
|
52
|
+
def stop(automation_id)
|
|
53
|
+
Client.request(:post, "/automations/#{Client.path_escape(automation_id)}/stop")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# DELETE /automations/:id
|
|
57
|
+
def delete(automation_id)
|
|
58
|
+
Client.request(:delete, "/automations/#{Client.path_escape(automation_id)}")
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailblastr
|
|
4
|
+
# Campaigns are DOMAIN-FIRST: `domain` (required on create) picks the
|
|
5
|
+
# contact pool the campaign targets; `from` may be any verified domain.
|
|
6
|
+
module Campaigns
|
|
7
|
+
class << self
|
|
8
|
+
# POST /campaigns
|
|
9
|
+
# Mailblastr::Campaigns.create({ domain: "yourdomain.com", from: "you@yourdomain.com",
|
|
10
|
+
# subject: "Hello", html: "<p>Hi</p>", segment_id: "seg_1" })
|
|
11
|
+
def create(params)
|
|
12
|
+
Client.require_domain!(params, "Campaigns.create")
|
|
13
|
+
Client.request(:post, "/campaigns", body: params)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# GET /campaigns/:id
|
|
17
|
+
def get(campaign_id)
|
|
18
|
+
Client.request(:get, "/campaigns/#{Client.path_escape(campaign_id)}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# GET /campaigns
|
|
22
|
+
def list(params = {})
|
|
23
|
+
Client.request(:get, "/campaigns", query: Client.pagination(params))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# PATCH /campaigns/:id
|
|
27
|
+
def update(campaign_id, params)
|
|
28
|
+
Client.request(:patch, "/campaigns/#{Client.path_escape(campaign_id)}", body: params)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Send now, or schedule with { scheduled_at: "..." }. POST /campaigns/:id/send
|
|
32
|
+
def send(campaign_id, params = {})
|
|
33
|
+
Client.request(:post, "/campaigns/#{Client.path_escape(campaign_id)}/send", body: params)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Cancel a scheduled campaign (returns it to draft). POST /campaigns/:id/cancel
|
|
37
|
+
def cancel(campaign_id)
|
|
38
|
+
Client.request(:post, "/campaigns/#{Client.path_escape(campaign_id)}/cancel")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Per-campaign analytics (counts, engagement rates, top links). GET /campaigns/:id/stats
|
|
42
|
+
def stats(campaign_id)
|
|
43
|
+
Client.request(:get, "/campaigns/#{Client.path_escape(campaign_id)}/stats")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# A/B winner evaluation for an A/B campaign. GET /campaigns/:id/ab
|
|
47
|
+
def ab(campaign_id)
|
|
48
|
+
Client.request(:get, "/campaigns/#{Client.path_escape(campaign_id)}/ab")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# DELETE /campaigns/:id
|
|
52
|
+
def delete(campaign_id)
|
|
53
|
+
Client.request(:delete, "/campaigns/#{Client.path_escape(campaign_id)}")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|