paubox 0.3.2 → 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 +4 -4
- data/.github/scripts/assert_specs_ran.rb +40 -0
- data/.github/workflows/ci.yml +40 -0
- data/.github/workflows/pr-title.yml +36 -0
- data/.github/workflows/release-please.yml +88 -0
- data/.gitignore +4 -1
- data/.release-please-manifest.json +3 -0
- data/CHANGELOG.md +74 -0
- data/CLAUDE.md +114 -0
- data/README.md +255 -12
- data/api.md +283 -0
- data/lib/paubox/client.rb +15 -5
- data/lib/paubox/dynamic_templates.rb +58 -0
- data/lib/paubox/form.rb +51 -0
- data/lib/paubox/form_submission.rb +34 -0
- data/lib/paubox/forms_client.rb +211 -0
- data/lib/paubox/version.rb +1 -1
- data/lib/paubox.rb +10 -2
- data/paubox_ruby.gemspec +4 -0
- data/release-please-config.json +14 -0
- metadata +44 -7
- data/.travis.yml +0 -5
data/README.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<img src="https://avatars.githubusercontent.com/u/22528478?s=200&v=4" alt="Paubox" width="150px">
|
|
2
2
|
|
|
3
3
|
# Paubox Gem
|
|
4
|
-
This is the official Ruby wrapper for the Paubox
|
|
4
|
+
This is the official Ruby wrapper for the Paubox API. It supports the Paubox Email API — which allows your application to send secure, HIPAA compliant email via Paubox and track deliveries and opens — and the Paubox Forms API, which allows you to fetch form definitions, submit form responses, and manage forms and their submissions.
|
|
5
5
|
|
|
6
6
|
It extends the [Ruby Mail Library](https://github.com/mikel/mail) for seamless integration in your existing Ruby application. The API wrapper also allows you to construct and send messages directly without the Ruby Mail Library.
|
|
7
7
|
|
|
8
8
|
# Table of Contents
|
|
9
9
|
* [Installation](#installation)
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* [Usage](#usage)
|
|
11
|
+
* [Sending Email](#sending-messages-with-the-ruby-mail-library)
|
|
12
|
+
* [Paubox Forms](#paubox-forms)
|
|
13
|
+
* [Contributing](#contributing)
|
|
14
|
+
* [License](#license)
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
<a name="#installation"></a>
|
|
@@ -30,7 +32,7 @@ Or install it yourself as:
|
|
|
30
32
|
$ gem install paubox
|
|
31
33
|
|
|
32
34
|
### Getting Paubox API Credentials
|
|
33
|
-
You will need to have a Paubox account. You can [sign up here](https://www.paubox.com/
|
|
35
|
+
You will need to have a Paubox account. You can [sign up here](https://www.paubox.com/pricing/paubox-email-api).
|
|
34
36
|
|
|
35
37
|
Once you have an account, follow the instructions on the Rest API dashboard to verify domain ownership and generate API credentials.
|
|
36
38
|
|
|
@@ -42,10 +44,11 @@ Keep your API credentials out of version control. Store these in environment var
|
|
|
42
44
|
```ruby
|
|
43
45
|
Paubox.configure do |config|
|
|
44
46
|
config.api_key = ENV['PAUBOX_API_KEY']
|
|
45
|
-
config.api_user = ENV['PAUBOX_API_USER']
|
|
46
47
|
end
|
|
47
48
|
```
|
|
48
49
|
|
|
50
|
+
_Note: earlier versions of this gem also required an `api_user`. It is no longer needed — the API key alone authenticates. The `api_user` configuration option is deprecated and ignored._
|
|
51
|
+
|
|
49
52
|
If you need to send from multiple domains, you can pass credentials in the options hash when you set Ruby Mail's `Mail#delivery_method`, or when using `Paubox::Message`, when you instantiate `Paubox::Client`.
|
|
50
53
|
|
|
51
54
|
**(optional) Setting credentials when using Ruby Mail:**
|
|
@@ -53,16 +56,14 @@ If you need to send from multiple domains, you can pass credentials in the optio
|
|
|
53
56
|
```ruby
|
|
54
57
|
message = Mail.new do
|
|
55
58
|
...
|
|
56
|
-
delivery_method(Mail::Paubox, api_key: ENV['PAUBOX_API_KEY']
|
|
57
|
-
api_user: ENV['PAUBOX_API_USER'])
|
|
59
|
+
delivery_method(Mail::Paubox, api_key: ENV['PAUBOX_API_KEY'])
|
|
58
60
|
end
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
**(optional) Setting credentials when using Paubox::Client:**
|
|
62
64
|
|
|
63
65
|
```ruby
|
|
64
|
-
client = Paubox::Client.new(api_key: ENV['PAUBOX_API_KEY']
|
|
65
|
-
api_user: ENV['PAUBOX_API_USER'])
|
|
66
|
+
client = Paubox::Client.new(api_key: ENV['PAUBOX_API_KEY'])
|
|
66
67
|
```
|
|
67
68
|
|
|
68
69
|
<a name="#usage"></a>
|
|
@@ -190,10 +191,42 @@ message = Paubox::Message.new(args)
|
|
|
190
191
|
client = Paubox::Client.new
|
|
191
192
|
client.deliver_mail(message)
|
|
192
193
|
=> {"message"=>"Service OK", "sourceTrackingId"=>"2a3c048485aa4cf6"}
|
|
194
|
+
```
|
|
195
|
+
### Manage Dynamic Templates
|
|
196
|
+
Can manage(create, update, find and delete) the dynamic templates. by using the following commands and use these to send the Templated Messages.
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
require 'Paubox'
|
|
200
|
+
require 'json'
|
|
201
|
+
|
|
202
|
+
template_name = "Template name"
|
|
203
|
+
template_path = "Template File path"
|
|
204
|
+
|
|
205
|
+
# For create the new dynamic template
|
|
206
|
+
Paubox::DynamicTemplates.create(template_name, template_path)
|
|
207
|
+
=> { "RestClient::Response"=>"201", "message"=>"Template #{name} created!" }
|
|
208
|
+
|
|
209
|
+
# For getting the list of all dynamic template of your organization
|
|
210
|
+
Paubox::DynamicTemplates.list
|
|
211
|
+
=>[{"id"=>1, "name"=>"test", "api_customer_id"=>11},
|
|
212
|
+
{"id"=>3, "name"=>"Test", "api_customer_id"=>11}]
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# For update the existing dynamic template
|
|
216
|
+
dynamic_template = Paubox::DynamicTemplates.find(template_id)
|
|
217
|
+
dynamic_template.update(template_path, template_name)
|
|
218
|
+
=> {"RestClient::Response"=>"200", "message"=>"Template #{name} updated!"}
|
|
219
|
+
|
|
220
|
+
# For delete the existing dynamic template
|
|
221
|
+
dynamic_template = Paubox::DynamicTemplates.find(template_id)
|
|
222
|
+
dynamic_template.delete
|
|
223
|
+
=> {"RestClient::Response"=>"200", "message"=>"Template #{name} deleted!"}
|
|
224
|
+
|
|
225
|
+
|
|
193
226
|
```
|
|
194
227
|
|
|
195
228
|
### Send Messages using Dynamic Templates
|
|
196
|
-
Using [dynamic templates](https://docs.paubox.com/
|
|
229
|
+
Using above[dynamic templates](https://docs.paubox.com/email-api/dynamic-templates) is similar to sending a regular message. Just create a `Paubox::TemplatedMessage` object and pass a `template` object with the name of the template and variables:
|
|
197
230
|
|
|
198
231
|
```ruby
|
|
199
232
|
require 'Paubox'
|
|
@@ -223,6 +256,7 @@ client.deliver_mail(templated_message)
|
|
|
223
256
|
|
|
224
257
|
_Note that there is no `content` when using templated messages._
|
|
225
258
|
|
|
259
|
+
|
|
226
260
|
### Checking Email Dispositions
|
|
227
261
|
```ruby
|
|
228
262
|
require 'Paubox'
|
|
@@ -259,10 +293,214 @@ status.opened_time
|
|
|
259
293
|
=> Mon, 30 Apr 2018 12:55:19 -0700
|
|
260
294
|
```
|
|
261
295
|
|
|
296
|
+
<a name="#paubox-forms"></a>
|
|
297
|
+
## Paubox Forms
|
|
298
|
+
|
|
299
|
+
The Paubox Forms API has two kinds of endpoints. **Public endpoints** (fetching a form definition, submitting a form) require no authentication and are intended for form embed use cases. **Authenticated endpoints** (managing forms and their submissions) require a Paubox API key scoped to `forms`.
|
|
300
|
+
|
|
301
|
+
### Public Endpoints (No Authentication)
|
|
302
|
+
|
|
303
|
+
These endpoints send no auth header, so `Paubox::FormsClient` can be constructed without any credentials.
|
|
304
|
+
|
|
305
|
+
#### Getting Form Metadata
|
|
306
|
+
|
|
307
|
+
```ruby
|
|
308
|
+
require 'Paubox'
|
|
309
|
+
|
|
310
|
+
client = Paubox::FormsClient.new
|
|
311
|
+
form = client.get_form('550e8400-e29b-41d4-a716-446655440000')
|
|
312
|
+
|
|
313
|
+
form.title # => "Patient Intake Form"
|
|
314
|
+
form.description # => "Please complete before your appointment."
|
|
315
|
+
form.active? # => true
|
|
316
|
+
form.signable? # => false
|
|
317
|
+
form.submission_count # => 42
|
|
318
|
+
form.form_html # => "<form>...</form>"
|
|
319
|
+
form.form_json # => { ... }
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
#### Submitting a Form
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
require 'Paubox'
|
|
326
|
+
|
|
327
|
+
client = Paubox::FormsClient.new
|
|
328
|
+
|
|
329
|
+
client.submit_form('550e8400-e29b-41d4-a716-446655440000',
|
|
330
|
+
form_data: {
|
|
331
|
+
first_name: 'Jane',
|
|
332
|
+
last_name: 'Smith',
|
|
333
|
+
email: 'jane@example.com'
|
|
334
|
+
})
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
#### Submitting a Form with Attachments
|
|
338
|
+
|
|
339
|
+
File attachments must be base64-encoded. The maximum request size is 250 MB.
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
require 'Paubox'
|
|
343
|
+
require 'base64'
|
|
344
|
+
|
|
345
|
+
client = Paubox::FormsClient.new
|
|
346
|
+
|
|
347
|
+
client.submit_form('550e8400-e29b-41d4-a716-446655440000',
|
|
348
|
+
form_data: {
|
|
349
|
+
first_name: 'Jane',
|
|
350
|
+
signature: '{signature_field}'
|
|
351
|
+
},
|
|
352
|
+
attachments: [
|
|
353
|
+
{
|
|
354
|
+
name: 'consent.pdf',
|
|
355
|
+
content: Base64.strict_encode64(File.binread('consent.pdf'))
|
|
356
|
+
}
|
|
357
|
+
])
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Authenticated Endpoints (Scoped API Key)
|
|
361
|
+
|
|
362
|
+
Form management endpoints require a Paubox API key scoped to `forms`. This is a different key from the Email API key (`Paubox.configuration.api_key`), which is never used for Forms endpoints. Pass the forms key when you instantiate the client, or configure it via `Paubox.configure` (the client falls back to `Paubox.configuration.forms_api_key`). The key is sent as an `Authorization: Bearer` header on every management request.
|
|
363
|
+
|
|
364
|
+
```ruby
|
|
365
|
+
require 'Paubox'
|
|
366
|
+
|
|
367
|
+
client = Paubox::FormsClient.new(api_key: ENV['PAUBOX_FORMS_API_KEY'])
|
|
368
|
+
|
|
369
|
+
# or globally:
|
|
370
|
+
Paubox.configure { |config| config.forms_api_key = ENV['PAUBOX_FORMS_API_KEY'] }
|
|
371
|
+
client = Paubox::FormsClient.new
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Calling a management endpoint without an API key raises `ArgumentError`.
|
|
375
|
+
|
|
376
|
+
#### Listing Forms
|
|
377
|
+
|
|
378
|
+
Supports filtering, ordering, and pagination. `customer_id` is required and must match the API key's customer — the client raises `ArgumentError` without it. Optional params: `form_id`, `search`, `order` (`'asc'`/`'desc'`), `order_by` (`'title'`, `'updated_at'`, `'submission_count'`, `'created_at'`), `archived`, `active`, `page`, and `items` (capped at 100 by the server).
|
|
379
|
+
|
|
380
|
+
```ruby
|
|
381
|
+
result = client.list_forms(customer_id: 123, search: 'intake',
|
|
382
|
+
order_by: 'updated_at', order: 'desc',
|
|
383
|
+
page: 1, items: 25)
|
|
384
|
+
|
|
385
|
+
result[:forms].first.title # => "Patient Intake Form"
|
|
386
|
+
result[:page_info] # => {"count"=>42, "pages"=>2, "page"=>1, "items"=>25}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Creating a Form
|
|
390
|
+
|
|
391
|
+
Required attributes: `title`, `form_json`, `customer_id`, and `version`. Optional attributes include `description`, `form_html`, `form_css`, `recipient`, `signable`, `signature_confirmation_label`, `subscription_list_id`, `type`, and `active`.
|
|
392
|
+
|
|
393
|
+
```ruby
|
|
394
|
+
client.create_form(title: 'Patient Intake Form',
|
|
395
|
+
form_json: { fields: [{ name: 'first_name' }] },
|
|
396
|
+
customer_id: 123,
|
|
397
|
+
version: 1,
|
|
398
|
+
recipient: 'intake@yourdomain.com')
|
|
399
|
+
=> {"id"=>"550e8400-e29b-41d4-a716-446655440000"}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### Finding a Form
|
|
403
|
+
|
|
404
|
+
Returns a `Paubox::Form`. Unlike the public `get_form`, `find_form` can fetch inactive and archived forms.
|
|
405
|
+
|
|
406
|
+
```ruby
|
|
407
|
+
form = client.find_form('550e8400-e29b-41d4-a716-446655440000')
|
|
408
|
+
|
|
409
|
+
form.title # => "Patient Intake Form"
|
|
410
|
+
form.archived? # => false
|
|
411
|
+
form.recipient # => "intake@yourdomain.com"
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
#### Updating a Form
|
|
415
|
+
|
|
416
|
+
Updates are partial: fields you omit are left unchanged. Updatable fields: `title`, `description`, `form_json`, `vanity_url`, `recipient`, `active`, and `subscription_list_id`.
|
|
417
|
+
|
|
418
|
+
```ruby
|
|
419
|
+
client.update_form('550e8400-e29b-41d4-a716-446655440000',
|
|
420
|
+
title: 'Patient Intake Form (v2)',
|
|
421
|
+
active: false)
|
|
422
|
+
=> {"detail"=>"Form updated successfully", "form_id"=>"550e8400-e29b-41d4-a716-446655440000"}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
#### Archiving and Unarchiving a Form
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
client.archive_form('550e8400-e29b-41d4-a716-446655440000')
|
|
429
|
+
=> {"detail"=>"Form archived."}
|
|
430
|
+
|
|
431
|
+
client.unarchive_form('550e8400-e29b-41d4-a716-446655440000')
|
|
432
|
+
=> {"detail"=>"Form unarchived."}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### Copying a Form
|
|
436
|
+
|
|
437
|
+
Copies an existing form under a new title and returns the new form as a `Paubox::Form`.
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
form = client.copy_form('550e8400-e29b-41d4-a716-446655440000',
|
|
441
|
+
title: 'Patient Intake Form (Copy)')
|
|
442
|
+
|
|
443
|
+
form.id # => "7c9e6679-7425-40de-944b-e07fc1f90ae7"
|
|
444
|
+
form.title # => "Patient Intake Form (Copy)"
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### Form Stats
|
|
448
|
+
|
|
449
|
+
Returns aggregate counts. `customer_id` is optional and defaults server-side to the API key's customer.
|
|
450
|
+
|
|
451
|
+
```ruby
|
|
452
|
+
client.form_stats
|
|
453
|
+
=> {"active_form_count"=>12, "total_submission_count"=>340, "submissions_last_7_days"=>18}
|
|
454
|
+
|
|
455
|
+
client.form_stats(customer_id: 123)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
#### Listing Submissions
|
|
459
|
+
|
|
460
|
+
Returns `Paubox::FormSubmission` objects plus pagination info. Available params: `submission_id`, `order_by` (`'submitter_email'`, `'created_at'`), `order`, `page`, and `items` (capped at 100 by the server).
|
|
461
|
+
|
|
462
|
+
```ruby
|
|
463
|
+
result = client.list_submissions('550e8400-e29b-41d4-a716-446655440000',
|
|
464
|
+
order_by: 'created_at', order: 'desc')
|
|
465
|
+
|
|
466
|
+
result[:total] # => 42
|
|
467
|
+
result[:page] # => 1
|
|
468
|
+
result[:items] # => 25
|
|
469
|
+
|
|
470
|
+
submission = result[:submissions].first
|
|
471
|
+
submission.submitter_email # => "jane@example.com"
|
|
472
|
+
submission.form_data # => {"first_name"=>"Jane", "last_name"=>"Smith"}
|
|
473
|
+
submission.created_at # => "2026-08-01T12:34:56Z"
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### Downloading Submissions as CSV
|
|
477
|
+
|
|
478
|
+
Returns the raw CSV as a `String`. Pass `submission_id:` to download a single submission.
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
# All submissions for a form
|
|
482
|
+
csv = client.submissions_csv('550e8400-e29b-41d4-a716-446655440000')
|
|
483
|
+
File.write('submissions.csv', csv)
|
|
484
|
+
|
|
485
|
+
# A single submission
|
|
486
|
+
csv = client.submissions_csv('550e8400-e29b-41d4-a716-446655440000',
|
|
487
|
+
submission_id: '7c9e6679-7425-40de-944b-e07fc1f90ae7')
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### Downloading a Submission as PDF
|
|
491
|
+
|
|
492
|
+
Returns the raw PDF bytes as a `String`.
|
|
493
|
+
|
|
494
|
+
```ruby
|
|
495
|
+
pdf = client.submission_pdf('550e8400-e29b-41d4-a716-446655440000',
|
|
496
|
+
'7c9e6679-7425-40de-944b-e07fc1f90ae7')
|
|
497
|
+
File.binwrite('submission.pdf', pdf)
|
|
498
|
+
```
|
|
499
|
+
|
|
262
500
|
<a name="#contributing"></a>
|
|
263
501
|
## Contributing
|
|
264
502
|
|
|
265
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/paubox/
|
|
503
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/paubox/paubox-ruby.
|
|
266
504
|
|
|
267
505
|
|
|
268
506
|
<a name="#license"></a>
|
|
@@ -281,3 +519,8 @@ limitations under the License.
|
|
|
281
519
|
## Copyright
|
|
282
520
|
Copyright © 2022, Paubox, Inc.
|
|
283
521
|
|
|
522
|
+
## 💬 Community & support
|
|
523
|
+
|
|
524
|
+
Questions, ideas, or want to share what you built? Join the **[Paubox Community](https://github.com/Paubox/community/discussions)** — the single home for discussions across every Paubox SDK and API.
|
|
525
|
+
|
|
526
|
+
🔐 Found a security issue? Email **devops@paubox.com** — please don't post it publicly.
|
data/api.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# Paubox Ruby Gem — API Reference
|
|
2
|
+
|
|
3
|
+
## Email API
|
|
4
|
+
|
|
5
|
+
### Authentication
|
|
6
|
+
|
|
7
|
+
All Email API requests use token-based authentication. The API key alone authenticates — no username is required. Configure the key once globally or pass it per client instance.
|
|
8
|
+
|
|
9
|
+
**Global configuration:**
|
|
10
|
+
```ruby
|
|
11
|
+
Paubox.configure do |config|
|
|
12
|
+
config.api_key = ENV['PAUBOX_API_KEY']
|
|
13
|
+
end
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Per-instance:**
|
|
17
|
+
```ruby
|
|
18
|
+
client = Paubox::Client.new(api_key: ENV['PAUBOX_API_KEY'])
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
_The `api_user` configuration option is deprecated and ignored._
|
|
22
|
+
|
|
23
|
+
**Authorization header sent on every request:**
|
|
24
|
+
```
|
|
25
|
+
Authorization: Token token=<api_key>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Base URL
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
https://api.paubox.com/v1
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
All Email API paths below are relative to this base.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### Send Message
|
|
39
|
+
|
|
40
|
+
**POST** `/messages`
|
|
41
|
+
|
|
42
|
+
Sends a HIPAA-compliant email.
|
|
43
|
+
|
|
44
|
+
**Request body:**
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"data": {
|
|
48
|
+
"message": {
|
|
49
|
+
"recipients": ["recipient@example.com"],
|
|
50
|
+
"cc": [],
|
|
51
|
+
"bcc": [],
|
|
52
|
+
"allowNonTLS": false,
|
|
53
|
+
"headers": {
|
|
54
|
+
"from": "sender@yourdomain.com",
|
|
55
|
+
"reply-to": "reply@yourdomain.com",
|
|
56
|
+
"subject": "Hello"
|
|
57
|
+
},
|
|
58
|
+
"content": {
|
|
59
|
+
"text/plain": "Plain text body",
|
|
60
|
+
"text/html": "<h1>HTML body</h1>"
|
|
61
|
+
},
|
|
62
|
+
"attachments": [
|
|
63
|
+
{
|
|
64
|
+
"fileName": "file.pdf",
|
|
65
|
+
"contentType": "application/pdf",
|
|
66
|
+
"content": "<base64-encoded content>"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Optional fields:**
|
|
75
|
+
- `forceSecureNotification` (`"true"` / `"false"`) — forces portal delivery
|
|
76
|
+
- `allowNonTLS` (`true` / `false`) — allows delivery without TLS
|
|
77
|
+
|
|
78
|
+
**Response (200):**
|
|
79
|
+
```json
|
|
80
|
+
{ "message": "Service OK", "sourceTrackingId": "2a3c048485aa4cf6" }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### Send Templated Message
|
|
86
|
+
|
|
87
|
+
**POST** `/templated_messages`
|
|
88
|
+
|
|
89
|
+
Sends a message using a dynamic template.
|
|
90
|
+
|
|
91
|
+
**Request body:**
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"data": {
|
|
95
|
+
"recipients": ["recipient@example.com"],
|
|
96
|
+
"headers": { "from": "sender@yourdomain.com", "subject": "Hello" },
|
|
97
|
+
"template_name": "My Template",
|
|
98
|
+
"template_values": { "first_name": "Jane", "last_name": "Smith" }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Response (200):**
|
|
104
|
+
```json
|
|
105
|
+
{ "sourceTrackingId": "166904b5-dce7-4de1-92e8-3d505c165ff5", "data": "Service OK" }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### Get Email Disposition
|
|
111
|
+
|
|
112
|
+
**GET** `/message_receipt?sourceTrackingId=<id>`
|
|
113
|
+
|
|
114
|
+
Returns delivery and open status for a sent message.
|
|
115
|
+
|
|
116
|
+
**Response (200):**
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"sourceTrackingId": "2a3c048485aa4cf6",
|
|
120
|
+
"data": {
|
|
121
|
+
"message": {
|
|
122
|
+
"id": "...",
|
|
123
|
+
"message_deliveries": [
|
|
124
|
+
{
|
|
125
|
+
"recipient": "recipient@example.com",
|
|
126
|
+
"status": {
|
|
127
|
+
"deliveryStatus": "delivered",
|
|
128
|
+
"deliveryTime": "2024-01-15T12:54:19-07:00",
|
|
129
|
+
"openedStatus": "opened",
|
|
130
|
+
"openedTime": "2024-01-15T12:55:19-07:00"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### Dynamic Templates
|
|
142
|
+
|
|
143
|
+
All paths are relative to the Email API base URL.
|
|
144
|
+
|
|
145
|
+
| Operation | Method | Path |
|
|
146
|
+
|----------------|---------|-------------------------------|
|
|
147
|
+
| List templates | GET | `/dynamic_templates` |
|
|
148
|
+
| Get template | GET | `/dynamic_templates/<id>` |
|
|
149
|
+
| Create | POST | `/dynamic_templates` |
|
|
150
|
+
| Update | PATCH | `/dynamic_templates/<id>` |
|
|
151
|
+
| Delete | DELETE | `/dynamic_templates/<id>` |
|
|
152
|
+
|
|
153
|
+
**Create / Update request body** (`multipart/form-data`):
|
|
154
|
+
- `data[name]` — template name
|
|
155
|
+
- `data[body]` — template file
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### API Status
|
|
160
|
+
|
|
161
|
+
**GET** `/status`
|
|
162
|
+
|
|
163
|
+
Returns service health. No authentication required in practice.
|
|
164
|
+
|
|
165
|
+
**Response (200):**
|
|
166
|
+
```json
|
|
167
|
+
{ "message": "Service OK" }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Forms API
|
|
173
|
+
|
|
174
|
+
### Authentication
|
|
175
|
+
|
|
176
|
+
**None required.** Forms API endpoints are public and designed for form embed use cases. No API key or authorization header is sent.
|
|
177
|
+
|
|
178
|
+
### Base URL
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
https://api.paubox.com/forms
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### Get Form Metadata
|
|
187
|
+
|
|
188
|
+
**GET** `/public/form_data/<form_id>`
|
|
189
|
+
|
|
190
|
+
Returns the full form definition for a given UUID. Used to render a form to a respondent.
|
|
191
|
+
|
|
192
|
+
**Path parameter:**
|
|
193
|
+
- `form_id` (UUID, required) — the form to retrieve
|
|
194
|
+
|
|
195
|
+
**Response (200):**
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
199
|
+
"title": "Patient Intake Form",
|
|
200
|
+
"description": "Please complete before your appointment.",
|
|
201
|
+
"form_json": {},
|
|
202
|
+
"form_html": "<form>...</form>",
|
|
203
|
+
"form_css": "form { font-family: sans-serif; }",
|
|
204
|
+
"vanity_url": null,
|
|
205
|
+
"version": 1,
|
|
206
|
+
"active": true,
|
|
207
|
+
"customer_id": 123,
|
|
208
|
+
"signable": false,
|
|
209
|
+
"signature_confirmation_label": null,
|
|
210
|
+
"submission_count": 42,
|
|
211
|
+
"type": null,
|
|
212
|
+
"deleted": false,
|
|
213
|
+
"archived": false,
|
|
214
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
215
|
+
"updated_at": "2024-06-01T08:00:00Z"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Errors:**
|
|
220
|
+
- `404` — form not found
|
|
221
|
+
|
|
222
|
+
**Ruby usage:**
|
|
223
|
+
```ruby
|
|
224
|
+
client = Paubox::FormsClient.new
|
|
225
|
+
form = client.get_form('550e8400-e29b-41d4-a716-446655440000')
|
|
226
|
+
|
|
227
|
+
form.title # => "Patient Intake Form"
|
|
228
|
+
form.active? # => true
|
|
229
|
+
form.signable? # => false
|
|
230
|
+
form.submission_count # => 42
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
### Submit Form Response
|
|
236
|
+
|
|
237
|
+
**POST** `/api/forms/<form_id>/submissions`
|
|
238
|
+
|
|
239
|
+
Submits a respondent's answers for a form. On success, the service stores the submission, increments the submission count, emails configured recipients, and returns 201 with no body. Maximum request size is **250 MB**.
|
|
240
|
+
|
|
241
|
+
**Path parameter:**
|
|
242
|
+
- `form_id` (UUID, required) — the form being submitted
|
|
243
|
+
|
|
244
|
+
**Request body:**
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"form_data": {
|
|
248
|
+
"first_name": "Jane",
|
|
249
|
+
"last_name": "Smith",
|
|
250
|
+
"email": "jane@example.com"
|
|
251
|
+
},
|
|
252
|
+
"attachments": [
|
|
253
|
+
{
|
|
254
|
+
"name": "consent.pdf",
|
|
255
|
+
"content": "<base64-encoded file content>"
|
|
256
|
+
}
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
- `form_data` (object, required) — key-value pairs matching the form's field schema
|
|
262
|
+
- `attachments` (array, optional) — file attachments; each item requires `name` (filename) and `content` (base64-encoded)
|
|
263
|
+
|
|
264
|
+
**Response:**
|
|
265
|
+
- `201` — submission accepted (no body)
|
|
266
|
+
- `400` — missing required `form_data` field
|
|
267
|
+
- `404` — form not found
|
|
268
|
+
|
|
269
|
+
**Ruby usage:**
|
|
270
|
+
```ruby
|
|
271
|
+
client = Paubox::FormsClient.new
|
|
272
|
+
|
|
273
|
+
# Text fields only
|
|
274
|
+
client.submit_form('550e8400-e29b-41d4-a716-446655440000',
|
|
275
|
+
form_data: { first_name: 'Jane', last_name: 'Smith', email: 'jane@example.com' })
|
|
276
|
+
|
|
277
|
+
# With file attachments
|
|
278
|
+
client.submit_form('550e8400-e29b-41d4-a716-446655440000',
|
|
279
|
+
form_data: { first_name: 'Jane', signature: '{signature_field}' },
|
|
280
|
+
attachments: [
|
|
281
|
+
{ name: 'consent.pdf', content: Base64.strict_encode64(File.binread('consent.pdf')) }
|
|
282
|
+
])
|
|
283
|
+
```
|
data/lib/paubox/client.rb
CHANGED
|
@@ -5,12 +5,16 @@ module Paubox
|
|
|
5
5
|
class Client
|
|
6
6
|
require 'rest-client'
|
|
7
7
|
require 'ostruct'
|
|
8
|
-
attr_reader :api_key, :
|
|
8
|
+
attr_reader :api_key, :api_host, :api_protocol, :api_version
|
|
9
|
+
|
|
10
|
+
# Deprecated: api_user is no longer used for authentication or URLs.
|
|
11
|
+
# It is kept only for backward compatibility and has no effect on requests.
|
|
12
|
+
attr_reader :api_user
|
|
9
13
|
|
|
10
14
|
def initialize(args = {})
|
|
11
15
|
args = defaults.merge(args)
|
|
12
16
|
@api_key = args[:api_key]
|
|
13
|
-
@api_user = args[:api_user]
|
|
17
|
+
@api_user = args[:api_user] # deprecated no-op, kept for backward compatibility
|
|
14
18
|
@api_host = args[:api_host]
|
|
15
19
|
@api_protocol = args[:api_protocol]
|
|
16
20
|
@api_version = args[:api_version]
|
|
@@ -50,6 +54,12 @@ module Paubox
|
|
|
50
54
|
end
|
|
51
55
|
alias message_receipt email_disposition
|
|
52
56
|
|
|
57
|
+
def send_request(method: :get, payload: {}, path: '')
|
|
58
|
+
url = request_endpoint(path)
|
|
59
|
+
|
|
60
|
+
RestClient::Request.execute(method: method, url: url, payload: payload, headers: auth_header)
|
|
61
|
+
end
|
|
62
|
+
|
|
53
63
|
private
|
|
54
64
|
|
|
55
65
|
def auth_header
|
|
@@ -59,7 +69,7 @@ module Paubox
|
|
|
59
69
|
end
|
|
60
70
|
|
|
61
71
|
def api_base_endpoint
|
|
62
|
-
"#{api_protocol}#{api_host}/#{api_version}
|
|
72
|
+
"#{api_protocol}#{api_host}/#{api_version}"
|
|
63
73
|
end
|
|
64
74
|
|
|
65
75
|
def request_endpoint(endpoint)
|
|
@@ -68,8 +78,8 @@ module Paubox
|
|
|
68
78
|
|
|
69
79
|
def defaults
|
|
70
80
|
{ api_key: Paubox.configuration.api_key,
|
|
71
|
-
api_user: Paubox.configuration.api_user,
|
|
72
|
-
api_host: 'api.paubox.
|
|
81
|
+
api_user: Paubox.configuration.api_user, # deprecated, unused
|
|
82
|
+
api_host: 'api.paubox.com',
|
|
73
83
|
api_protocol: 'https://',
|
|
74
84
|
api_version: 'v1',
|
|
75
85
|
test_mode: false }
|