sendgrid-ruby 6.2.0 → 6.3.4
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/.codeclimate.yml +1 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +12 -21
- data/CHANGELOG.md +57 -8
- data/CONTRIBUTING.md +10 -17
- data/Dockerfile +14 -0
- data/FIRST_TIMERS.md +79 -0
- data/Gemfile +0 -1
- data/ISSUE_TEMPLATE.md +5 -1
- data/Makefile +9 -2
- data/PULL_REQUEST_TEMPLATE.md +1 -1
- data/README.md +22 -21
- data/TROUBLESHOOTING.md +5 -5
- data/USAGE.md +35 -36
- data/examples/helpers/eventwebhook/example.rb +16 -0
- data/examples/helpers/mail/example.rb +11 -4
- data/examples/mail/mail.rb +1 -1
- data/lib/rack/sendgrid_webhook_verification.rb +52 -0
- data/lib/sendgrid-ruby.rb +2 -0
- data/lib/sendgrid/helpers/eventwebhook/eventwebhook.rb +52 -0
- data/lib/sendgrid/helpers/inbound/README.md +5 -5
- data/lib/sendgrid/helpers/inbound/public/index.html +1 -1
- data/lib/sendgrid/helpers/mail/README.md +3 -3
- data/lib/sendgrid/helpers/settings/README.md +2 -2
- data/lib/sendgrid/version.rb +1 -1
- data/mail_helper_v3.md +9 -9
- data/sendgrid-ruby.gemspec +2 -0
- data/spec/fixtures/event_webhook.rb +16 -0
- data/spec/rack/sendgrid_webhook_verification_spec.rb +116 -0
- data/spec/sendgrid/helpers/eventwebhook/eventwebhook_spec.rb +103 -0
- data/spec/spec_helper.rb +2 -0
- data/static/img/github-fork.png +0 -0
- data/static/img/github-sign-up.png +0 -0
- data/test/sendgrid/helpers/mail/test_mail.rb +1 -1
- data/test/sendgrid/test_sendgrid-ruby.rb +24 -59
- data/twilio_sendgrid_logo.png +0 -0
- data/use-cases/README.md +16 -0
- data/use-cases/domain-authentication.md +5 -0
- data/use-cases/email-statistics.md +52 -0
- data/use-cases/legacy-templates.md +98 -0
- data/use-cases/sms.md +39 -0
- data/use-cases/transactional-templates.md +111 -0
- data/use-cases/twilio-email.md +13 -0
- data/use-cases/twilio-setup.md +54 -0
- metadata +54 -8
- data/USE_CASES.md +0 -405
- data/docker/Dockerfile +0 -12
- data/docker/README.md +0 -30
- data/test/prism.sh +0 -42
data/spec/spec_helper.rb
CHANGED
Binary file
|
Binary file
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
1
3
|
require_relative '../../lib/sendgrid-ruby.rb'
|
2
4
|
require 'ruby_http_client'
|
3
5
|
require 'minitest/autorun'
|
@@ -5,34 +7,8 @@ require 'minitest/unit'
|
|
5
7
|
|
6
8
|
class TestAPI < MiniTest::Test
|
7
9
|
|
8
|
-
unless File.exist?('/usr/local/bin/prism') || File.exist?(File.join(Dir.pwd, 'prism/bin/prism'))
|
9
|
-
if RUBY_PLATFORM =~ /mswin|mingw/
|
10
|
-
puts 'Please download the Windows binary (https://github.com/stoplightio/prism/releases) and place it in your /usr/local/bin directory'
|
11
|
-
else
|
12
|
-
puts 'Installing Prism'
|
13
|
-
IO.popen(['curl', '-s', 'https://raw.githubusercontent.com/stoplightio/prism/master/install.sh']) do |io|
|
14
|
-
out = io.read
|
15
|
-
unless system(out)
|
16
|
-
puts "Error downloading the prism binary, you can try downloading directly here (https://github.com/stoplightio/prism/releases) and place in your /usr/local/bin directory, #{out}"
|
17
|
-
exit
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
puts 'Activating Prism (~20 seconds)'
|
24
|
-
@@prism_pid = spawn('prism run --mock --list --spec https://raw.githubusercontent.com/sendgrid/sendgrid-oai/master/oai_stoplight.json', [:out, :err] => '/dev/null')
|
25
|
-
sleep(15)
|
26
|
-
puts 'Prism started'
|
27
|
-
|
28
10
|
def setup
|
29
|
-
|
30
|
-
@sg = SendGrid::API.new(api_key: "SENDGRID_API_KEY", host: host)
|
31
|
-
end
|
32
|
-
|
33
|
-
Minitest.after_run do
|
34
|
-
Process.kill('TERM', @@prism_pid)
|
35
|
-
puts 'Prism shut down'
|
11
|
+
@sg = SendGrid::API.new(api_key: "SENDGRID_API_KEY")
|
36
12
|
end
|
37
13
|
|
38
14
|
def test_init
|
@@ -58,7 +34,7 @@ class TestAPI < MiniTest::Test
|
|
58
34
|
assert_equal(test_headers, sg.request_headers)
|
59
35
|
assert_equal("v3", sg.version)
|
60
36
|
assert_equal(subuser, sg.impersonate_subuser)
|
61
|
-
assert_equal("6.
|
37
|
+
assert_equal("6.3.4", SendGrid::VERSION)
|
62
38
|
assert_instance_of(SendGrid::Client, sg.client)
|
63
39
|
end
|
64
40
|
|
@@ -2676,76 +2652,65 @@ class TestAPI < MiniTest::Test
|
|
2676
2652
|
self.assert_equal('200', response.status_code)
|
2677
2653
|
end
|
2678
2654
|
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2683
|
-
year_range = File.open('./LICENSE.md', &:readline).gsub(/[^\d-]/, '')
|
2684
|
-
self.assert_equal("#{Time.now.year}", year_range)
|
2685
|
-
end
|
2655
|
+
def test_license_file_year
|
2656
|
+
# Read the third line from the license file
|
2657
|
+
year = IO.readlines('./LICENSE.md')[2].gsub(/[^\d]/, '')
|
2658
|
+
self.assert_equal("#{Time.now.year}", year)
|
2686
2659
|
end
|
2687
2660
|
|
2688
|
-
def test_docker_exists
|
2689
|
-
assert(File.file?('./Docker') || File.file?('./docker/Dockerfile'))
|
2690
|
-
end
|
2691
|
-
|
2692
|
-
# def test_docker_compose_exists
|
2693
|
-
# assert(File.file?('./docker-compose.yml') || File.file?('./docker/docker-compose.yml'))
|
2694
|
-
# end
|
2695
|
-
|
2696
2661
|
def test_env_sample_exists
|
2697
|
-
|
2662
|
+
assert(File.file?('./.env_sample'))
|
2698
2663
|
end
|
2699
2664
|
|
2700
2665
|
def test_gitignore_exists
|
2701
|
-
|
2666
|
+
assert(File.file?('./.gitignore'))
|
2702
2667
|
end
|
2703
2668
|
|
2704
2669
|
def test_travis_exists
|
2705
|
-
|
2670
|
+
assert(File.file?('./.travis.yml'))
|
2706
2671
|
end
|
2707
2672
|
|
2708
2673
|
def test_codeclimate_exists
|
2709
|
-
|
2674
|
+
assert(File.file?('./.codeclimate.yml'))
|
2710
2675
|
end
|
2711
2676
|
|
2712
2677
|
def test_changelog_exists
|
2713
|
-
|
2678
|
+
assert(File.file?('./CHANGELOG.md'))
|
2714
2679
|
end
|
2715
2680
|
|
2716
2681
|
def test_code_of_conduct_exists
|
2717
|
-
|
2682
|
+
assert(File.file?('./CODE_OF_CONDUCT.md'))
|
2718
2683
|
end
|
2719
2684
|
|
2720
2685
|
def test_contributing_exists
|
2721
|
-
|
2686
|
+
assert(File.file?('./CONTRIBUTING.md'))
|
2722
2687
|
end
|
2723
2688
|
|
2724
2689
|
def test_issue_template_exists
|
2725
|
-
|
2690
|
+
assert(File.file?('./ISSUE_TEMPLATE.md'))
|
2726
2691
|
end
|
2727
2692
|
|
2728
2693
|
def test_license_exists
|
2729
|
-
|
2694
|
+
assert(File.file?('./LICENSE.md'))
|
2730
2695
|
end
|
2731
2696
|
|
2732
|
-
def
|
2733
|
-
|
2697
|
+
def test_pr_template_exists
|
2698
|
+
assert(File.file?('./PULL_REQUEST_TEMPLATE.md'))
|
2734
2699
|
end
|
2735
2700
|
|
2736
2701
|
def test_readme_exists
|
2737
|
-
|
2702
|
+
assert(File.file?('./README.md'))
|
2738
2703
|
end
|
2739
2704
|
|
2740
2705
|
def test_troubleshooting_exists
|
2741
|
-
|
2706
|
+
assert(File.file?('./TROUBLESHOOTING.md'))
|
2742
2707
|
end
|
2743
2708
|
|
2744
2709
|
def test_usage_exists
|
2745
|
-
|
2710
|
+
assert(File.file?('./USAGE.md'))
|
2746
2711
|
end
|
2747
2712
|
|
2748
|
-
def
|
2749
|
-
|
2713
|
+
def test_use_cases_readme_exists
|
2714
|
+
assert(File.file?('./use-cases/README.md'))
|
2750
2715
|
end
|
2751
2716
|
end
|
Binary file
|
data/use-cases/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
This directory provides examples for specific use cases.
|
2
|
+
|
3
|
+
Please [open an issue](https://github.com/sendgrid/sendgrid-ruby/issues) or [make a pull request](https://github.com/sendgrid/sendgrid-ruby/pulls) for any use cases you would like us to document here. Thank you!
|
4
|
+
|
5
|
+
# Email Use Cases
|
6
|
+
* [Transactional Templates](transactional-templates.md)
|
7
|
+
* [Legacy Templates](legacy-templates.md)
|
8
|
+
|
9
|
+
# Twilio Use Cases
|
10
|
+
* [Twilio Setup](twilio-setup.md)
|
11
|
+
* [Send an Email With Twilio Email (Pilot)](twilio-email.md)
|
12
|
+
* [Send an SMS Message](sms.md)
|
13
|
+
|
14
|
+
# Non-Email Use Cases
|
15
|
+
* [How to Set up a Domain Authentication](domain-authentication.md)
|
16
|
+
* [How to View Email Statistics](email-statistics.md)
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# How to Setup a Domain Authentication
|
2
|
+
|
3
|
+
You can find documentation for how to setup a domain authentication via the UI [here](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) and via API [here](../USAGE.md#sender-authentication).
|
4
|
+
|
5
|
+
Find more information about all of Twilio SendGrid's authentication related documentation [here](https://sendgrid.com/docs/ui/account-and-settings/).
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# How to View Email Statistics
|
2
|
+
|
3
|
+
You can find documentation for how to view your email statistics via the UI [here](https://app.sendgrid.com/statistics) and via API [here](../USAGE.md#stats).
|
4
|
+
|
5
|
+
Alternatively, we can post events to a URL of your choice via our [Event Webhook](https://sendgrid.com/docs/API_Reference/Webhooks/event.html) about events that occur as Twilio SendGrid processes your email.
|
6
|
+
|
7
|
+
You can also use the email statistics helper to make it easier to interact with the API.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'sendgrid-ruby'
|
11
|
+
require 'date'
|
12
|
+
|
13
|
+
include SendGrid
|
14
|
+
|
15
|
+
sg_client = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY']).client
|
16
|
+
stats = SendGrid::EmailStats.new(sendgrid_client: sg_client)
|
17
|
+
|
18
|
+
# Fetch stats by day, between 2 dates
|
19
|
+
from = Date.new(2017, 10, 01)
|
20
|
+
to = Date.new(2017, 10, 12)
|
21
|
+
|
22
|
+
email_stats = stats.by_day(from, to)
|
23
|
+
|
24
|
+
email_stats.metrics
|
25
|
+
|
26
|
+
if !email_stats.error?
|
27
|
+
email_stats.metrics.each do |metric|
|
28
|
+
puts "Date - #{metric.date}"
|
29
|
+
puts "Number of Requests - #{metric.requests}"
|
30
|
+
puts "Bounces - #{metric.bounces}"
|
31
|
+
puts "Opens - #{metric.opens}"
|
32
|
+
puts "Clicks - #{metric.clicks}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Fetch stats by week, between 2 dates for a category
|
37
|
+
from = Date.new(2017, 10, 01)
|
38
|
+
to = Date.new(2017, 10, 12)
|
39
|
+
category = 'abcd'
|
40
|
+
|
41
|
+
email_stats = stats.by_week(from, to, category)
|
42
|
+
|
43
|
+
if !email_stats.error?
|
44
|
+
email_stats.metrics.each do |metric|
|
45
|
+
puts "Date - #{metric.date}"
|
46
|
+
puts "Number of Requests - #{metric.requests}"
|
47
|
+
puts "Bounces - #{metric.bounces}"
|
48
|
+
puts "Opens - #{metric.opens}"
|
49
|
+
puts "Clicks - #{metric.clicks}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Legacy Templates
|
2
|
+
|
3
|
+
For this example, we assume you have created a [legacy template](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). Following is the template content we used for testing.
|
4
|
+
|
5
|
+
Template ID (replace with your own):
|
6
|
+
```text
|
7
|
+
13b8f94f-bcae-4ec6-b752-70d6cb59f932
|
8
|
+
```
|
9
|
+
|
10
|
+
Email Subject:
|
11
|
+
```text
|
12
|
+
<%subject%>
|
13
|
+
```
|
14
|
+
|
15
|
+
Template Body:
|
16
|
+
```html
|
17
|
+
<html>
|
18
|
+
<head>
|
19
|
+
<title></title>
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
Hello -name-,
|
23
|
+
<br /><br/>
|
24
|
+
I'm glad you are trying out the template feature!
|
25
|
+
<br /><br/>
|
26
|
+
<%body%>
|
27
|
+
<br /><br/>
|
28
|
+
I hope you are having a great day in -city- :)
|
29
|
+
<br /><br/>
|
30
|
+
</body>
|
31
|
+
</html>
|
32
|
+
```
|
33
|
+
|
34
|
+
## With Mail Helper Class
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'sendgrid-ruby'
|
38
|
+
include SendGrid
|
39
|
+
|
40
|
+
mail = SendGrid::Mail.new
|
41
|
+
mail.from = Email.new(email: 'test@example.com')
|
42
|
+
mail.subject = 'I\'m replacing the subject tag'
|
43
|
+
personalization = Personalization.new
|
44
|
+
personalization.add_to(Email.new(email: 'test@example.com'))
|
45
|
+
personalization.add_substitution(Substitution.new(key: '-name-', value: 'Example User'))
|
46
|
+
personalization.add_substitution(Substitution.new(key: '-city-', value: 'Denver'))
|
47
|
+
mail.add_personalization(personalization)
|
48
|
+
mail.template_id = '13b8f94f-bcae-4ec6-b752-70d6cb59f932'
|
49
|
+
|
50
|
+
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
|
51
|
+
begin
|
52
|
+
response = sg.client.mail._("send").post(request_body: mail.to_json)
|
53
|
+
rescue Exception => e
|
54
|
+
puts e.message
|
55
|
+
end
|
56
|
+
puts response.status_code
|
57
|
+
puts response.body
|
58
|
+
puts response.parsed_body
|
59
|
+
puts response.headers
|
60
|
+
```
|
61
|
+
|
62
|
+
## Without Mail Helper Class
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
require 'sendgrid-ruby'
|
66
|
+
include SendGrid
|
67
|
+
|
68
|
+
data = JSON.parse('{
|
69
|
+
"personalizations": [
|
70
|
+
{
|
71
|
+
"to": [
|
72
|
+
{
|
73
|
+
"email": "test@example.com"
|
74
|
+
}
|
75
|
+
],
|
76
|
+
"substitutions": {
|
77
|
+
"-name-": "Example User",
|
78
|
+
"-city-": "Denver"
|
79
|
+
},
|
80
|
+
"subject": "I\'m replacing the subject tag"
|
81
|
+
}
|
82
|
+
],
|
83
|
+
"from": {
|
84
|
+
"email": "test@example.com"
|
85
|
+
},
|
86
|
+
"template_id": "13b8f94f-bcae-4ec6-b752-70d6cb59f932"
|
87
|
+
}')
|
88
|
+
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
|
89
|
+
begin
|
90
|
+
response = sg.client.mail._("send").post(request_body: data)
|
91
|
+
rescue Exception => e
|
92
|
+
puts e.message
|
93
|
+
end
|
94
|
+
puts response.status_code
|
95
|
+
puts response.body
|
96
|
+
puts response.parsed_body
|
97
|
+
puts response.headers
|
98
|
+
```
|
data/use-cases/sms.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
First, follow the [Twilio Setup](twilio-setup.md) guide for creating a Twilio account and setting up environment variables with the proper credentials.
|
2
|
+
|
3
|
+
Then, install the Twilio Helper Library. Add this line to your application's Gemfile:
|
4
|
+
|
5
|
+
```bash
|
6
|
+
gem 'twilio-ruby'
|
7
|
+
```
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
```bash
|
12
|
+
bundle
|
13
|
+
```
|
14
|
+
|
15
|
+
Or install it yourself using:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
gem install twilio-ruby
|
19
|
+
```
|
20
|
+
|
21
|
+
Finally, send a message.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'twilio-ruby'
|
25
|
+
|
26
|
+
# put your own credentials here
|
27
|
+
account_sid = ENV['TWILIO_ACCOUNT_SID']
|
28
|
+
auth_token = ENV['TWILIO_AUTH_TOKEN']
|
29
|
+
|
30
|
+
# set up a client to talk to the Twilio REST API
|
31
|
+
@client = Twilio::REST::Client.new account_sid, auth_token
|
32
|
+
@client.api.account.messages.create(
|
33
|
+
from: '+14159341234',
|
34
|
+
to: '+16105557069',
|
35
|
+
body: 'Hey there!'
|
36
|
+
)
|
37
|
+
```
|
38
|
+
|
39
|
+
For more information, please visit the [Twilio SMS Ruby documentation](https://www.twilio.com/docs/sms/quickstart/ruby).
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# Transactional Templates
|
2
|
+
|
3
|
+
For this example, we assume you have created a [transactional template](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html) in the UI or via the API. Following is the template content we used for testing.
|
4
|
+
|
5
|
+
Template ID (replace with your own):
|
6
|
+
```text
|
7
|
+
d-2c214ac919e84170b21855cc129b4a5f
|
8
|
+
```
|
9
|
+
|
10
|
+
Email Subject:
|
11
|
+
```text
|
12
|
+
{{subject}}
|
13
|
+
```
|
14
|
+
|
15
|
+
Template Body:
|
16
|
+
```html
|
17
|
+
<html>
|
18
|
+
<head>
|
19
|
+
<title></title>
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
Hello {{name}},
|
23
|
+
<br/><br/>
|
24
|
+
I'm glad you are trying out the template feature!
|
25
|
+
<br/><br/>
|
26
|
+
I hope you are having a great day in {{city}} :)
|
27
|
+
<br/><br/>
|
28
|
+
</body>
|
29
|
+
</html>
|
30
|
+
```
|
31
|
+
|
32
|
+
## With Mail Helper Class
|
33
|
+
```ruby
|
34
|
+
require 'sendgrid-ruby'
|
35
|
+
include SendGrid
|
36
|
+
|
37
|
+
mail = Mail.new
|
38
|
+
mail.from = Email.new(email: 'test@example.com')
|
39
|
+
personalization = Personalization.new
|
40
|
+
personalization.add_to(Email.new(email: 'test@example.com'))
|
41
|
+
personalization.add_dynamic_template_data({
|
42
|
+
"subject" => "Testing Templates",
|
43
|
+
"name" => "Example User",
|
44
|
+
"city" => "Denver"
|
45
|
+
})
|
46
|
+
mail.add_personalization(personalization)
|
47
|
+
mail.template_id = 'd-2c214ac919e84170b21855cc129b4a5f'
|
48
|
+
|
49
|
+
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
|
50
|
+
begin
|
51
|
+
response = sg.client.mail._("send").post(request_body: mail.to_json)
|
52
|
+
rescue Exception => e
|
53
|
+
puts e.message
|
54
|
+
end
|
55
|
+
puts response.status_code
|
56
|
+
puts response.body
|
57
|
+
puts response.parsed_body
|
58
|
+
puts response.headers
|
59
|
+
```
|
60
|
+
|
61
|
+
## Without Mail Helper Class
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
require 'sendgrid-ruby'
|
65
|
+
include SendGrid
|
66
|
+
|
67
|
+
data = JSON.parse('{
|
68
|
+
"personalizations": [
|
69
|
+
{
|
70
|
+
"to": [
|
71
|
+
{
|
72
|
+
"email": "test@example.com"
|
73
|
+
}
|
74
|
+
],
|
75
|
+
"dynamic_template_data": {
|
76
|
+
"subject": "Testing Templates",
|
77
|
+
"name": "Example User",
|
78
|
+
"city": "Denver"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
],
|
82
|
+
"from": {
|
83
|
+
"email": "test@example.com"
|
84
|
+
},
|
85
|
+
"template_id": "d-2c214ac919e84170b21855cc129b4a5f"
|
86
|
+
}')
|
87
|
+
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
|
88
|
+
begin
|
89
|
+
response = sg.client.mail._("send").post(request_body: data)
|
90
|
+
rescue Exception => e
|
91
|
+
puts e.message
|
92
|
+
end
|
93
|
+
puts response.status_code
|
94
|
+
puts response.body
|
95
|
+
puts response.parsed_body
|
96
|
+
puts response.headers
|
97
|
+
```
|
98
|
+
|
99
|
+
## Adding Attachments
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
attachment = Attachment.new
|
103
|
+
attachment.content = Base64.strict_encode64(File.open(fpath, 'rb').read)
|
104
|
+
attachment.type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
105
|
+
attachment.filename = fname
|
106
|
+
attachment.disposition = 'attachment'
|
107
|
+
attachment.content_id = 'Reports Sheet'
|
108
|
+
mail.add_attachment(attachment)
|
109
|
+
```
|
110
|
+
|
111
|
+
Attachments must be base64 encoded, using Base64's strict_encode64 where no line feeds are added.
|