This 'wrapper gem' hooks a Ruby app (currently only tested with Rails) up to the DocuSign REST API to allow for embedded signing.
This gem is a fork of j2fly's version with a few modifications that make it easier to use API 'v2'.
Add this line to your application's Gemfile:
gem 'docusign_rest'
And then execute:
$ bundle
Or install it yourself as:
$ gem install docusign_rest
There is a bundled rake task that will prompt you for your DocuSign credentials including:
- Username
- Password
- Integrator Key
and create the config/initializers/docusign_rest.rb
file in your Rails app for you. If the file was unable to be created, the rake task will output the config block for you to manually add to an initializer.
Note please run the below task and ensure your initializer is in place before attempting to use any of the methods in this gem. Without the initializer this gem will not be able to properly authenticate you to the DocuSign REST API.
$ bundle exec rake docusign_rest:generate_config
outputs:
Please do the following:
------------------------
1) Login or register for an account at demo.docusign.net
...or their production url if applicable
2) Click 'Preferences' in the upper right corner of the page
3) Click 'API' in far lower left corner of the menu
4) Request a new 'Integrator Key' via the web interface
* You will use this key in one of the next steps to retrieve your 'accountId'
Please enter your DocuSign username: [email protected]
Please enter your DocuSign password: p@ssw0rd1
Please enter your DocuSign integrator_key: KEYS-19ddd1cc-cb56-4ca6-87ec-38db47d14b32
The following block of code was added to config/initializers/docusign_rest.rb
require 'docusign_rest'
DocusignRest.configure do |config|
config.username = '[email protected]'
config.password = 'p@ssw0rd1'
config.integrator_key = 'KEYS-19ddd1cc-cb56-4ca6-87ec-38db47d14b32'
config.account_id = '123456'
#config.endpoint = 'https://www.docusign.net/restapi'
#config.api_version = 'v1'
end
KK- if you wind up with an SSL error here in Ruby 1.9 / Rails, you will need to add and link to an SSL certificate within your project. Here are some instructions on how to get the certificate. Stick this code into an initializer.
require 'open-uri'
require 'net/https'
module Net
class HTTP
alias_method :original_use_ssl=, :use_ssl=
def use_ssl=(flag)
self.ca_file = File.join("#{Rails.root}", "lib", "ssl_certificates", "ca-bundle.crt")
self.verify_mode = OpenSSL::SSL::VERIFY_PEER
self.original_use_ssl = flag
end
end
end
There are several other configuration options available but the two most likely to be needed are:
config.endpoint = 'https://docusign.net/restapi'
config.api_version = 'v2'
The above options allow you to change the endpoint (to be able to hit the production DocuSign API, for instance) and to modify the API version you wish to use. If there is a big change in the API it's likely that this gem will need to be updated to leverage changes on the DocuSign side. However, it doesn't hurt to provide the option in case there are several minor updates that do not break functionality but would otherwise require a new gem release. These config options have existed since the gem was created, but in v0.0.3 and above, the options are auto-generated in the config file as comments to make them easier to discover.
KK- I've set the default to v2, since it corresponds better to the iodocs API explorer that Docusign has on their site.
The docusign_rest gem makes creating multipart POST (aka file upload) requests to the DocuSign REST API dead simple. It's built on top of Net:HTTP and utilizes the multipart-post gem to assist with formatting the multipart requests. The DocuSign REST API requires that all files be embedded as JSON directly in the request body (not the body_stream like multipart-post does by default) so the docusign_rest gem takes care of setting that up for you.
This gem also monkey patches one small part of multipart-post to inject some header values and formatting that DocuSign requires. If you would like to see the monkey patched code please take a look at lib/multipart-post/parts.rb. It's only re-opening one method, but feel free to make sure you understand that code if it concerns you.
- These examples assume you have already run the
docusign_rest:generate_config
rake task and have the configure block properly setup in an initializer with your username, password, integrator_key, and account_id. - Unless noted otherwise, these requests return the JSON parsed body of the response so you can index the returned hash directly. For example:
template_response["templateId"]
.
Getting account_id:
client = DocusignRest::Client.new
puts client.get_account_id
Creating an envelope from a document:
Note: In the example below there are two sign here tabs for the user with a role of 'Attorney'. There are also two documents attached to the envelope, however, this exact configuration would only allow for signature on the first document. If you need signature for a second document, you'll need to add further options, namely: document_id: 2
in one of the sign_here_tabs so that DocuSign knows where to embed that signature tab.
client = DocusignRest::Client.new
document_envelope_response = client.create_envelope_from_document(
email: {
subject: "test email subject",
body: "this is the email body and it's large!"
},
# If embedded is set to true in the signers array below, emails
# don't go out to the signers and you can embed the signature page in an
# iFrame by using the client.get_recipient_view method
signers: [
{
embedded: true,
name: 'Test Guy',
email: '[email protected]',
role_name: 'Issuer',
sign_here_tabs: [
{
anchor_string: 'sign_here_1',
anchor_x_offset: '140',
anchor_y_offset: '8'
}
]
},
{
embedded: true,
name: 'Test Girl',
email: '[email protected]',
role_name: 'Attorney',
sign_here_tabs: [
{
anchor_string: 'sign_here_2',
anchor_x_offset: '140',
anchor_y_offset: '8'
},
{
anchor_string: 'sign_here_3',
anchor_x_offset: '140',
anchor_y_offset: '8'
}
]
}
],
files: [
{path: '/Absolute/path/to/test.pdf', name: 'test.pdf'},
{path: '/Absolute/path/to/test2.pdf', name: 'test2.pdf'}
],
status: 'sent'
)
Creating a template:
client = DocusignRest::Client.new
@template_response = client.create_template(
description: 'Template Description',
name: "Template Name",
signers: [
{
embedded: true,
name: 'jon',
email: '[email protected]',
role_name: 'Issuer',
sign_here_tabs: [
{
anchor_string: 'issuer_sig',
anchor_x_offset: '140',
anchor_y_offset: '8'
}
]
},
{
embedded: true,
name: 'tim',
email: '[email protected]',
role_name: 'Attorney',
sign_here_tabs: [
{
anchor_string: 'attorney_sig',
anchor_x_offset: '140',
anchor_y_offset: '8'
}
]
}
],
files: [
{path: '/Absolute/path/to/test.pdf', name: 'test.pdf'}
]
)
Creating an envelope from a template:
KK: I've changed the code for this method. Now, submit your parameters in a hash that mirrors the formats shown in the iodocs documentation. You won't be able to underscore_case variables anymore since I've turned the translator off- use camelcase versions instead.
If you'd like the document to be embedded rather than mailed, you'll have to specify a client role as clientUserId.
Now you can fill in data tabs as well.
client = DocusignRest::Client.new
@envelope_response = client.create_envelope_from_template(
account_id: client.get_account_id,
status: 'sent',
emailBlurb: "Envelope body content here",
emailSubject: "The test email subject envelope",
templateId: "XXXX-XXXX-XXXX-XXXXXX",
templateRoles: [
{
clientUserId: '[email protected]',
name: 'jon',
email: '[email protected]',
roleName: 'Issuer',
tabs: {
textTabs: [
{
tabLabel: "amount",
name: "amount",
value: "$40,000"
}
]
}
}
]
)
Retrieving the url for embedded signing. (Returns a string, not a hash)
client = DocusignRest::Client.new
@url = client.get_recipient_view(
envelope_id: @envelope_response["envelopeId"],
name: current_user.full_name,
email: current_user.email,
return_url: 'http://google.com'
)
Check status of an envelope including the signers hash w/ the status of each signer
client = DocusignRest::Client.new
response = client.get_envelope_recipients(
envelope_id: @envelope_response["envelopeId"],
include_tabs: true,
include_extended: true
)
Retrieve a document from an envelope and store it at a local file path
client = DocusignRest::Client.new
client.get_document_from_envelope(
envelope_id: @envelope_response["envelopeId"],
document_id: 1,
local_save_path: "#{Rails.root.join('docusign_docs/file_name.pdf')}"
)
In order to return to your application after the signing process is complete it's important to have a way to evaluate whether or not the signing was successful and then do something about each case. The way I set this up was to render the embedded signing iframe for a controller action called 'embedded_signing' and specify the return_url of the client.get_recipient_view
API call to be something like: http://myapp.com/docusign_response. Then in the same controller as the embedded_signing method, define the docusign_response method. This is where the signing process will redirect to after the user is done interacting with the DocuSign iframe. DocuSign passes a query string parameter in the return_url named 'event' and you can check like so: if params[:event] == "signing_complete"
then you'll want to redirect to another spot in your app, not in the iframe. To do so, we need to use JavaScript to access the iframe's parent and set it's location to the path of our choosing. To do this, instanciate the DocusignRest::Utility class and call the breakout_path method like this:
class SomeController < ApplicationController
# the view corresponding to this action has the iFrame in it with the
# @url as it's src. @envelope_response is populated from either:
# @envelope_response = client.create_envelope_from_document
# or
# @envelope_response = client.create_envelope_from_template
def embedded_signing
client = DocusignRest::Client.new
@url = client.get_recipient_view(
envelope_id: @envelope_response["envelopeId"],
name: current_user.display_name,
email: current_user.email,
return_url: "http://localhost:3000/docusign_response"
)
end
def docusign_response
utility = DocusignRest::Utility.new
if params[:event] == "signing_complete"
flash[:notice] = "Thanks! Successfully signed"
render :text => utility.breakout_path(some_path), content_type: :html
else
flash[:notice] = "You chose not to sign the document."
render :text => utility.breakout_path(some_other_path), content_type: :html
end
end
end
KK- this utility doesn't seem to work with edge Rails. You can just close the iframe using the following:
render :inline => "<script>window.top.location.reload()</script>"
I'm not sure how live this is, but I'll be building out the API functionality over the coming months. Drop me a message and I'll grant you access.