Beispiel #1
0
def send():
    global ds_account_id, ds_signer1_email, ds_signer1_name, ds_cc1_email, ds_cc1_name
    msg = ds_recipe_lib.init(ds_user_email, ds_user_pw, ds_integration_id,
                             ds_account_id)
    if (msg != None):
        return {'ok': False, 'msg': msg}

    # Ready...
    # Possible create some fake people
    ds_signer1_email = ds_recipe_lib.get_signer_email(ds_signer1_email)
    ds_signer1_name = ds_recipe_lib.get_signer_name(ds_signer1_name)
    ds_cc1_email = ds_recipe_lib.get_signer_email(ds_cc1_email)
    ds_cc1_name = ds_recipe_lib.get_signer_name(ds_cc1_name)

    # STEP 1 - Login
    r = ds_recipe_lib.login()
    if (not r["ok"]):
        return r
    ds_account_id = ds_recipe_lib.ds_account_id

    #
    # STEP 2 - Create and send envelope with eventNotification
    #
    webhook_url = ds_recipe_lib.get_base_url() + webhook_path
    webhook_url = webhook_url.replace("http", "https")
    event_notification = {
        "url":
        webhook_url,
        "loggingEnabled":
        "true",  # The api wants strings for true/false
        "requireAcknowledgment":
        "true",
        "useSoapInterface":
        "false",
        "includeCertificateWithSoap":
        "false",
        "signMessageWithX509Cert":
        "false",
        "includeDocuments":
        "true",
        "includeEnvelopeVoidReason":
        "true",
        "includeTimeZone":
        "true",
        "includeSenderAccountAsCustomField":
        "true",
        "includeDocumentFields":
        "true",
        "includeCertificateOfCompletion":
        "true",
        "envelopeEvents": [  # for this recipe, we're requesting notifications
            # for all envelope and recipient events
            {
                "envelopeEventStatusCode": "sent"
            },
            {
                "envelopeEventStatusCode": "delivered"
            },
            {
                "envelopeEventStatusCode": "completed"
            },
            {
                "envelopeEventStatusCode": "declined"
            },
            {
                "envelopeEventStatusCode": "voided"
            }
        ],
        "recipientEvents": [{
            "recipientEventStatusCode": "Sent"
        }, {
            "recipientEventStatusCode": "Delivered"
        }, {
            "recipientEventStatusCode": "Completed"
        }, {
            "recipientEventStatusCode": "Declined"
        }, {
            "recipientEventStatusCode": "AuthenticationFailed"
        }, {
            "recipientEventStatusCode": "AutoResponded"
        }]
    }

    # construct the body of the request
    file_contents = open(doc_document_path, "rb").read()

    # Our goal: provide an email subject that is most meaningful to the recipients
    # The regex strips the 3 or 4 character extension from the filename.
    subject = "Please sign the " + re.sub('/\\.[^.\\s]{3,4}$/', '',
                                          doc_document_name) + " document"
    # File contents provided here instead of a multi-part request
    docs = [{
        "documentId": "1",
        "name": doc_document_name,
        "documentBase64": base64.b64encode(file_contents)
    }]

    signers = [{
        "email": ds_signer1_email,
        "name": ds_signer1_name,
        "recipientId": "1",
        "routingOrder": "1",
        "tabs": nda_fields()
    }]

    ccs = [{
        "email": ds_cc1_email,
        "name": ds_cc1_name,
        "recipientId": "2",
        "routingOrder": "2"
    }]

    data = {
        "emailSubject": subject,
        "documents": docs,
        "recipients": {
            "signers": signers,
            "carbonCopies": ccs
        },
        "eventNotification": event_notification,
        "status": "sent"
    }

    # append "/envelopes" to the baseUrl and use in the request
    url = ds_recipe_lib.ds_base_url + "/envelopes"
    try:
        r = requests.post(url, headers=ds_recipe_lib.ds_headers, json=data)
    except requests.exceptions.RequestException as e:
        return {
            'ok': False,
            'msg': "Error calling Envelopes:create: " + str(e)
        }

    status = r.status_code
    if (status != 201):
        return ({
            'ok':
            False,
            'msg':
            "Error calling DocuSign Envelopes:create, status is: " +
            str(status)
        })

    data = r.json()
    envelope_id = data['envelopeId']
    setup_output_dir(envelope_id)

    # Instructions for reading the email
    html =  "<h2>Signature request sent!</h2>" + \
            "<p>Envelope ID: " + envelope_id + "</p>" + \
            "<p>Signer: " + ds_signer1_name + "</p>" + \
            "<p>CC: " + ds_cc1_name + "</p>" + \
            "<h2>Next steps</h2>" + \
            "<h3>1. View the incoming notifications and documents</h3>" + \
            "<p><a href='" + ds_recipe_lib.get_base_url() + "/files/" + envelope_id_to_dir(envelope_id) + "'" + \
            "  class='btn btn-primary' role='button' target='_blank' style='margin-right:1.5em;'>" + \
            "View Notification Files</a> (A new tab/window will be used.)</p>" + \
            "<h3>2. Respond to the Signature Request</h3>"

    ds_signer1_email_access = ds_recipe_lib.get_temp_email_access(
        ds_signer1_email)
    if (ds_signer1_email_access):
        # A temp account was used for the email
        html += "<p>Respond to the request via your mobile phone by using the QR code: </p>" + \
                "<p>" + ds_recipe_lib.get_temp_email_access_qrcode(ds_signer1_email_access) + "</p>" + \
                "<p> or via <a target='_blank' href='" + ds_signer1_email_access + "'>your web browser.</a></p>"
    else:
        # A regular email account was used
        html += "<p>Respond to the request via your mobile phone or other mail tool.</p>" + \
                "<p>The email was sent to " + ds_signer1_name + " &lt;" + ds_signer1_email + "&gt;</p>"

    html += "<p>Webhook url: " + webhook_url + "</p>"

    return {
        "ok": True,
        "envelope_id": envelope_id,
        "ds_signer1_email": ds_signer1_email,
        "ds_signer1_name": ds_signer1_name,
        "ds_signer1_access": ds_signer1_email_access,
        "ds_signer1_qr": ds_signer1_email,
        "ds_cc1_email": ds_cc1_email,
        "ds_cc1_name": ds_cc1_name,
        "webhook_url": webhook_url,
        "html": html
    }
Beispiel #2
0
def list_all_unread():
    request_json = request.json
    print request_json
    global ds_account_id
    msg = ds_recipe_lib.init(ds_user_email, ds_user_pw, ds_integration_id,
                             ds_account_id)
    if (msg != None):
        return {'ok': False, 'msg': msg}

    r = ds_recipe_lib.login()
    if (not r["ok"]):
        return r
    else:
        print 'login successful'

    ds_account_id = ds_recipe_lib.ds_account_id
    action_conditions = request_json['queryResult']['action']
    if action_conditions == 'email.ask':
        api_response = requests.get(
            "https://demo.docusign.net/restapi/v2/accounts/" + ds_account_id +
            "/search_folders/awaiting_my_signature?order=desc",
            headers=ds_recipe_lib.ds_headers)
        # print api_response.text
        response_text = json.loads(api_response.text)
        print response_text
        pending_envelops = response_text[u'folderItems']
        pending_envelop_senders = [x["senderName"] for x in pending_envelops]
        fulfillmentText = ""
        for i, sender in enumerate(pending_envelop_senders):
            if i == 0:
                fulfillmentText += "You have one email from " + sender.split(
                )[0] + " "
            else:
                fulfillmentText += "one email from " + sender.split()[0] + " "

        response = {"fulfillmentText": fulfillmentText}

    elif action_conditions == 'email.from':  #read document of a particular person
        person_name = request_json['queryResult']['parameters'][
            'name']  #some name to be inputted from Nitesh
        api_response = requests.get(
            "https://demo.docusign.net/restapi/v2/accounts/" + ds_account_id +
            "/search_folders/awaiting_my_signature?order=desc",
            headers=ds_recipe_lib.ds_headers)
        # print api_response.text
        response_text = json.loads(api_response.text)
        print response_text, 'ddddddddddddddddddddddddd'
        pending_envelops = response_text[u'folderItems']
        pending_envelop_senders = [x["senderName"] for x in pending_envelops]
        envelop_ids = [x["envelopeId"] for x in pending_envelops]
        fulfillmentText = ""
        for i, sender in enumerate(pending_envelop_senders):
            if person_name in sender:
                new_api_response = json.loads(
                    requests.get(
                        "https://demo.docusign.net/restapi/v2/accounts/" +
                        ds_account_id + "/envelopes/" + envelop_ids[i] +
                        "/documents",
                        headers=ds_recipe_lib.ds_headers).text)
                print new_api_response
                documents = new_api_response['envelopeDocuments']
                final_documents = []
                for d in documents:
                    if d['documentId'] != 'certificate':
                        final_documents.append(d)

                response = {
                    "fulfillmentText":
                    "There are " + str(len(final_documents)) +
                    " documents to read"
                }
                break

    elif action_conditions == 'read.text':
        index_of_document = str(
            int(request_json['queryResult']['parameters']['number']))
        person_name = request_json['queryResult']['parameters'][
            'name']  #some name to be inputted from Nitesh
        api_response = requests.get(
            "https://demo.docusign.net/restapi/v2/accounts/" + ds_account_id +
            "/search_folders/awaiting_my_signature?order=desc",
            headers=ds_recipe_lib.ds_headers)
        # print api_response.text
        response_text = json.loads(api_response.text)
        print response_text, 'ddddddddddddddddddddddddd'
        pending_envelops = response_text[u'folderItems']
        pending_envelop_senders = [x["senderName"] for x in pending_envelops]
        envelop_ids = [x["envelopeId"] for x in pending_envelops]
        fulfillmentText = ""
        for i, sender in enumerate(pending_envelop_senders):
            if person_name in sender:
                new_api_response = json.loads(
                    requests.get(
                        "https://demo.docusign.net/restapi/v2/accounts/" +
                        ds_account_id + "/envelopes/" + envelop_ids[i] +
                        "/documents",
                        headers=ds_recipe_lib.ds_headers).text)
                print new_api_response
                documents = new_api_response['envelopeDocuments']
                doc_to_consider = [
                    x for x in documents if x['order'] == index_of_document
                ][0]
                pdf_file = requests.get(
                    "https://demo.docusign.net/restapi/v2/accounts/" +
                    ds_account_id + "/envelopes/" + envelop_ids[i] +
                    "/documents/" + doc_to_consider['order'],
                    headers=ds_recipe_lib.ds_headers)
                with open('pdf_file.pdf', 'wb') as f:
                    f.write(pdf_file.content)

                pdfReaderObject = openFile("pdf_file.pdf")
                resumeText = entirePdfToText(pdfReaderObject)
                summary = summarize(resumeText, ratio=0.2, split=False)
                summary = summary.replace("\n", " ")
                response = {"fulfillmentText": summary}

    elif action_conditions == 'agreed':
        index_of_document = str(
            int(request_json['queryResult']['parameters']['number']))
        person_name = request_json['queryResult']['parameters'][
            'name']  #some name to be inputted from Nitesh
        api_response = requests.get(
            "https://demo.docusign.net/restapi/v2/accounts/" + ds_account_id +
            "/search_folders/awaiting_my_signature?order=desc",
            headers=ds_recipe_lib.ds_headers)
        # print api_response.text
        response_text = json.loads(api_response.text)
        print response_text, 'ddddddddddddddddddddddddd'
        pending_envelops = response_text[u'folderItems']
        pending_envelop_senders = [x["senderName"] for x in pending_envelops]
        envelop_ids = [x["envelopeId"] for x in pending_envelops]
        fulfillmentText = ""
        for i, sender in enumerate(pending_envelop_senders):
            if person_name in sender:
                new_api_response = json.loads(
                    requests.get(
                        "https://demo.docusign.net/restapi/v2/accounts/" +
                        ds_account_id + "/envelopes/" + envelop_ids[i] +
                        "/documents",
                        headers=ds_recipe_lib.ds_headers).text)
                print new_api_response
                documents = new_api_response['envelopeDocuments']
                doc_to_consider = [
                    x for x in documents if x['order'] == index_of_document
                ][0]
                envelope_created_resp = py_010_webhook_lib.create_envelope()
                print envelope_created_resp
                final_req_data = {
                    "userName": envelope_created_resp["ds_signer1_name"],
                    "email": envelope_created_resp["ds_signer1_email"],
                    "recipientId": "1",
                    "clientUserId": "1234",
                    "authenticationMethod": "email",
                    "returnUrl": "https://www.docusign.com/devcenter"
                }

                response = requests.post(
                    "https://demo.docusign.net/restapi/v2/accounts/" +
                    ds_account_id + "/envelopes/" +
                    envelope_created_resp["envelope_id"] + "/views/recipient",
                    headers=ds_recipe_lib.ds_headers,
                    json=final_req_data).text

                print response, 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
                response_url = json.loads(response)['url']

                options = Options()
                options.add_argument("--headless")
                driver = webdriver.Firefox(
                    firefox_options=options,
                    executable_path=os.path.dirname(os.getcwd()) +
                    "/geckodriver")
                driver.get(response_url)
                time.sleep(10)
                driver.find_element_by_id('action-bar-btn-continue').click()
                driver.find_element_by_class_name(
                    'signature-tab-content').click()
                driver.find_element_by_id('action-bar-btn-finish').click()
                driver.quit()
                response = {}

    else:
        response = {}

    print response, 'ssssssssssssssssssssssssssssssssssss'
    response = app.response_class(response=json.dumps(response),
                                  status=200,
                                  mimetype='application/json')
    return response
Beispiel #3
0
def send():
    global ds_account_id, ds_signer1_email, ds_signer1_name, \
        ds_cc1_email, ds_cc1_name
    msg = ds_recipe_lib.init(ds_user_email, ds_user_pw, ds_integration_id,
                             ds_account_id)
    if msg != None:
        return {'ok': False, 'msg': msg}

    # Ready...
    # Possibly create some fake people
    # ds_signer1_email = ds_recipe_lib.get_signer_email(ds_signer1_email)
    # ds_signer1_name = ds_recipe_lib.get_signer_name(ds_signer1_name)
    # ds_cc1_email = ds_recipe_lib.get_signer_email(ds_cc1_email)
    # ds_cc1_name = ds_recipe_lib.get_signer_name(ds_cc1_name)

    ds_signer1_email = '*****@*****.**'
    ds_signer1_name = 'Signer 1'
    ds_signer2_email = '*****@*****.**'
    ds_signer2_name = 'Signer 2'
    ds_cc1_email = '*****@*****.**'
    ds_cc1_name = 'Carbon Copy'

    # STEP 1 - Login

    r = ds_recipe_lib.login()
    if not r['ok']:
        return r
    ds_account_id = ds_recipe_lib.ds_account_id

    #
    # STEP 2 - Create and send envelope
    #

    # construct the body of the request

    file_contents = open(doc_document_path, 'rb').read()
    file2_contents = open(doc2_document_path, 'rb').read()
    file3_contents = open(doc3_document_path, 'rb').read()

    # Please use the most accurate and relevant subject line.

    subject = 'Please sign the house documentation package'

    # File contents are provided here
    # The documents array can include multiple documents, of differing types.
    # All documents are converted to pdf prior to signing.
    # The fileExtension field defaults to "pdf".

    documents1 = [{
        'documentId': '1',
        'name': doc_document_name,
        'fileExtension': (os.path.splitext(doc_document_path)[1])[1:],
        'documentBase64': base64.b64encode(file_contents),
    }, {
        'documentId':
        '2',
        'name':
        doc2_document_name,
        'fileExtension': (os.path.splitext(doc2_document_path)[1])[1:],
        'documentBase64':
        base64.b64encode(file2_contents),
    }]

    documents2 = [{
        'documentId': '1',
        'name': doc_document_name,
        'fileExtension': (os.path.splitext(doc_document_path)[1])[1:],
        'documentBase64': base64.b64encode(file_contents),
    }, {
        'documentId':
        '3',
        'name':
        doc3_document_name,
        'fileExtension': (os.path.splitext(doc3_document_path)[1])[1:],
        'documentBase64':
        base64.b64encode(file3_contents),
    }]

    # The signing fields
    #
    # Invisible (white) Anchor field names for the NDA.pdf document:
    #   * signer1sig
    #   * signer1name
    #   * signer1company
    #   * signer1date
    #
    # Explicitly placed fields are used in the contractor_agreement
    # and on the house diagram
    #
    # Some anchor fields for document 3, the contractor_agreement.docx, use existing
    # content from the document:
    #   * "Client Signature"
    #   * "Client Name"
    #
    # NOTE: Anchor fields search ALL the documents in the envelope for
    # matches to the field's anchor text
    fields1 = {
        "signHereTabs": [
            {
                "anchorString": "signer1sig",  # Anchored for doc 1
                "anchorXOffset": "0",
                "anchorYOffset": "0",
                "anchorUnits": "mms",
                "recipientId": "1",
                "name": "Please sign here",
                "optional": "false",
                "scaleValue": 1,
                "tabLabel": "signer1sig"
            },
            {
                "documentId": "2",  # Explicit position for doc 2
                "pageNumber": "1",
                "recipientId": "2",
                "xPosition": "89",
                "yPosition": "40",
                "name": "Please sign here",
                "optional": "false",
                "scaleValue": 1,
                "tabLabel": "signer1_doc2"
            }
        ],
        "fullNameTabs": [{
            "anchorString": "signer1name",  # Anchored for doc 1
            "anchorYOffset": "-6",
            "fontSize": "Size12",
            "recipientId": "1",
            "tabLabel": "Full Name",
            "name": "Full Name"
        }],
        "textTabs": [
            {
                "anchorString": "signer1company",  # Anchored for doc 1
                "anchorYOffset": "-8",
                "fontSize": "Size12",
                "recipientId": "1",  # Because the same tab label is 
                "tabLabel":
                "Company",  # used, these fields will have duplicate data
                "name":
                "Company",  # Note that the account's "Data Population Scope"
                "required": "true"
            },  # must be set to "Envelope" to enable this feature.
        ],
        "dateSignedTabs": [
            {
                "anchorString": "signer1date",  # Anchored for doc 1
                "anchorYOffset": "-6",
                "fontSize": "Size12",
                "recipientId": "1",
                "name": "Date Signed",
                "tabLabel": "date_signed"
            },
            {
                "documentId": "2",  # Explicit position for doc 2
                "pageNumber": "1",
                "recipientId": "1",
                "xPosition": "89",
                "yPosition": "100",
                "fontSize": "Size12",
                "recipientId": "1",
                "name": "Date Signed",
                "tabLabel": "doc3_date_signed"
            }
        ]
    }

    fields2 = {
        "signHereTabs": [
            {
                "anchorString": "signer1sig",  # Anchored for doc 1
                "anchorXOffset": "0",
                "anchorYOffset": "0",
                "anchorUnits": "mms",
                "recipientId": "1",
                "name": "Please sign here",
                "optional": "false",
                "scaleValue": 1,
                "tabLabel": "signer1sig"
            },
            {
                "anchorString": "Client Signature",  # Anchored for doc 3
                "anchorXOffset": "0",
                "anchorYOffset": "-4",
                "anchorUnits": "mms",
                "recipientId": "1",
                "name": "Please sign here",
                "optional": "false",
                "scaleValue": 1,
                "tabLabel": "doc3_client_sig"
            }
        ],
        "fullNameTabs": [{
            "anchorString": "signer1name",  # Anchored for doc 1
            "anchorYOffset": "-6",
            "fontSize": "Size12",
            "recipientId": "1",
            "tabLabel": "Full Name",
            "name": "Full Name"
        }],
        "textTabs": [
            {
                "anchorString": "signer1company",  # Anchored for doc 1
                "anchorYOffset": "-8",
                "fontSize": "Size12",
                "recipientId": "1",  # Because the same tab label is 
                "tabLabel":
                "Company",  # used, these fields will have duplicate data
                "name":
                "Company",  # Note that the account's "Data Population Scope"
                "required": "true"
            },  # must be set to "Envelope" to enable this feature.
            {
                "anchorString": "Client Name",  # Anchored for doc 3
                "anchorYOffset": "-38",
                "fontSize": "Size12",
                "recipientId": "1",
                "tabLabel": "Company",
                "name": "Company",
                "required": "true"
            },
            {
                "documentId": "3",  # Explicit position for doc 3
                "pageNumber": "1",
                "recipientId": "1",
                "xPosition": "145",
                "yPosition": "195",
                "fontSize": "Size10",
                "required": "true",
                "tabLabel": "Company",
                "name": "Company"
            }
        ],
        "dateSignedTabs": [{
            "anchorString": "signer1date",  # Anchored for doc 1
            "anchorYOffset": "-6",
            "fontSize": "Size12",
            "recipientId": "1",
            "name": "Date Signed",
            "tabLabel": "date_signed"
        }]
    }

    signers1 = [{
        'email': ds_signer1_email,
        'name': ds_signer1_name,
        'recipientId': '1',
        'routingOrder': '1',
        'tabs': fields1,
    }]

    signers2 = [{
        'email': ds_signer2_email,
        'name': ds_signer2_name,
        'recipientId': '2',
        'routingOrder': '2',
        'tabs': fields2,
    }]

    ccs = [{
        'email': ds_cc1_email,
        'name': ds_cc1_name,
        'recipientId': '3',
        'routingOrder': '3',
    }]

    data1 = {
        'emailSubject': subject,
        'documents': documents1,
        'recipients': {
            'signers': signers1,
            'carbonCopies': ccs
        },
        'status': 'sent',
    }

    data2 = {
        'emailSubject': subject,
        'documents': documents2,
        'recipients': {
            'signers': signers2,
            'carbonCopies': ccs
        },
        'status': 'sent',
    }

    # append "/envelopes" to the baseUrl and use in the request

    url = ds_recipe_lib.ds_base_url + '/envelopes'
    try:
        r = requests.post(url, headers=ds_recipe_lib.ds_headers, json=data1)
    except requests.exceptions.RequestException, e:
        return {'ok': False, 'msg': 'Error calling Envelopes:create: ' \
                + str(e)}
def create_envelope():
    global ds_account_id, ds_signer1_email, ds_signer1_name, ds_cc1_email, ds_cc1_name
    msg = ds_recipe_lib.init(ds_user_email, ds_user_pw, ds_integration_id, ds_account_id)
    if (msg != None):
        return {'ok': False, 'msg': msg}

    # Ready...
    # Possible create some fake people
    ds_signer1_email = "*****@*****.**" #ds_recipe_lib.get_signer_email(ds_signer1_email)
    ds_signer1_name = ds_recipe_lib.get_signer_name(ds_signer1_name)
    ds_cc1_email = ds_recipe_lib.get_signer_email(ds_cc1_email)
    ds_cc1_name = ds_recipe_lib.get_signer_name(ds_cc1_name)

    # STEP 1 - Login
    r = ds_recipe_lib.login()
    if (not r["ok"]):
        return r
    ds_account_id = ds_recipe_lib.ds_account_id    

    #
    # STEP 2 - Create and send envelope with eventNotification
    #
    webhook_url = ds_recipe_lib.get_base_url() + webhook_path
    event_notification = {"url": webhook_url,
        "loggingEnabled": "true", # The api wants strings for true/false
        "requireAcknowledgment": "true",
        "useSoapInterface": "false",
        "includeCertificateWithSoap": "false",
        "signMessageWithX509Cert": "false",
        "includeDocuments": "true",
        "includeEnvelopeVoidReason": "true",
        "includeTimeZone": "true",
        "includeSenderAccountAsCustomField": "true",
        "includeDocumentFields": "true",
        "includeCertificateOfCompletion": "true",
        "envelopeEvents": [ # for this recipe, we're requesting notifications
            # for all envelope and recipient events
            {"envelopeEventStatusCode": "sent"},
              {"envelopeEventStatusCode": "delivered"},
              {"envelopeEventStatusCode": "completed"},
            {"envelopeEventStatusCode": "declined"},
            {"envelopeEventStatusCode": "voided"}],
        "recipientEvents": [
            {"recipientEventStatusCode": "Sent"},
            {"recipientEventStatusCode": "Delivered"},
            {"recipientEventStatusCode": "Completed"},
            {"recipientEventStatusCode": "Declined"},
            {"recipientEventStatusCode": "AuthenticationFailed"},
            {"recipientEventStatusCode": "AutoResponded"}]
    }

    # construct the body of the request
    file_contents = open(doc_document_path, "rb").read()

    # Our goal: provide an email subject that is most meaningful to the recipients
    # The regex strips the 3 or 4 character extension from the filename.
    subject = "Please sign the " + re.sub('/\\.[^.\\s]{3,4}$/', '', doc_document_name) + " document"
    # File contents provided here instead of a multi-part request
    docs = [{"documentId": "1", 
            "name": doc_document_name,
            "documentBase64": base64.b64encode(file_contents)}]
    
    signers = [{"email": ds_signer1_email,
                "name": ds_signer1_name,
                "recipientId": "1",
                "clientUserId":"1234",
                "routingOrder": "1",
                "tabs": nda_fields()}]
    
    data = {"emailSubject": subject,
            "documents": docs, 
            "recipients": {"signers": signers},
            "eventNotification": event_notification,
            "status": "sent"
    }
        
    # append "/envelopes" to the baseUrl and use in the request
    url = ds_recipe_lib.ds_base_url + "/envelopes"
    try:
        r = requests.post(url, headers=ds_recipe_lib.ds_headers, json=data)
    except requests.exceptions.RequestException as e:
        return {'ok': False, 'msg': "Error calling Envelopes:create: " + str(e)}
        
    status = r.status_code
    if (status != 201): 
        return ({'ok': False, 'msg': "Error calling DocuSign Envelopes:create, status is: " + str(status)})

    data = r.json()
    envelope_id = data['envelopeId']
    setup_output_dir(envelope_id)
    
    ds_signer1_email_access = ds_recipe_lib.get_temp_email_access(ds_signer1_email)
    return {"ok": True,
        "envelope_id": envelope_id,
        "ds_signer1_email": ds_signer1_email,
        "ds_signer1_name": ds_signer1_name,
        "ds_signer1_access": ds_signer1_email_access,
        "ds_signer1_qr": ds_signer1_email,
        "ds_cc1_email": ds_cc1_email,
        "ds_cc1_name": ds_cc1_name,
        "webhook_url": webhook_url
    }