Category Archives: Uncategorized

HOWTO: Integrate Adobe Sign API with Ruby on Rails

Adobe Sign is a product that allows companies to send documents to another individual or company to sign. This product is similar to Docusign and Hellosign. Most signing services provide APIs that allow developers to automate the signing process.

This document describes the steps involved to programmatically send a PDF document with dynamically-filled fields to a user to sign, and receive call-backs when the document is signed (or rejected).

  1. Signup for an Adobe Sign developer account at https://www.adobe.io/products/sign.html. Please note that they don’t let you register using the same email as an existing Adobe Sign user (Why? I don’t know..)

  2. Once registered and confirmed, you can goto Adobe Sign’s website, log in, and upload a PDF template into their document library. You can add data fields to various locations. Don’t forget to add a signature field and a signature date field.

  3. Before we can start the OAuth process, we need to create a “webhook” on your web application to receive POSTs from Adobe Sign. We need that during the OAuth process to read an OAuth code from them, and also during the signature process to receive call-back when a documents is signed. In Rails, you can do the following to create a simple webhook that prints out the parameters:

    routes.rb

    
      post '/sign_webhook' => 'adobe_sign#sign_webhook'
      get '/sign_webhook' => 'adobe_sign#sign_webhook'
    

    adobe_sign_controller.rb

    
      def sign_webhook
        puts "---------- sign webhook : params -------------------------"
        puts params
        puts "---------- sign webhook : request body -------------------"
        puts request.body.read
        puts "---------- sign webhook : end ----------------------------"
      end
    

    adobe_sign/sign_webhook.html.erb

    
      <%= debug(params) %>
    
  4. You can test out the webhook by checking: http://localhost:3000/sign_webhook?a=1&b=2. Push this to production. For the rest of the document, I am using “livio.herokuapp.com” as the name of our production machine. Make sure you have SSL enabled on your production server. Adobe Sign won’t allow callbacks to go to unsecured urls.

  5. We can begin the OAuth process to get access token. Here’s some doc provided by them, but it sure requires a lot of trial and error to get the whole workflow to work properly, as the documentation is not very easy to follow.

    https://www.adobe.io/products/sign/docs/overview

  6. Follow the steps described here to create an app for OAuth. Make sure redirect URL is specified. For this example, we are using https://livio.herokuapp.com/sign_webhook. Check all the boxes in the “Scopes” section and use “account” as modifier. To perform what we are trying to accomplish in this document, you will need the following scopes:

    
    user_login:account
    library_read:account
    agreement_send:account
    agreement_write:account
    agreement_read:account
    

    Take down the client ID and client Secret as we will need that in subsequent steps.

  7. We are ready to retrieve an access token to begin making API calls. Monitor your production server log to capture the parameters sent to the webhook. In this example, since we are using Heroku, we can simply run heroku logs --tail. Next, goto a web browser and type in the following URL. Make sure all your URL parameters are properly encoded.

    
    
    https://secure.na1.echosign.com/public/oauth?
    
    redirect_uri=https%3A%2F%2Flivio.herokuapp.com%2Fsign_webhook&
    response_type=code&
    client_id=[your_client_id]&
    scope=user_login%3Aaccount+library_read%3Aaccount+agreement_send%3Aaccount+agreement_write%3Aaccount+agreement_read%3Aaccount
    
  8. When you authenticate and grant access to the app, you should see the following in your production log:

    
    {"code": "[your_oauth_code]",
     "api_access_point": https://api.na1.echosign.com/",
     "web_access_point": https://secure.na1.echosign.com/"}
    

    Capture the “code” in the log file and use it in the following step.

  9. Use “curl” or other tools to make the following POST request. I like a Chrome plug-in called Postman as it is very easy to debug and the UI is very intuitive.

    
    POST: http://api.echosign.com/oauth/token?
       code=[your_oauth_code]&
       client_id=[your_client_id]&
       client_secret=[your_client_secret]&
       redirect_uri=https%3A%2F%2Flivio.herokuapp.com%2Fsign_webhook&
       grant_type=authorization_code
    

    Make sure you keep body empty when you make the POST request. You should get the following response:

    
    {
        "access_token": "[your_access_token]",
        "refresh_token": "[your_refresh_token]",
        "token_type": "Bearer",
        "expires_in": 3600
    }
    
  10. The access token is only valid for an hour. That won’t work for us since we will be making server side call non-deterministically. Luckily, the refresh token is valid forever, and we can use the refresh token to get a new access token. So before we send an agreement, we need to grab a new access token. Here’s some Ruby code to get a new access token:

    
      def self.adobe_sign_access_token
    
        require 'net/http'
        require 'json'
    
        refresh_token = "[your_refresh_token]"
        client_id = '[your_client_id]'
        client_secret = '[your_client_secret]'
    
        @host = 'api.na1.echosign.com'
        @port = '443'
        @path = "/oauth/refresh"
        @body = "refresh_token=#{refresh_token}&client_id=#{client_id}&client_secret=#{client_secret}&grant_type=refresh_token"
    
        request = Net::HTTP::Post.new(@path, initheader = {'Content-Type' => 'application/x-www-form-urlencoded'})
        request.body = @body
    
        http = Net::HTTP.new(@host, @port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    
        response = http.start { |http| http.request(request) }
    
        j_response = JSON.parse(response.body)
        if j_response.present?
          puts "[adobe_sign] Access Token = #{j_response["access_token"]}"
          return j_response["access_token"]
        end
    
        raise "Error getting Adobe Sign Refresh Token"
      end
    
  11. Before we can send an agreement to sign, we need to retrieve the template ID of the template we uploaded in an earlier step. We can do so by making the following GET request.

    
      def self.adobe_sign_get_templates
    
        @host = 'api.na1.echosign.com'
        @port = '443'
        @path = "/api/rest/v5/libraryDocuments"
    
        access_token = self.adobe_sign_refresh_token
        request = Net::HTTP::Get.new(@path, initheader = {"Access-Token" => access_token})
        request.body = @body
    
        http = Net::HTTP.new(@host, @port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    
        response = http.start { |http| http.request(request) }
    
        j_response = JSON.parse(response.body)
        ap j_response
    
      end
    
  12. Finally, we are all set to send an agreement. The following code shows how the form fields can be pre-filled. In this example, we assumed that there are two fields called “cost” and “vendor_name”, whose value will be passed into the method.

    
      def self.adobe_sign_send_agreement (email, cost, vendor_name)
    
        library_document_id = "[your_template_id]"
    
        # get access token
        access_token = self.adobe_sign_refresh_token
    
        @host = 'api.na1.echosign.com'
        @port = '443'
        @path = "/api/rest/v5/agreements"
    
        @body = {
            documentCreationInfo: {
                fileInfos: [{
                                libraryDocumentId: library_document_id
                            }],
                name: "Our First Agreement",
                recipientSetInfos: [{
                                        recipientSetMemberInfos: [{email: email}],
                                        recipientSetRole: "SIGNER"}],
                signatureType: "ESIGN",
                signatureFlow: "SENDER_SIGNATURE_NOT_REQUIRED",
                callbackInfo: "https://livio.herokuapp.com/sign_webhook",
                mergeFieldInfo: [
                    {defaultValue: cost, fieldName: "cost"},
                    {defaultValue: vendor_name, fieldName: "vendor_name"},
                ]
            }
        }.to_json
    
        request = Net::HTTP::Post.new(@path, initheader = {'Content-Type' => 'application/json', "Access-Token" => access_token})
        request.body = @body
    
        http = Net::HTTP.new(@host, @port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    
        response = http.start { |http| http.request(request) }
        j_response = JSON.parse(response.body)
    
        ap j_response
      end
    

    When you run the method, if everything goes well, you will get the following response with the agreement ID.

    
    {
      "agreementId": "3AAABLblqZhCE1yk2iufMtVoHyVtjrfMtCFLmYXXyaWP18_eNdnLq3_ifO2A2XUhRq5XiXA3PXwWgOdiPLtQGj3oFLuBgth62"
    }
    

    You probably want to store it in the database somewhere, and when the agreement is signed by the user, the webhook will get triggered and be able to update the status.

  13. The agreement is sent. When the recipient signs the doc, you will receive a POST to the webhook with the following parameters. Your webhook should grab the status and update your database accordingly.

    
    {"documentKey"=>"[agreement_id]", "eventType"=>"ESIGNED", "status"=>"SIGNED"}
    
  14. And that’s it! We’ve now sent an agreement out for signature with pre-filled form fields. When the agreement is signed, our web app receives a callback which could update the database or notify the administrator. Can you think of other use cases for Adobe Sign? Let me know!