unione 1.1.3 → 1.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 966428a227c0701201a9efb14fb683d891df950a2cc1e446fda99113eca0b2b6
4
- data.tar.gz: 9664b54ea7723c2172e47fdd062468610505fcc24f040d5fae1e5f8e1e364697
3
+ metadata.gz: 905c25d5be2c9eb7f499e528962a173bd5852d40ed4239d07d9484d830227900
4
+ data.tar.gz: 2b0bc8aa4a2149a3e663ed7da251174ceeb180671e3f90ee932814ad791cbfc9
5
5
  SHA512:
6
- metadata.gz: 8f1a2f280c5d7066e0b033c8ab86de1e647540e3a73c9232b189c83e2d8798cb3ad55134b2c276ed3898dfa5e956592cb8ac233c660631dab7452881bc9afed3
7
- data.tar.gz: f36d07366d6b07d739d0f91268e2ef0a9e5dffe7f60bc99006aade3a926983b52f378d3a44e3fc23f43911d47e1b673094d0f378ca77ab260cb19fdb154b33fa
6
+ metadata.gz: a0916665cc508953defe333e3517a0b72b0000a6dde8ffce70753e1cf01a5b9772fd78a26a118c188481f55c41bc40601345c86574399afab16a296e96323c06
7
+ data.tar.gz: 92a7e623a9c261c4577cbeb3cbce612dfa21742bdad0a48c30628f844f2f4d0fb5bacf0755d9eaec37d5c6588da303398b60cfb07c412276416e04a32d20f00f
data/AGENTS.md CHANGED
@@ -11,9 +11,9 @@ UniOne Ruby SDK — client for the UniOne transactional email Web API (Web API 1
11
11
  gem build *.gemspec
12
12
  ```
13
13
 
14
- - **Source label.** Every request carries the query parameter `platform=ruby-sdk.1.1.3`, added in one place, the shared request client (`ApiClient`); a package built on this SDK puts its own label in front: `<package>.<version>,ruby-sdk.1.1.3`.
14
+ - **Source label.** Every request carries the query parameter `platform=ruby-sdk.1.2.3`, added in one place, the shared request client (`ApiClient`); a package built on this SDK puts its own label in front: `<package>.<version>,ruby-sdk.1.2.3`.
15
15
  - **Tests.** Before a release the generator builds and lints the package and runs every example of docs/ and of this file against a mock of the API: no real API key, no real sends.
16
- - **Release.** The version (1.1.3 in the gemspec) and the tag `v1.1.3` come from the generator; README.md states the Web API version the release was generated from.
16
+ - **Release.** The version (1.2.3 in the gemspec) and the tag `v1.2.3` come from the generator; README.md states the Web API version the release was generated from.
17
17
  - **Secrets.** Never commit API keys; the examples read `UNIONE_API_KEY` from the environment.
18
18
 
19
19
  ## Integrating UniOne with this SDK
@@ -38,7 +38,7 @@ Rules that generated code must follow:
38
38
  - Base URL `https://api.unione.io/en/transactional/api/v1` (an account registered on another host uses that one: `eu1.unione.io`, `us1.unione.io`); every method is POST with a JSON body; auth header `X-API-KEY`. The SDK does all of it.
39
39
  - First email without DNS: `domain/get-sandbox-domain` → `confirmed-email/create` (the user clicks the link within 48 hours) → `email/send` from `noreply@<sandbox-domain>` to the confirmed address. Account API key only; up to 10 confirmed addresses, 100 emails a day; the domain expires after 31 days.
40
40
  - Never ship a sandbox domain as the production sender (it is not in `domain/list`); read `from_email` from config. Production needs a verified domain: `domain/get-dns-records` → DNS records → `domain/validate-verification-record`, `domain/validate-dkim` → `domain/list`.
41
- - Set `message.idempotence_key` on retryable sends; retry network errors, `429` and `5xx` with backoff, not other errors unchanged. Up to 500 recipients and 10 MB per request, each attachment up to 7 MB; always `body.html` or `body.plaintext`.
41
+ - Set `message.idempotence_key` on retryable sends; retry network errors, `429` and `5xx` with backoff, not other errors unchanged. Up to 500 recipients and 10 MB per request with the attachments in base64 (a third larger than the files); always `body.html` or `body.plaintext`.
42
42
  - Delivery events via webhook (`webhook/set`) with a fast, idempotent HTTPS handler, not polling; check the `auth` field of every notification: the MD5 of the request body with the `auth` value replaced by the API key.
43
43
  - Keep the default unsubscribe and tracking behaviour; changes go through UniOne support.
44
44
  - Do not invent methods or fields. Check [docs/](docs/) of this package, the [API reference](https://docs.unione.io/en/web-api-ref) or [llms.txt](https://docs.unione.io/llms.txt).
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
- # UniOne Ruby SDK 1.1.3
1
+ # UniOne Ruby SDK
2
2
 
3
- Web API 1.90
3
+ [![Ruby](https://img.shields.io/gem/v/unione)](https://rubygems.org/gems/unione) [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
4
4
 
5
5
  Official Ruby library for the [UniOne](https://unione.io) transactional email API.
6
6
 
7
- Quickstart guide: [Send emails with Ruby](https://docs.unione.io/en/send-with-ruby) · Docs for LLMs: [llms.txt](https://docs.unione.io/llms.txt) · [API reference](https://docs.unione.io/en/web-api-ref)
7
+ [API reference](https://docs.unione.io/en/web-api-ref) · Docs for LLMs: [llms.txt](https://docs.unione.io/llms.txt)
8
+
9
+ > **On Ruby on Rails?** Install [`unione-rails`](https://github.com/unione-repo/unione-rails) instead: this SDK plus configuration, a mailer and a webhook route.
8
10
 
9
11
  ## Install
10
12
 
@@ -14,106 +16,214 @@ gem install unione
14
16
 
15
17
  Requirements: Ruby 2.7+. No third-party dependencies.
16
18
 
17
- ## Get an API key
18
-
19
- 1. Sign up at [cp.unione.io](https://cp.unione.io/en/user/registration/).
20
- 2. Copy your API key from [Settings → Security → API](https://cp.unione.io/en/settings/security/api).
21
- 3. Put it in an environment variable:
19
+ Get an API key in the [control panel](https://cp.unione.io/en/settings/security/api) and keep it in the environment, never in code:
22
20
 
23
21
  ```bash
24
22
  export UNIONE_API_KEY="your-api-key"
25
23
  ```
26
24
 
27
- > **Server-side only.** Use this SDK in backend code: API routes, server functions, workers.
28
- > Never ship the API key to a browser or a mobile app, and never commit it.
25
+ ## Send an email
26
+
27
+ ```ruby
28
+ require 'unione'
29
+
30
+ Body = UniOne::Common::Model::BodyObject
31
+ Email = UniOne::Api::Email::Model
32
+ Recipient = UniOne::Api::Email::Model::MessageObjectRecipientsItem
33
+
34
+ client = UniOne.client(ENV.fetch('UNIONE_API_KEY'))
35
+
36
+ message = Email::MessageObject.new(
37
+ recipients: [Recipient.from_address('Alex <alex@example.com>')],
38
+ body: Body.html('<p>Hello, {{to_name}}! Your order has shipped.</p>'),
39
+ subject: 'Your order',
40
+ from_email: 'shop@example.com',
41
+ from_name: 'Shop'
42
+ )
43
+ response = client.email.send_email(Email::EmailSendRequest.new(message: message))
44
+ puts response.job_id
45
+ ```
46
+
47
+ The sender address must be on a domain verified in your account. No domain yet? Send from the [sandbox domain](#no-domain-yet-the-test-sandbox-domain) in three steps.
48
+
49
+ ## Contents
29
50
 
30
- ## Send your first email — no DNS setup
51
+ - [Recipes](#recipes): personalization · attachments · CC and BCC · scheduling · safe retries · errors · webhooks
52
+ - [No domain yet: the test sandbox domain](#no-domain-yet-the-test-sandbox-domain)
53
+ - [Going to production](#going-to-production)
54
+ - [Reference](#reference)
55
+ - [Help](#help)
31
56
 
32
- Every account can send test email right away from a **sandbox domain**: pre-verified, DKIM and SPF already set.
33
- It delivers only to addresses you confirm.
57
+ ## Recipes
34
58
 
35
- <!-- checked examples -->
59
+ Every recipe continues the example above: the client and its imports are there.
36
60
 
37
- **1. Get your sandbox domain** (`domain/get-sandbox-domain`). The call creates the domain on first use and returns the same one afterwards.
61
+ ### Personalize for each recipient
62
+
63
+ Up to 500 recipients per call, each gets a separate copy with its own substitutions. Metadata of a recipient comes back in its webhook events.
38
64
 
39
65
  ```ruby
40
- require 'unione'
66
+ message = Email::MessageObject.new(
67
+ recipients: [
68
+ Recipient.from_address('Alex <alex@example.com>', { 'order' => '1001' }).with_metadata({ 'user_id' => '42' }),
69
+ Recipient.from_address('Maria <maria@example.com>', { 'order' => '1002' }).with_metadata({ 'user_id' => '43' })
70
+ ],
71
+ body: Body.html('<p>{{to_name}}, order #{{order}} has shipped.</p>'),
72
+ subject: 'Order #{{order}}',
73
+ from_email: 'shop@example.com'
74
+ )
75
+ client.email.send_email(Email::EmailSendRequest.new(message: message))
76
+ ```
41
77
 
42
- client = UniOne.client(ENV.fetch('UNIONE_API_KEY'))
78
+ ### Attach a file
79
+
80
+ ```ruby
81
+ Common = UniOne::Common::Model
43
82
 
44
- response = client.domain.get_sandbox_domain
45
- p response.domain
83
+ message = Email::MessageObject.new(
84
+ recipients: [Recipient.from_address('alex@example.com')],
85
+ body: Body.html('<p>Your invoice is attached.</p>'),
86
+ subject: 'Invoice',
87
+ from_email: 'shop@example.com',
88
+ attachments: [
89
+ Common::AttachmentsArrayItem.new(
90
+ type: 'application/pdf',
91
+ name: 'invoice.pdf',
92
+ content: [File.binread('invoice.pdf')].pack('m0')
93
+ )
94
+ ]
95
+ )
96
+ client.email.send_email(Email::EmailSendRequest.new(message: message))
46
97
  ```
47
98
 
48
- **2. Confirm your inbox** (`confirmed-email/create`). UniOne sends a confirmation email; click the link in it within 48 hours.
99
+ ### CC and BCC
100
+
101
+ Every address goes to `recipients` (each gets a copy); the visible ones also go to the headers. `audit@example.com` is in neither header: a BCC. The rules and limits: [CC and BCC](https://docs.unione.io/en/cc-and-bcc).
49
102
 
50
103
  ```ruby
51
- require 'unione'
104
+ Common = UniOne::Common::Model
52
105
 
53
- ConfirmedEmail = UniOne::Api::ConfirmedEmail::Model
106
+ message = Email::MessageObject.new(
107
+ recipients: [
108
+ Recipient.from_address('alex@example.com'),
109
+ Recipient.from_address('boss@example.com'),
110
+ Recipient.from_address('audit@example.com')
111
+ ],
112
+ body: Body.html('<p>The report is attached.</p>'),
113
+ subject: 'Weekly report',
114
+ from_email: 'shop@example.com',
115
+ headers: Common::HeadersObject.new(extra: { 'To' => 'alex@example.com', 'CC' => 'boss@example.com' })
116
+ )
117
+ client.email.send_email(Email::EmailSendRequest.new(message: message))
118
+ ```
54
119
 
55
- client = UniOne.client(ENV.fetch('UNIONE_API_KEY'))
120
+ ### Schedule
121
+
122
+ `send_at` is UTC, up to 24 hours ahead.
56
123
 
57
- response = client.confirmed_email.create(ConfirmedEmail::ConfirmedEmailRequest.new(
58
- email: 'you@example.com'
59
- ))
60
- p response.confirmed_email
124
+ ```ruby
125
+ send_at = (Time.now.utc + 3600).strftime('%Y-%m-%d %H:%M:%S') # UTC, "YYYY-MM-DD hh:mm:ss"
126
+ message = Email::MessageObject.new(
127
+ recipients: [Recipient.from_address('alex@example.com')],
128
+ body: Body.text('See you tomorrow!'),
129
+ subject: 'Reminder',
130
+ from_email: 'shop@example.com',
131
+ options: Email::MessageObjectOptions.new(send_at: send_at)
132
+ )
133
+ client.email.send_email(Email::EmailSendRequest.new(message: message))
61
134
  ```
62
135
 
63
- Check the status with `confirmed-email/list` — your address should show `"status": "confirmed"`.
136
+ ### Retry safely
64
137
 
65
- **3. Send the email** (`email/send`) from any address on the sandbox domain to the confirmed address.
138
+ A repeat with the same `idempotence_key` within a minute is not sent twice: retry network errors, timeouts, 429 and 5xx with it.
66
139
 
67
140
  ```ruby
68
- require 'unione'
141
+ message = Email::MessageObject.new(
142
+ recipients: [Recipient.from_address('alex@example.com')],
143
+ body: Body.html('<p>Use this link to reset your password.</p>'),
144
+ subject: 'Reset your password',
145
+ from_email: 'shop@example.com',
146
+ idempotence_key: 'password-reset-8f3a'
147
+ )
148
+ client.email.send_email(Email::EmailSendRequest.new(message: message))
149
+ ```
69
150
 
70
- Common = UniOne::Common::Model
71
- Email = UniOne::Api::Email::Model
151
+ ### Handle errors
72
152
 
73
- client = UniOne.client(ENV.fetch('UNIONE_API_KEY'))
153
+ Recipients the API drops (unsubscribed, invalid, suppressed) are not an error: they are in `failed_emails` of the response, the rest still get the message.
74
154
 
75
- response = client.email.send_email(Email::EmailSendRequest.new(
76
- message: Email::MessageObject.new(
77
- recipients: [
78
- Email::MessageObjectRecipientsItem.new(
79
- email: 'you@example.com'
80
- )
81
- ],
82
- body: Common::BodyObject.new(
83
- html: '<p>It works!</p>'
84
- ),
85
- subject: 'Hello from UniOne',
86
- from_email: 'noreply@<sandbox-domain>'
87
- )
88
- ))
89
- p response.job_id
155
+ ```ruby
156
+ begin
157
+ client.email.send_email(request)
158
+ rescue UniOne::Client::ApiError => e
159
+ # HTTP status (0: network error or timeout), API error code, message
160
+ puts "#{e.http_status} #{e.error_code}: #{e.message}"
161
+ end
90
162
  ```
91
163
 
92
- <!-- /checked examples -->
164
+ ### Receive delivery events
93
165
 
94
- ### What you get back
166
+ Register your HTTPS endpoint once (`webhook/set`). Then check the signature of every notification (`auth`: MD5 of the raw body with the `auth` value replaced by your API key; a wrong one gets 401) and process the events.
95
167
 
96
- ```json
97
- {
98
- "status": "success",
99
- "job_id": "1ZymBc-00041N-9X",
100
- "emails": [
101
- "you@example.com"
102
- ]
103
- }
168
+ ```ruby
169
+ require 'digest'
170
+
171
+ # raw: the body of the POST request as received; returns the HTTP status to answer
172
+ def handle_webhook(raw)
173
+ callback = UniOne::Api::Webhook::WebhookApi.parse_callback(raw)
174
+ auth = callback.auth.to_s
175
+ # auth: MD5 of the raw body with the auth value replaced by your API key
176
+ return 401 unless Digest::MD5.hexdigest(raw.gsub(auth, ENV.fetch('UNIONE_API_KEY'))) == auth
177
+
178
+ (callback.events_by_user || []).each do |user|
179
+ (user.events || []).each do |event|
180
+ # email, status (delivered, opened, hard_bounced, spam, ...), job_id, metadata
181
+ end
182
+ end
183
+ 200
184
+ end
104
185
  ```
105
186
 
106
- On error the API returns `status: "error"`, a `message` and a numeric `code`:
187
+ > **Answer 200 within 3 seconds** and process the events in the background (a queue): otherwise UniOne repeats the request every 10 minutes for 24 hours (`retry_count` grows), and a handler that failed every request for at least 10 events of a day is turned inactive. The handler must take at least **5 parallel requests of up to 100 events each**; deduplicate, a repeat delivers the same events again.
188
+
189
+ ## No domain yet: the test sandbox domain
190
+
191
+ Until your domain is verified, send from the **sandbox domain**: a test domain every account has, already verified, with DKIM and SPF set. It delivers only to addresses you confirm (the confirmation link comes by email, valid 48 hours), up to 100 emails a day. The sandbox domain is deleted 31 days after it was created, but you can get it again: the next `domain/get-sandbox-domain` call creates a new one (with a new name, so read it from the call, do not hardcode it).
107
192
 
108
- ```json
109
- {
110
- "status": "error",
111
- "code": 102,
112
- "message": "Error ID:83274F98-C7D5-11F1-B221-6214FF8707B1. Can not decode key wrongkey"
113
- }
193
+ ```ruby
194
+ ConfirmedEmail = UniOne::Api::ConfirmedEmail::Model
195
+
196
+ # 1. Your sandbox domain: created on the first call, the same one afterwards
197
+ domain = client.domain.get_sandbox_domain.domain
198
+
199
+ # 2. Confirm your inbox: UniOne sends a link, click it within 48 hours
200
+ client.confirmed_email.create(ConfirmedEmail::ConfirmedEmailRequest.new(email: 'you@example.com'))
201
+
202
+ # 3. Send from any address on the sandbox domain to the confirmed one
203
+ message = Email::MessageObject.new(
204
+ recipients: [Recipient.from_address('you@example.com')],
205
+ body: Body.html('<p>It works!</p>'),
206
+ subject: 'Hello from UniOne',
207
+ from_email: "noreply@#{domain}"
208
+ )
209
+ client.email.send_email(Email::EmailSendRequest.new(message: message))
114
210
  ```
115
211
 
116
- ### Same flow with curl
212
+ <details>
213
+ <summary>Sandbox limits</summary>
214
+
215
+ - Sends only to confirmed addresses: up to 10 per account.
216
+ - Up to 100 emails a day.
217
+ - The sandbox domain expires after 31 days; call `domain/get-sandbox-domain` again to get a new one.
218
+ - Works only with the account API key, not a project key.
219
+ - Not listed in `domain/list`; the other `domain/*` methods do not apply to it.
220
+
221
+ The sandbox is for development and testing. Do not use a sandbox domain as the sender in production.
222
+
223
+ </details>
224
+
225
+ <details>
226
+ <summary>The same with curl</summary>
117
227
 
118
228
  ```bash
119
229
  BASE=https://api.unione.io/en/transactional/api/v1
@@ -143,58 +253,51 @@ curl -s -X POST $BASE/email/send.json \
143
253
  }'
144
254
  ```
145
255
 
146
- ## Sandbox limits
256
+ </details>
147
257
 
148
- - Sends only to confirmed addresses: up to 10 per account.
149
- - Up to 100 emails a day.
150
- - The sandbox domain expires after 31 days; call `domain/get-sandbox-domain` again to get a new one.
151
- - Works only with the account API key, not a project key.
152
- - Not listed in `domain/list`; the other `domain/*` methods do not apply to it.
153
-
154
- The sandbox is for development and testing. Do not use a sandbox domain as the sender in production.
155
-
156
- ## Before production: verify your domain
157
-
158
- 1. `domain/get-dns-records` — returns the values for your DNS records.
159
- 2. Add these records at your DNS provider (the domain page of the control panel shows them too):
160
-
161
- | Type | Host | Value |
162
- |---|---|---|
163
- | TXT | `@` | the `verification-record` value (`unione-validate-hash=…`) |
164
- | TXT | `<selector>._domainkey` (the selector is on the domain page of the control panel, e.g. `us`) | `v=DKIM1; k=rsa; p=<dkim>` |
165
- | TXT | `@` | `v=spf1 include:spf.unione.io ~all` |
166
- | CNAME | `_dmarc` | the `dmarc` value |
258
+ ## Going to production
167
259
 
168
- 3. `domain/validate-verification-record`, then `domain/validate-dkim`.
169
- 4. `domain/list` — wait until the verification record is confirmed and DKIM is active.
170
- 5. Read `from_email` from your config, so switching from the sandbox needs no code change.
260
+ **1. Verify your sending domain.** `domain/get-dns-records` returns the verification record, the DKIM key and the DMARC target; add the records at your DNS provider, then `domain/validate-verification-record` and `domain/validate-dkim`.
171
261
 
172
- ## Sending reliably
262
+ | Type | Host | Value | Where it comes from |
263
+ |---|---|---|---|
264
+ | TXT | `@` | `unione-validate-hash=…` | `verification-record` of the response, as is |
265
+ | TXT | `<selector>._domainkey` (the selector is on the domain page of the control panel, e.g. `us`) | `v=DKIM1; k=rsa; p=<dkim>` | `dkim` of the response after `v=DKIM1; k=rsa; p=` |
266
+ | TXT | `@` | `v=spf1 include:spf.unione.io ~all` | not in the response; add the include to your SPF record if you have one |
267
+ | CNAME | `_dmarc` | `<domain>.dmarc.…` | `dmarc` of the response (UniOne publishes the DMARC policy); keep your own DMARC TXT record instead if the domain has one |
173
268
 
174
- - **Retries:** set `message.idempotence_key` on sends that may be retried, so a repeated request does not send twice.
175
- - **Limits:** up to 500 recipients and 10 MB per request; each attachment up to 7 MB.
176
- - **Body:** always include `body.html` or `body.plaintext`.
177
- - **Delivery status:** register a webhook (`webhook/set`) with a fast, idempotent HTTPS handler instead of polling.
178
- - **Defaults:** keep the default unsubscribe footer and tracking; changing them requires UniOne support.
179
- - **Errors:** retry `429` and `5xx` with backoff; do not retry other errors unchanged.
269
+ **2. Make sending reliable.**
180
270
 
181
- ## Links
271
+ | | |
272
+ |---|---|
273
+ | Retries | `429` and `5xx` with backoff and the same `idempotence_key`; fix other errors instead of retrying |
274
+ | Limits | 500 recipients and 10 MB per request, attachments included: base64 makes a file about a third larger |
275
+ | Body | always `html` or `plaintext` (a template can supply it) |
276
+ | Delivery status | a webhook, not polling |
277
+ | Queue | send from a background job, not inside the web request |
278
+ | Config | `from_email` from config, so the sandbox → production switch needs no code change |
279
+ | Defaults | keep the unsubscribe footer and tracking; changing them needs UniOne support |
182
280
 
183
- - [Send emails with Ruby](https://docs.unione.io/en/send-with-ruby) and other [integration guides](https://docs.unione.io/en/integrations/)
184
- - [Web API reference](https://docs.unione.io/en/web-api-ref)
185
- - [llms.txt](https://docs.unione.io/llms.txt) for AI coding agents
186
- - [UniOne MCP server and agent setup](https://unione.io/en/agents)
187
- - [Support](https://cp.unione.io/en/support)
281
+ ## Reference
188
282
 
189
- ## For SDK contributors
283
+ ### Sections
190
284
 
191
- See [AGENTS.md](AGENTS.md): how this SDK is generated from the OpenAPI spec, built, tested and released.
192
-
193
- ---
194
-
195
- # SDK reference
285
+ | Section | Methods | Description |
286
+ |---|---|---|
287
+ | [Email](docs/Email.md) | `send_email`, `subscribe` | This group of methods is related with email sending. |
288
+ | [EmailValidation](docs/EmailValidation.md) | `single` | Email validation methods. As a result of the check, you’ll get detailed information on email validity. It will help eliminate nonexistent emails, decrease the number of complaints, clean up the subscribers list and eventually enhance your sender reputation score. For example, you may add validation to a subscription form on your site to let users know they’ve just entered an invalid address. |
289
+ | [ConfirmedEmail](docs/ConfirmedEmail.md) | `create`, `list`, `delete` | Manage email addresses confirmed for sandbox-domain sending. These methods are available only with a main account API key. |
290
+ | [Template](docs/Template.md) | `set`, `get`, `list`, `delete` | You have the option of storing a template on the UniOne side and then using it by id, which is highly recommended for sending repetitive messages or high volume traffic. UniOne supports two different [template engines](https://docs.unione.io/en/template-engines). |
291
+ | [Webhook](docs/Webhook.md) | `set`, `get`, `list`, `delete` | UniOne lets you recieve notifications about email status changes or spam blocks. You provide an URL and every time a watched event occurs we will notify you by submitting a request with JSON data to this URL. |
292
+ | [Suppression](docs/Suppression.md) | `set`, `get`, `list`, `delete` | Methods for managing suppression lists. An email can be added to suppression list because of unsubscription, bounce or complain. |
293
+ | [Domain](docs/Domain.md) | `get_dns_records`, `validate_verification_record`, `validate_dkim`, `list`, `get_sandbox_domain`, `delete` | You can use these methods to setup sender domains and check their statuses. They might be useful in case you want to automate adding domains for your own clients. |
294
+ | [EventDump](docs/EventDump.md) | `create`, `get`, `list`, `delete` | These methods are used to obtain a list of events for a given period of time. The data is stored for up to 45 days, depending on your tariff, i. e. you may fetch all event data starting from 00:00:00 31 days ago and up to current time. For custom tarifs with big sending volumes event dumps are available only for the last 7 days. |
295
+ | [Tag](docs/Tag.md) | `list`, `delete` | This family of methods is used to manage user-defined tags. Tags are used to categorize letters based on your own criteria, e. g. to create a distinction between marketing and transactional letters or group together all notifications for a particular order type. UniOne allows you to assign tags to all outgoing emails, and the tags you’ve set may then be used to filter and analyze delivery statistics. |
296
+ | [Project](docs/Project.md) | `create`, `update`, `list`, `delete` | Projects allow you to group several independent domains, webhooks, templates, and unsubscription lists together. It's very useful if you have your own clients and manage their data from a single UniOne account. Each project receives its own API key, and all API calls with that API key only deal with the data of that project. This way, you can assign a separate project for each client and be sure that they won't conflict; e.g. unsubscribing from one project won't affect another. |
297
+ | [System](docs/System.md) | `ping`, `info` | System operations. |
298
+ | [Obsolete](docs/Obsolete.md) | `unsubscribed_set`, `unsubscribed_check`, `unsubscribed_list` | Obsolete methods dealing with unsubscribes. We recommend using the more flexible [suppression](https://docs.unione.io/en/web-api-ref#suppression) methods instead. Note that you can't resubscribe an unsubscribed recipient using WebAPI, but you can send him an email with subscription link using email/subscribe method. |
196
299
 
197
- ## API host
300
+ ### API host
198
301
 
199
302
  The second argument selects the API host (datacenter) or a full base URL.
200
303
 
@@ -205,7 +308,7 @@ client = UniOne.client('YOUR_API_KEY', 'us1.unione.io')
205
308
  client = UniOne.client('YOUR_API_KEY', UniOne::Client::Configuration::HOST_US1)
206
309
  ```
207
310
 
208
- ## Custom HTTP client / headers
311
+ ### Custom HTTP client / headers
209
312
 
210
313
  `headers:` adds HTTP headers to every request. `transport:` replaces the HTTP layer: any object responding to `call(method, uri, headers, body, timeout)` that returns `[status, body]` (a lambda works too). Wrap the default `Client::NetHttpTransport` for retries or logging, or call Faraday / HTTParty in your own one; an exception of the transport becomes `ApiError`.
211
314
 
@@ -248,23 +351,13 @@ headers = UniOne::Common::Model::HeadersObject.new(extra: {
248
351
  })
249
352
  ```
250
353
 
251
- ## Error handling
354
+ ### Error handling
252
355
 
253
356
  Every method raises `ApiError` when the API returns an error; it carries the [API error code](https://docs.unione.io/en/web-api-ref#api-errors) and the HTTP status.
254
357
 
255
- ## Sections
358
+ ## Help
256
359
 
257
- | Section | Methods | Description |
258
- |---|---|---|
259
- | [Email](docs/Email.md) | `send_email`, `subscribe` | This group of methods is related with email sending. |
260
- | [EmailValidation](docs/EmailValidation.md) | `single` | Email validation methods. As a result of the check, you’ll get detailed information on email validity. It will help eliminate nonexistent emails, decrease the number of complaints, clean up the subscribers list and eventually enhance your sender reputation score. For example, you may add validation to a subscription form on your site to let users know they’ve just entered an invalid address. |
261
- | [ConfirmedEmail](docs/ConfirmedEmail.md) | `create`, `list`, `delete` | Manage email addresses confirmed for sandbox-domain sending. These methods are available only with a main account API key. |
262
- | [Template](docs/Template.md) | `set`, `get`, `list`, `delete` | You have the option of storing a template on the UniOne side and then using it by id, which is highly recommended for sending repetitive messages or high volume traffic. UniOne supports two different [template engines](https://docs.unione.io/en/template-engines). |
263
- | [Webhook](docs/Webhook.md) | `set`, `get`, `list`, `delete` | UniOne lets you recieve notifications about email status changes or spam blocks. You provide an URL and every time a watched event occurs we will notify you by submitting a request with JSON data to this URL. |
264
- | [Suppression](docs/Suppression.md) | `set`, `get`, `list`, `delete` | Methods for managing suppression lists. An email can be added to suppression list because of unsubscription, bounce or complain. |
265
- | [Domain](docs/Domain.md) | `get_dns_records`, `validate_verification_record`, `validate_dkim`, `list`, `get_sandbox_domain`, `delete` | You can use these methods to setup sender domains and check their statuses. They might be useful in case you want to automate adding domains for your own clients. |
266
- | [EventDump](docs/EventDump.md) | `create`, `get`, `list`, `delete` | These methods are used to obtain a list of events for a given period of time. The data is stored for up to 45 days, depending on your tariff, i. e. you may fetch all event data starting from 00:00:00 31 days ago and up to current time. For custom tarifs with big sending volumes event dumps are available only for the last 7 days. |
267
- | [Tag](docs/Tag.md) | `list`, `delete` | This family of methods is used to manage user-defined tags. Tags are used to categorize letters based on your own criteria, e. g. to create a distinction between marketing and transactional letters or group together all notifications for a particular order type. UniOne allows you to assign tags to all outgoing emails, and the tags you’ve set may then be used to filter and analyze delivery statistics. |
268
- | [Project](docs/Project.md) | `create`, `update`, `list`, `delete` | Projects allow you to group several independent domains, webhooks, templates, and unsubscription lists together. It's very useful if you have your own clients and manage their data from a single UniOne account. Each project receives its own API key, and all API calls with that API key only deal with the data of that project. This way, you can assign a separate project for each client and be sure that they won't conflict; e.g. unsubscribing from one project won't affect another. |
269
- | [System](docs/System.md) | `ping`, `info` | System operations. |
270
- | [Obsolete](docs/Obsolete.md) | `unsubscribed_set`, `unsubscribed_check`, `unsubscribed_list` | Obsolete methods dealing with unsubscribes. We recommend using the more flexible [suppression](https://docs.unione.io/en/web-api-ref#suppression) methods instead. Note that you can't resubscribe an unsubscribed recipient using WebAPI, but you can send him an email with subscription link using email/subscribe method. |
360
+ - [Integration guides](https://docs.unione.io/en/integrations)
361
+ - AI coding agents: [llms.txt](https://docs.unione.io/llms.txt), the [MCP server](https://unione.io/en/agents), skills: `npx skills add https://github.com/unione-repo/unione-skills.git`
362
+ - [Support](https://cp.unione.io/en/support) · [Issues](https://github.com/unione-repo/unione-ruby-sdk/issues)
363
+ - Contributing: [AGENTS.md](AGENTS.md) — the SDK is generated from the OpenAPI spec.
data/docs/Domain.md CHANGED
@@ -33,13 +33,16 @@ You should add then DNS records like this to all of the domains you want to conf
33
33
 
34
34
  `@ IN TXT "unione-validate-hash=483bb362ebdbeedd755cfb1d4d661"`
35
35
 
36
- `us._domainkey IN TXT "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo7"`
36
+ `us._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo7"`
37
37
 
38
38
  `@ IN TXT "v=spf1 include:spf.unione.io ~all"`
39
39
 
40
+ `_dmarc IN CNAME "example.com.dmarc.unione.io."`
41
+
40
42
  - First record value should contain "verification-record" field returned by get-dns-records method "as is".
41
- - Second record value should contain "dkim" field value after "p=". Mind the name of this record! "us._domainkey." prefix is important for DKIM selector of UniOne to work properly.
42
- - Third record contains optional, but very recommended SPF entry. Probably you already have one, then just add append include:spf.unione.io right after v=spf1.
43
+ - Second record value should contain "dkim" field value after "v=DKIM1; k=rsa; p=". Mind the name of this record! "us._domainkey." prefix is important for DKIM selector of UniOne to work properly.
44
+ - Third record contains optional, but very recommended SPF entry. It is not returned by the method. Probably you already have one, then just add append include:spf.unione.io right after v=spf1.
45
+ - Fourth record is a CNAME of the "_dmarc" host to the "dmarc" field value: UniOne then publishes the DMARC policy of the domain. If the domain already has its own DMARC TXT record, keep it instead.
43
46
 
44
47
  Syntax requirements of different DNS providers may differ:
45
48
  - sometimes it's necesarry to put full domain name instead of @ sign.
@@ -196,6 +199,8 @@ Creates a sandbox domain for the account or returns the existing one.
196
199
 
197
200
  Returns an account sandbox domain that is ready for use in the `from_email` field of [email/send](https://docs.unione.io/en/web-api-ref#email-send). If the account has no sandbox domain, UniOne creates one and returns it. Repeated calls return the same domain.
198
201
 
202
+ The sandbox domain is removed 31 days after it was created; the next call creates a new one. Up to 100 emails a day can be sent from it (the default limit of the account).
203
+
199
204
  The method is available only with an account API key. Project API keys cannot create sandbox domains.
200
205
 
201
206
  ### Request
@@ -30,13 +30,16 @@ module UniOne
30
30
  #
31
31
  # `@ IN TXT "unione-validate-hash=483bb362ebdbeedd755cfb1d4d661"`
32
32
  #
33
- # `us._domainkey IN TXT "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo7"`
33
+ # `us._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo7"`
34
34
  #
35
35
  # `@ IN TXT "v=spf1 include:spf.unione.io ~all"`
36
36
  #
37
+ # `_dmarc IN CNAME "example.com.dmarc.unione.io."`
38
+ #
37
39
  # - First record value should contain "verification-record" field returned by get-dns-records method "as is".
38
- # - Second record value should contain "dkim" field value after "p=". Mind the name of this record! "us._domainkey." prefix is important for DKIM selector of UniOne to work properly.
39
- # - Third record contains optional, but very recommended SPF entry. Probably you already have one, then just add append include:spf.unione.io right after v=spf1.
40
+ # - Second record value should contain "dkim" field value after "v=DKIM1; k=rsa; p=". Mind the name of this record! "us._domainkey." prefix is important for DKIM selector of UniOne to work properly.
41
+ # - Third record contains optional, but very recommended SPF entry. It is not returned by the method. Probably you already have one, then just add append include:spf.unione.io right after v=spf1.
42
+ # - Fourth record is a CNAME of the "_dmarc" host to the "dmarc" field value: UniOne then publishes the DMARC policy of the domain. If the domain already has its own DMARC TXT record, keep it instead.
40
43
  #
41
44
  # Syntax requirements of different DNS providers may differ:
42
45
  # - sometimes it's necesarry to put full domain name instead of @ sign.
@@ -102,6 +105,9 @@ module UniOne
102
105
  # [email/send](https://docs.unione.io/en/web-api-ref#email-send). If the account has no sandbox
103
106
  # domain, UniOne creates one and returns it. Repeated calls return the same domain.
104
107
  #
108
+ # The sandbox domain is removed 31 days after it was created; the next call creates a new one. Up to
109
+ # 100 emails a day can be sent from it (the default limit of the account).
110
+ #
105
111
  # The method is available only with an account API key. Project API keys cannot create sandbox
106
112
  # domains.
107
113
  #
@@ -47,6 +47,31 @@ module UniOne
47
47
  @metadata = metadata
48
48
  end
49
49
 
50
+ # A recipient from "email" or "Name <email>": the name goes to the to_name substitution (the
51
+ # name in the "To" header).
52
+ # @param address [String]
53
+ # @param substitutions [Hash{String => Object}, nil] more substitutions of the recipient
54
+ # @return [MessageObjectRecipientsItem]
55
+ def self.from_address(address, substitutions = nil)
56
+ email = address.to_s.strip
57
+ name = ''
58
+ match = email.match(/\A(.*)<([^>]+)>\z/m)
59
+ if match
60
+ email = match[2].strip
61
+ name = match[1].strip.gsub(/\A"+|"+\z/, '')
62
+ end
63
+ substitutions = { 'to_name' => name }.merge(substitutions || {}) unless name.empty?
64
+ new(email: email, substitutions: substitutions)
65
+ end
66
+
67
+ # Sets the metadata of the recipient (returned in the webhook events of its email).
68
+ # @param metadata [Hash{String => String}]
69
+ # @return [MessageObjectRecipientsItem] the recipient
70
+ def with_metadata(metadata)
71
+ self.metadata = metadata
72
+ self
73
+ end
74
+
50
75
  # Builds the object from decoded JSON. Lenient: a missing field, nil or a value of another type
51
76
  # gives nil (data that is not a Hash gives an empty object).
52
77
  def self.from_h(data)
@@ -7,7 +7,7 @@ require 'uri'
7
7
  module UniOne
8
8
  module Client
9
9
  # The SDK and its version in the "platform" query parameter of every request.
10
- PLATFORM = 'ruby-sdk.1.1.3'
10
+ PLATFORM = 'ruby-sdk.1.2.3'
11
11
 
12
12
  # Default transport (HTTP layer of ApiClient): Net::HTTP of the standard library.
13
13
  #
@@ -36,7 +36,7 @@ module UniOne
36
36
  # default ones of the same name
37
37
  attr_accessor :headers
38
38
 
39
- def initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: 30, user_agent: 'unione-ruby-sdk/1.1.3', platform: nil, headers: {})
39
+ def initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: 30, user_agent: 'unione-ruby-sdk/1.2.3', platform: nil, headers: {})
40
40
  @api_key = api_key
41
41
  @base_url = base_url.sub(%r{/+\z}, '')
42
42
  @timeout = timeout
@@ -28,6 +28,18 @@ module UniOne
28
28
  @amp = amp
29
29
  end
30
30
 
31
+ # An HTML body, with an optional plain-text part.
32
+ # @return [BodyObject]
33
+ def self.html(html, plaintext = nil)
34
+ new(html: html, plaintext: plaintext)
35
+ end
36
+
37
+ # A plain-text body.
38
+ # @return [BodyObject]
39
+ def self.text(plaintext)
40
+ new(plaintext: plaintext)
41
+ end
42
+
31
43
  # Builds the object from decoded JSON. Lenient: a missing field, nil or a value of another type
32
44
  # gives nil (data that is not a Hash gives an empty object).
33
45
  def self.from_h(data)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UniOne
4
- VERSION = '1.1.3'
4
+ VERSION = '1.2.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unione
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - UniOne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-20 00:00:00.000000000 Z
11
+ date: 2026-10-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: