anypost 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.
- checksums.yaml +4 -4
- data/README.md +27 -54
- data/lib/anypost/resources/events.rb +5 -1
- data/lib/anypost/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 626219a093ae533c0f15851744e4a6eb92a42e29f793c70ac6b219c64f74f5c2
|
|
4
|
+
data.tar.gz: 840398e4b95ff4e1bbdb6c1d5b719865304c9f0625f7e6c8f5de5b19cafbf45c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: caab7720e980e41910338662984bc706b2d731b01d3ebf4938f3db65c534b2b6f3ddd67efb014439030d71e4092763a05557477f3a6dffd0a0b615652e2219d7
|
|
7
|
+
data.tar.gz: db2311ea2c576f98d120fb5e5f07c6914b3e4ee0c92a3fe64b94a7e294418a19b95529576070531f7ffb03a7f2393982a1581014ea0bcce34f6120c97da3b84f
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@ The official Ruby gem for the [Anypost](https://anypost.com) email API.
|
|
|
4
4
|
|
|
5
5
|
Requires Ruby 3.2+. Built on [Faraday](https://github.com/lostisland/faraday).
|
|
6
6
|
|
|
7
|
+
This README covers the SDK itself: installation, idioms, and configuration. For platform concepts and the full field-level API reference, see the [Anypost documentation](https://anypost.com/docs).
|
|
8
|
+
|
|
7
9
|
## Install
|
|
8
10
|
|
|
9
11
|
```bash
|
|
@@ -24,10 +26,10 @@ require "anypost"
|
|
|
24
26
|
client = Anypost::Client.new("ap_your_api_key")
|
|
25
27
|
|
|
26
28
|
email = client.email.send(
|
|
27
|
-
from: "
|
|
28
|
-
to: ["
|
|
29
|
-
subject: "
|
|
30
|
-
html: "<p>
|
|
29
|
+
from: "YourCo <you@yourdomain.com>",
|
|
30
|
+
to: ["you@example.com"],
|
|
31
|
+
subject: "Welcome to Anypost",
|
|
32
|
+
html: "<p>Hello, inbox!</p>"
|
|
31
33
|
)
|
|
32
34
|
|
|
33
35
|
puts email.id
|
|
@@ -49,7 +51,7 @@ One of `text`, `html`, or `template_id` is required. All recipients in `to`, `cc
|
|
|
49
51
|
|
|
50
52
|
```ruby
|
|
51
53
|
client.email.send(
|
|
52
|
-
from: "
|
|
54
|
+
from: "YourCo <you@yourdomain.com>",
|
|
53
55
|
to: ["a@example.com", "b@example.com"],
|
|
54
56
|
cc: ["team@example.com"],
|
|
55
57
|
reply_to: "support@yourdomain.com",
|
|
@@ -60,11 +62,11 @@ client.email.send(
|
|
|
60
62
|
)
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
Attachment `content` is the raw file bytes
|
|
65
|
+
Attachment `content` is the raw file bytes: pass what `File.binread` returns and the SDK base64-encodes it. Do not pre-encode it. The request body is capped at 5 MB.
|
|
64
66
|
|
|
65
67
|
```ruby
|
|
66
68
|
client.email.send(
|
|
67
|
-
from: "you@yourdomain.com",
|
|
69
|
+
from: "YourCo <you@yourdomain.com>",
|
|
68
70
|
to: ["someone@example.com"],
|
|
69
71
|
subject: "Your report",
|
|
70
72
|
text: "Attached.",
|
|
@@ -78,20 +80,22 @@ Send with a published template and per-recipient variables:
|
|
|
78
80
|
|
|
79
81
|
```ruby
|
|
80
82
|
client.email.send(
|
|
81
|
-
from: "you@yourdomain.com",
|
|
83
|
+
from: "YourCo <you@yourdomain.com>",
|
|
82
84
|
to: ["someone@example.com"],
|
|
83
85
|
template_id: "template_018f2c5e-3a40-7a91-9c25-3a0b1d5e6f78",
|
|
84
86
|
variables: {name: "Ada", plan: "pro"}
|
|
85
87
|
)
|
|
86
88
|
```
|
|
87
89
|
|
|
90
|
+
See the [send reference](https://anypost.com/docs/reference/emails) for the complete field list.
|
|
91
|
+
|
|
88
92
|
## Batch
|
|
89
93
|
|
|
90
94
|
Send 1 to 100 independent messages in one request. `defaults` fills any field an entry omits.
|
|
91
95
|
|
|
92
96
|
```ruby
|
|
93
97
|
result = client.email.send_batch(
|
|
94
|
-
defaults: {from: "you@yourdomain.com"},
|
|
98
|
+
defaults: {from: "YourCo <you@yourdomain.com>"},
|
|
95
99
|
emails: [
|
|
96
100
|
{to: ["a@example.com"], subject: "Hi A", text: "..."},
|
|
97
101
|
{to: ["b@example.com"], subject: "Hi B", text: "..."}
|
|
@@ -115,7 +119,7 @@ end
|
|
|
115
119
|
|
|
116
120
|
## Domains
|
|
117
121
|
|
|
118
|
-
Manage sending domains under `client.domains`. Add a domain, publish the
|
|
122
|
+
Manage sending domains under `client.domains`. Add a domain, publish the DNS records it returns, then verify.
|
|
119
123
|
|
|
120
124
|
```ruby
|
|
121
125
|
domain = client.domains.create(name: "example.com")
|
|
@@ -123,21 +127,13 @@ domain = client.domains.create(name: "example.com")
|
|
|
123
127
|
domain.dns_records.each do |record|
|
|
124
128
|
puts "#{record.type} #{record.name} -> #{record.value}"
|
|
125
129
|
end
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
`verify` always returns the current domain — a still-`pending` domain does not raise. Read `status` and `verification_failure`, and poll while DNS propagates.
|
|
129
130
|
|
|
130
|
-
```ruby
|
|
131
131
|
checked = client.domains.verify(domain.id)
|
|
132
|
+
# verify returns the current domain even while pending; it does not raise
|
|
132
133
|
puts checked.verification_failure.code unless checked.status == "verified"
|
|
133
134
|
```
|
|
134
135
|
|
|
135
|
-
`get`, `update` (tracking config only), and `delete` round out the resource
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
client.domains.update(domain.id, tracking: {opens_enabled: true, clicks_enabled: true, subdomain: "track"})
|
|
139
|
-
client.domains.delete(domain.id)
|
|
140
|
-
```
|
|
136
|
+
`get`, `update` (tracking config only), and `delete` round out the resource. See [Domains](https://anypost.com/docs/reference/domains) for the verification lifecycle and field reference.
|
|
141
137
|
|
|
142
138
|
## API keys
|
|
143
139
|
|
|
@@ -150,12 +146,9 @@ created = client.api_keys.create(
|
|
|
150
146
|
allowed_domains: ["example.com"]
|
|
151
147
|
)
|
|
152
148
|
puts created.key # store now; never retrievable again
|
|
153
|
-
|
|
154
|
-
client.api_keys.update(created.id, name: "Production server", permissions: "full")
|
|
155
|
-
client.api_keys.delete(created.id)
|
|
156
149
|
```
|
|
157
150
|
|
|
158
|
-
`get` returns metadata only
|
|
151
|
+
`get` returns metadata only (`key_prefix`, never the secret); `update` and `delete` round out the resource. See [API keys](https://anypost.com/docs/reference/api-keys) for the permission model and cache propagation.
|
|
159
152
|
|
|
160
153
|
## Templates
|
|
161
154
|
|
|
@@ -168,30 +161,22 @@ template = client.templates.create(
|
|
|
168
161
|
html: "<h1>Welcome, {{ name }}</h1>"
|
|
169
162
|
)
|
|
170
163
|
|
|
171
|
-
client.templates.update_draft(template.id, subject: "Welcome to Acme", html: "<h1>Welcome, {{ name }}</h1>")
|
|
172
164
|
client.templates.publish(template.id)
|
|
173
165
|
```
|
|
174
166
|
|
|
175
|
-
`kind`
|
|
167
|
+
`kind` (`html` or `markdown`) is immutable once set; the plain-text body is always derived server-side. `get_draft`, `update_draft`, `delete_draft`, `duplicate`, `get`, `update` (name only), and `delete` round out the resource. Send a published template with `template_id` (see [Sending](#sending)). See [Templates](https://anypost.com/docs/reference/templates) for the full model.
|
|
176
168
|
|
|
177
169
|
## Suppressions
|
|
178
170
|
|
|
179
|
-
A suppression blocks sends to an address, scoped to a `topic`. The wildcard `*` blocks every topic; a
|
|
171
|
+
A suppression blocks sends to an address, scoped to a `topic`. The wildcard `*` blocks every topic; a named topic (e.g. `marketing`) leaves transactional traffic untouched.
|
|
180
172
|
|
|
181
173
|
```ruby
|
|
182
174
|
client.suppressions.create(email: "alice@example.com", topic: "marketing", note: "Customer requested removal")
|
|
183
175
|
|
|
184
|
-
row = client.suppressions.get("alice@example.com", "*")
|
|
185
176
|
client.suppressions.delete("alice@example.com", "marketing")
|
|
186
177
|
```
|
|
187
178
|
|
|
188
|
-
`list`
|
|
189
|
-
|
|
190
|
-
```ruby
|
|
191
|
-
client.suppressions.list(reason: "complaint").each do |s|
|
|
192
|
-
puts "#{s.email} #{s.topic} #{s.suppressed_at}"
|
|
193
|
-
end
|
|
194
|
-
```
|
|
179
|
+
`get`, `list` (with `email_contains`, `topic`, `reason`, and `origin` filters), `list_for_email`, and `delete_for_email` round out the resource. See [Suppressions](https://anypost.com/docs/reference/suppressions) for scoping and the automatic-suppression rules for bounces and complaints.
|
|
195
180
|
|
|
196
181
|
## Webhooks
|
|
197
182
|
|
|
@@ -206,18 +191,11 @@ webhook = client.webhooks.create(
|
|
|
206
191
|
puts webhook.signing_secret # store now; never retrievable again
|
|
207
192
|
```
|
|
208
193
|
|
|
209
|
-
`update
|
|
210
|
-
|
|
211
|
-
```ruby
|
|
212
|
-
result = client.webhooks.test(webhook.id)
|
|
213
|
-
puts "#{result.status_code} #{result.error}" unless result.delivered
|
|
214
|
-
|
|
215
|
-
rotated = client.webhooks.rotate_secret(webhook.id)
|
|
216
|
-
```
|
|
194
|
+
`update`, `test`, `rotate_secret`, `get`, `list`, and `delete` round out the resource. See [Webhooks](https://anypost.com/docs/reference/webhooks) for the event catalog, status transitions, and the secret-rotation grace window.
|
|
217
195
|
|
|
218
196
|
### Verifying deliveries
|
|
219
197
|
|
|
220
|
-
`Anypost::WebhookSignature.verify` is a module method
|
|
198
|
+
`Anypost::WebhookSignature.verify` is a module method. It needs the signing secret, not an API key, so call it in your handler without a client. Pass the **raw** request body (the exact bytes, before JSON parsing), the `Anypost-Signature` header, and the secret. It returns on success and raises `Anypost::WebhookVerificationError` otherwise. `Anypost::WebhookSignature.unwrap` does the same and returns the parsed delivery as a `Response`.
|
|
221
199
|
|
|
222
200
|
```ruby
|
|
223
201
|
begin
|
|
@@ -231,7 +209,7 @@ rescue Anypost::WebhookVerificationError => e
|
|
|
231
209
|
end
|
|
232
210
|
```
|
|
233
211
|
|
|
234
|
-
Reach for `verify` when something else has already parsed the body. Keep the raw bytes for the verify step, then use your parsed object once it passes
|
|
212
|
+
Reach for `verify` when something else has already parsed the body. Keep the raw bytes for the verify step, then use your parsed object once it passes (a Rack-style handler):
|
|
235
213
|
|
|
236
214
|
```ruby
|
|
237
215
|
post "/anypost" do
|
|
@@ -247,11 +225,11 @@ post "/anypost" do
|
|
|
247
225
|
end
|
|
248
226
|
```
|
|
249
227
|
|
|
250
|
-
Deliveries older than five minutes are rejected by default to bound replay; pass `tolerance_seconds:` to widen, narrow, or disable (`0`) that check. During a secret rotation the header carries a `v1=` component per active secret, and a match on any one passes
|
|
228
|
+
Deliveries older than five minutes are rejected by default to bound replay; pass `tolerance_seconds:` to widen, narrow, or disable (`0`) that check. During a secret rotation the header carries a `v1=` component per active secret, and a match on any one passes, so deliveries keep verifying while you redeploy.
|
|
251
229
|
|
|
252
230
|
## Events
|
|
253
231
|
|
|
254
|
-
`client.events.list` pages the team's event stream, newest-first. The window defaults to the last 24 hours and is clamped to your plan's retention. Events are read-only and not addressable by id
|
|
232
|
+
`client.events.list` pages the team's event stream, newest-first. The window defaults to the last 24 hours and is clamped to your plan's retention. Events are read-only and not addressable by id, so there is no `get`.
|
|
255
233
|
|
|
256
234
|
```ruby
|
|
257
235
|
client.events.list(event_type: "email.bounced").each do |event|
|
|
@@ -259,16 +237,11 @@ client.events.list(event_type: "email.bounced").each do |event|
|
|
|
259
237
|
end
|
|
260
238
|
```
|
|
261
239
|
|
|
262
|
-
Filter by `start`, `end`, `event_type`, `recipient`, `email_id`, `message_id`, `domain`, `topic`, `campaign`, `template_id`,
|
|
263
|
-
|
|
264
|
-
```ruby
|
|
265
|
-
# Events tagged "onboarding" OR "welcome", that also bounced.
|
|
266
|
-
page = client.events.list(tags: ["onboarding", "welcome"], event_type: "email.bounced")
|
|
267
|
-
```
|
|
240
|
+
Filter by `start`, `end`, `event_type`, `recipient`, `email_id`, `message_id`, `domain`, `topic`, `campaign`, `template_id`, `ip_pool`, and `tags`, an array that matches an event carrying *any* of the given tags. Every other filter is exact-match. This is also how you backfill the gap after a webhook endpoint was disabled: page the events that occurred during the outage once it's healthy. See [Events](https://anypost.com/docs/reference/events) for the field reference.
|
|
268
241
|
|
|
269
242
|
## Pagination
|
|
270
243
|
|
|
271
|
-
List endpoints return a `Page`. Read one page directly, or iterate it to walk every page
|
|
244
|
+
List endpoints return a `Page`. Read one page directly, or iterate it to walk every page; the client fetches each one as needed.
|
|
272
245
|
|
|
273
246
|
```ruby
|
|
274
247
|
page = client.domains.list(limit: 50)
|
|
@@ -9,7 +9,7 @@ module Anypost
|
|
|
9
9
|
# The window defaults to the last 24 hours and is clamped to the plan's
|
|
10
10
|
# retention. Filter with `start`, `end`, `event_type`, `recipient`,
|
|
11
11
|
# `email_id`, `message_id`, `domain`, `topic`, `campaign`, `template_id`,
|
|
12
|
-
# and `tags` (an array, matched with hasAny).
|
|
12
|
+
# `ip_pool`, and `tags` (an array, matched with hasAny).
|
|
13
13
|
def list(params = {})
|
|
14
14
|
tags = params[:tags]
|
|
15
15
|
paginate("/events", {
|
|
@@ -25,6 +25,10 @@ module Anypost
|
|
|
25
25
|
topic: params[:topic],
|
|
26
26
|
campaign: params[:campaign],
|
|
27
27
|
template_id: params[:template_id],
|
|
28
|
+
# Exact match against the [a-z0-9]([a-z0-9-]*[a-z0-9])? pool-name
|
|
29
|
+
# shape. A value outside it returns an empty list rather than being
|
|
30
|
+
# ignored, so a typo cannot silently widen the answer to "all pools".
|
|
31
|
+
ip_pool: params[:ip_pool],
|
|
28
32
|
# Sent comma-separated (tags=a,b); the API matches with hasAny.
|
|
29
33
|
tags: (tags.is_a?(Array) && !tags.empty?) ? tags.join(",") : nil
|
|
30
34
|
})
|
data/lib/anypost/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: anypost
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anypost
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|