Related
I want to create templates for document signing using docusign in PHP. It seems their API is not fully detail.
Docusign for template creation
I have followed their instructions and have obtained my access token, base_url etc via cURL in PHP.
Here is the PHP code they provide without further details. No PHP SDK and how to use it. I couldn't find were to use my access_token, base_url etc as per documentation.
private function make_template_req(): EnvelopeTemplate
{
$doc_file = 'World_Wide_Corp_fields.pdf';
$content_bytes = file_get_contents(self::DEMO_DOCS_PATH . $doc_file);
$base64_file_content = base64_encode($content_bytes);
# Create the document model
$document = new Document([ # create the DocuSign document object
'document_base64' => $base64_file_content,
'name' => 'Lorem Ipsum', # can be different from actual file name
'file_extension' => 'pdf', # many different document types are accepted
'document_id' => '1' # a label used to reference the doc
]);
# Create the signer recipient model
# Since these are role definitions, no name/email:
$signer = new Signer([
'role_name' => 'signer', 'recipient_id' => "1", 'routing_order' => "1"]);
# create a cc recipient to receive a copy of the documents
$cc = new CarbonCopy([
'role_name' => 'cc', 'recipient_id' => "2", 'routing_order' => "2"]);
# Create fields using absolute positioning
# Create a sign_here tab (field on the document)
$sign_here = new SignHere(['document_id' => '1', 'page_number' => '1',
'x_position' => '191', 'y_position' => '148']);
$check1 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '417', 'tab_label' => 'ckAuthorization']);
$check2 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '447', 'tab_label' => 'ckAuthentication']);
$check3 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '478', 'tab_label' => 'ckAgreement']);
$check4 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '508', 'tab_label' => 'ckAcknowledgement']);
$list1 = new ModelList([
'font' => "helvetica",
'font_size' => "size11",
'anchor_string' => '/l1q/',
'anchor_y_offset' => '-10', 'anchor_units' => 'pixels',
'anchor_x_offset' => '0',
'list_items' => [
['text' => "Red" , 'value' => "red" ], ['text' => "Orange", 'value' => "orange"],
['text' => "Yellow", 'value' => "yellow"], ['text' => "Green" , 'value' => "green" ],
['text' => "Blue" , 'value' => "blue" ], ['text' => "Indigo", 'value' => "indigo"]
],
'required' => "true",
'tab_label' => "l1q"
]);
$number1 = new Number(['document_id' => "1", 'page_number' => "1",
'x_position' => "163", 'y_position' => "260",
'font' => "helvetica", 'font_size' => "size14", 'tab_label' => "numbersOnly",
'width' => "84", 'required' => "false"]);
$radio_group = new RadioGroup(['document_id' => "1", 'group_name' => "radio1",
'radios' => [
new Radio(['page_number' => "1", 'x_position' => "142", 'y_position' => "384",
'value' => "white", 'required' => "false"]),
new Radio(['page_number' => "1", 'x_position' => "74", 'y_position' => "384",
'value' => "red", 'required' => "false"]),
new Radio(['page_number' => "1", 'x_position' => "220", 'y_position' => "384",
'value' => "blue", 'required' => "false"])
]]);
$text = new Text(['document_id' => "1", 'page_number' => "1",
'x_position' => "153", 'y_position' => "230",
'font' => "helvetica", 'font_size' => "size14", 'tab_label' => "text",
'height' => "23", 'width' => "84", 'required' => "false"]);
# Add the tabs model to the signer
# The Tabs object wants arrays of the different field/tab types
$signer->setTabs(new Tabs(['sign_here_tabs' => [$sign_here],
'checkbox_tabs' => [$check1, $check2, $check3, $check4], 'list_tabs' => [$list1],
'number_tabs' => [$number1], 'radio_group_tabs' => [$radio_group], 'text_tabs' => [$text]
]));
# Template object:
$template_request = new EnvelopeTemplate([
'description' => "Example template created via the API",
'name' => $this->template_name,
'shared' => "false",
'documents' => [$document], 'email_subject' => "Please sign this document",
'recipients' => new Recipients([
'signers' => [$signer], 'carbon_copies' => [$cc]]),
'status' => "created"
]);
return $template_request;
}
$results = $templates_api->createTemplate($args['account_id'], $template_req_object);
I decided to use cURL but they provide Bash code in Step 2 (Create Template) and cURL code in Step 3 (Call the eSignature REST API) which is confusing.
I decide to combine Bash Code and Curl code in php to see if it will work but it throws error.
$access_token='access_token goes here';
$base_uri='my base url goes here';
$account_id ='my account id goes here';
$data_param=
'{
"description": "Example template created via the API",
"name": "Example Signer and CC template",
"shared": "false",
"documents": [
{
"documentBase64": "' > $request_data
cat $doc1_base64 >> $request_data
printf '",
"documentId": "1", "fileExtension": "pdf",
"name": "Lorem Ipsum"
}
],
"emailSubject": "Please sign this document",
"recipients": {
"carbonCopies": [
{"recipientId": "2", "roleName": "cc", "routingOrder": "2"}
],
"signers": [
{
"recipientId": "1", "roleName": "signer", "routingOrder": "1",
"tabs": {
"checkboxTabs": [
{
"documentId": "1", "pageNumber": "1",
"tabLabel": "ckAuthorization", "xPosition": "75",
"yPosition": "417"
},
{
"documentId": "1", "pageNumber": "1",
"tabLabel": "ckAuthentication", "xPosition": "75",
"yPosition": "447"
},
{
"documentId": "1", "pageNumber": "1",
"tabLabel": "ckAgreement", "xPosition": "75",
"yPosition": "478"
},
{
"documentId": "1", "pageNumber": "1",
"tabLabel": "ckAcknowledgement", "xPosition": "75",
"yPosition": "508"
}
],
"listTabs": [
{
"documentId": "1", "font": "helvetica",
"fontSize": "size14",
"listItems": [
{"text": "Red", "value": "red"},
{"text": "Orange", "value": "orange"},
{"text": "Yellow", "value": "yellow"},
{"text": "Green", "value": "green"},
{"text": "Blue", "value": "blue"},
{"text": "Indigo", "value": "indigo"},
{"text": "Violet", "value": "violet"}
],
"pageNumber": "1", "required": "false",
"tabLabel": "list", "xPosition": "142",
"yPosition": "291"
}
],
"radioGroupTabs": [
{
"documentId": "1", "groupName": "radio1",
"radios": [
{
"pageNumber": "1", "required": "false",
"value": "white", "xPosition": "142",
"yPosition": "384"
},
{
"pageNumber": "1", "required": "false",
"value": "red", "xPosition": "74",
"yPosition": "384"
},
{
"pageNumber": "1", "required": "false",
"value": "blue", "xPosition": "220",
"yPosition": "384"
}
]
}
],
"signHereTabs": [
{
"documentId": "1", "pageNumber": "1",
"xPosition": "191", "yPosition": "148"
}
],
"textTabs": [
{
"documentId": "1", "font": "helvetica",
"fontSize": "size14", "height": 23,
"pageNumber": "1", "required": "false",
"tabLabel": "text", "width": 84,
"xPosition": "153", "yPosition": "230"
},
{
"documentId": "1", "font": "helvetica",
"fontSize": "size14", "height": 23,
"pageNumber": "1", "required": "false",
"tabLabel": "numbersOnly", "width": 84,
"xPosition": "153", "yPosition": "260"
}
]
}
}
]
},
"status": "created"
}';
$url ="$base_uri/v2.1/accounts/$account_id/templates";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', "Authorization: Bearer $access_token"));
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_param);
//curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$output = curl_exec($ch);
echo $output;
Updated part of the code
After installing PHP SDK and run my sample code below, it throws error
<?php
require_once('vendor/autoload.php');
$access_token='my token goes here';
$base_path='https://demo.docusign.net';
$account_id ='my account id goes here';
// You will need to obtain an access token using your chosen authentication flow
$api_client = new \DocuSign\eSign\client\ApiClient($base_path);
$config = new \DocuSign\eSign\Model\Configuration($api_client);
$config->addDefaultHeader('Authorization', 'Bearer ' + $access_token);
$users_api = new \DocuSign\eSign\Api\UsersApi($api_client);
?>
Error:
Fatal error: Uncaught TypeError: DocuSign\eSign\Client\ApiClient::__construct(): Argument #1 ($config) must be of type ?DocuSign\eSign\Configuration, string given, called in C:\xampp\htdocs\docusign-esign\index.php on line 9 and defined in C:\xampp\htdocs\docusign-esign\vendor\docusign\esign-client\src\Client\ApiClient.php:91 Stack trace: #0 C:\xampp\htdocs\docusign-esign\index.php(9): DocuSign\eSign\Client\ApiClient->__construct('https://demo.do...') #1 {main} thrown in C:\xampp\htdocs\docusign-esign\vendor\docusign\esign-client\src\Client\ApiClient.php on line 91
When I run the entire code as per below, it throws error
<?php
require_once('vendor/autoload.php');
$access_token='my access token';
$base_path='https://demo.docusign.net';
$account_id ='my account id goes here';
private function make_template_req(): EnvelopeTemplate
{
// You will need to obtain an access token using your chosen authentication flow
$api_client = new \DocuSign\eSign\client\ApiClient($base_path);
$config = new \DocuSign\eSign\Model\Configuration($api_client);
$config->addDefaultHeader('Authorization', 'Bearer ' + $access_token);
$users_api = new \DocuSign\eSign\Api\UsersApi($api_client);
$doc_file = 'World_Wide_Corp_fields.pdf';
$content_bytes = file_get_contents(self::DEMO_DOCS_PATH . $doc_file);
$base64_file_content = base64_encode($content_bytes);
# Create the document model
$document = new Document([ # create the DocuSign document object
'document_base64' => $base64_file_content,
'name' => 'Lorem Ipsum', # can be different from actual file name
'file_extension' => 'pdf', # many different document types are accepted
'document_id' => '1' # a label used to reference the doc
]);
# Create the signer recipient model
# Since these are role definitions, no name/email:
$signer = new Signer([
'role_name' => 'signer', 'recipient_id' => "1", 'routing_order' => "1"]);
# create a cc recipient to receive a copy of the documents
$cc = new CarbonCopy([
'role_name' => 'cc', 'recipient_id' => "2", 'routing_order' => "2"]);
# Create fields using absolute positioning
# Create a sign_here tab (field on the document)
$sign_here = new SignHere(['document_id' => '1', 'page_number' => '1',
'x_position' => '191', 'y_position' => '148']);
$check1 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '417', 'tab_label' => 'ckAuthorization']);
$check2 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '447', 'tab_label' => 'ckAuthentication']);
$check3 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '478', 'tab_label' => 'ckAgreement']);
$check4 = new Checkbox(['document_id' => '1', 'page_number' => '1',
'x_position' => '75', 'y_position' => '508', 'tab_label' => 'ckAcknowledgement']);
$list1 = new ModelList([
'font' => "helvetica",
'font_size' => "size11",
'anchor_string' => '/l1q/',
'anchor_y_offset' => '-10', 'anchor_units' => 'pixels',
'anchor_x_offset' => '0',
'list_items' => [
['text' => "Red" , 'value' => "red" ], ['text' => "Orange", 'value' => "orange"],
['text' => "Yellow", 'value' => "yellow"], ['text' => "Green" , 'value' => "green" ],
['text' => "Blue" , 'value' => "blue" ], ['text' => "Indigo", 'value' => "indigo"]
],
'required' => "true",
'tab_label' => "l1q"
]);
$number1 = new Number(['document_id' => "1", 'page_number' => "1",
'x_position' => "163", 'y_position' => "260",
'font' => "helvetica", 'font_size' => "size14", 'tab_label' => "numbersOnly",
'width' => "84", 'required' => "false"]);
$radio_group = new RadioGroup(['document_id' => "1", 'group_name' => "radio1",
'radios' => [
new Radio(['page_number' => "1", 'x_position' => "142", 'y_position' => "384",
'value' => "white", 'required' => "false"]),
new Radio(['page_number' => "1", 'x_position' => "74", 'y_position' => "384",
'value' => "red", 'required' => "false"]),
new Radio(['page_number' => "1", 'x_position' => "220", 'y_position' => "384",
'value' => "blue", 'required' => "false"])
]]);
$text = new Text(['document_id' => "1", 'page_number' => "1",
'x_position' => "153", 'y_position' => "230",
'font' => "helvetica", 'font_size' => "size14", 'tab_label' => "text",
'height' => "23", 'width' => "84", 'required' => "false"]);
# Add the tabs model to the signer
# The Tabs object wants arrays of the different field/tab types
$signer->setTabs(new Tabs(['sign_here_tabs' => [$sign_here],
'checkbox_tabs' => [$check1, $check2, $check3, $check4], 'list_tabs' => [$list1],
'number_tabs' => [$number1], 'radio_group_tabs' => [$radio_group], 'text_tabs' => [$text]
]));
# Template object:
$template_request = new EnvelopeTemplate([
'description' => "Example template created via the API",
'name' => $this->template_name,
'shared' => "false",
'documents' => [$document], 'email_subject' => "Please sign this document",
'recipients' => new Recipients([
'signers' => [$signer], 'carbon_copies' => [$cc]]),
'status' => "created"
]);
return $template_request;
}
$results = $templates_api->createTemplate($args['account_id'], $template_req_object);
?>
Error:
Parse error: syntax error, unexpected token "private", expecting end of file in C:\xampp\htdocs\docusign-esign\index.php on line 9
I couldn't find were to use my access_token, base_url etc as per
documentation
Here is how to use $access_token and $base_path, assuming you already obtained it using OAuth.
# You will need to obtain an access token using your chosen authentication flow
$api_client = new \DocuSign\eSign\client\ApiClient($base_path);
$config = new \DocuSign\eSign\Model\Configuration($api_client);
$config->addDefaultHeader('Authorization', 'Bearer ' + $access_token);
$users_api = new \DocuSign\eSign\Api\UsersApi($api_client);
I'm creating an Envelope signing request using the DocuSign API which includes 2 checkbox tabs. When I view the document after receiving the email when I click on either of the 2 checkboxes it puts a tick in both of them.
The 2 checkboxes appear but clicking either of them marks them both as ticked.
Here's the JSON for the tabs:
"tabs" :
{
"checkboxTabs" :
[
{
"documentId" : "1",
"pageNumber" : "6",
"required" : "true",
"tabId" : "0",
"tabLabel" : "",
"xPosition" : "323",
"yPosition" : "170"
},
{
"documentId" : "1",
"pageNumber" : "6",
"required" : "true",
"tabId" : "1",
"tabLabel" : "",
"xPosition" : "323",
"yPosition" : "184"
}
],
"dateSignedTabs" :
[
{
"documentId" : "1",
"pageNumber" : "6",
"xPosition" : "447",
"yPosition" : "272"
}
],
"signHereTabs" :
[
{
"documentId" : "1",
"pageNumber" : "6",
"xPosition" : "266",
"yPosition" : "231"
}
],
"textTabs" :
[
{
"documentId" : "1",
"pageNumber" : "6",
"tabLabel" : "Name",
"xPosition" : "86",
"yPosition" : "273"
}
]
}
They have unique "tabId" values - not sure what I need to do to make them separate checkboxes that can be ticked/unticked independently.
You'll need to give each checkbox a unique TabLabel. By giving them the same 'empty' TabLabel, you're telling DocuSign that data should populate between them.
I generate an envelope of 10-15 documents. There are two end users, the user that will sign should not see document 1 with the data of the second user. The second is to see all the documents.
I tried to use "excludedDocuments", but I come across the error "ACCOUNT_LACKS_PERMISSIONS".
The example I am generating (incomplete)
{"compositeTemplates": [
{
"inlineTemplates": [
{
"sequence": "1",
"recipients": {
"signers": [
{
"email": "albert#princeton.edu",
"name": "Albert Einstein",
"recipientId": "1",
"clientUserId": "albert#princeton.edu",
"routingOrder": "1",
"tabs": {
"textTabs": [],
"radioGroupTabs": [],
"checkboxTabs": []
}
}
]
}
}
],
"document": {
"documentId": 1,
"name": "FirstFile",
"transformPdfFields": "true"
}
},
{
"inlineTemplates": [
{
"sequence": "1",
"recipients": {
"signers": [
{
"email": "albert#princeton.edu",
"name": "Albert Einstein",
"recipientId": "1",
"clientUserId": "albert#princeton.edu",
"routingOrder": "1",
"tabs": {
"textTabs": [],
"radioGroupTabs": [],
"checkboxTabs": []
}
}
]
}
}
],
"document": {
"documentId": 2,
"name": "SecondFile",
"transformPdfFields": "true"
}
}
]}
Please tell me how to solve this problem. Thank you in advance
upd PHP7 code:
$compositeTemplates[] = [
'inlineTemplates' => [
[
'sequence' => '1',
'recipients' => [
'signers' => [
[
'email' => $userData['email'],
'name' => $userData['name'],
'recipientId' => '1',
'clientUserId' => $userData['email'],
'routingOrder' => '1',
"excludedDocuments" => ['1'],
'tabs' => [
'textTabs' => Template::fileTextTabs($sendData[$withoutExtension]['text'] ?? false), //here the simple formation of tabs according to what is
'radioGroupTabs' => Template::fileRadioGroupTabs($sendData[$withoutExtension]['radio'] ?? false),
'checkboxTabs' => Template::fileCheckboxTabs($sendData[$withoutExtension]['checkbox'] ?? false),
],
],
],
"carbonCopies" => [
[
"email" => 'mylyrium#gmail.com',
"name" => 'copies',
"recipientId" => "2",
"routingOrder" => '1',
],
],
],
],
],
'document' => [
'documentId' => $id,
'name' => $filename,
'transformPdfFields' => 'true',
],
];
$id++;
This error means that the Admin account is not properly configured to have the document visibility enabled.
To do so, go to your DocuSign Admin Account and scroll down to Sending Settings.
Make sure that one of the below options is selected instead of Off
For more information on the Document Visibility drop-down options, see the official documentation
I'm working on a DocuSign integration. I have the basics functional but can't seem to figure out how to merge a local document (PDF) with a server template such that tabs configured on the server template get used or overlaid on the passed document.
My template is defined on the server and I can use it directly from the web UI without issue (it's a W4 form). The template has three tabs (SSN, Sign here, and date) as you can see below. Accessing this template via it's ID using the API Explorer yields the following json
{
"envelopeTemplateDefinition": {
"templateId": "_redacted_",
"name": "W4 3/13/2017",
"shared": "true",
"password": "",
"description": "",
"lastModified": "2017-06-05T18:45:28.4470000Z",
"lastModifiedBy": {
"userName": "Andrew",
"userId": "_redacted_",
"email": "my_email_address",
"uri": "/users/_redacted_
},
"pageCount": 2,
"uri": "/templates/_redacted_",
"folderName": "Templates",
"folderId": "_redacted_",
"folderUri": "/folders/_redacted_",
"owner": {
"userName": "Andrew",
"userId": "_redacted_",
"email": "my_email_address"
}
},
"documents": [
{
"documentId": "46677269",
"uri": "/envelopes/_redacted_/documents/46677269",
"name": "W4.pdf",
"order": "1",
"pages": "2",
"display": "inline",
"includeInDownload": "true",
"signerMustAcknowledge": "no_interaction",
"templateLocked": "false",
"templateRequired": "false",
"documentGroup": "content"
}
],
"emailSubject": "Please DocuSign: W4.pdf",
"emailBlurb": "",
"signingLocation": "online",
"autoNavigation": "true",
"envelopeIdStamping": "true",
"authoritativeCopy": "false",
"notification": {
"reminders": {
"reminderEnabled": "false",
"reminderDelay": "0",
"reminderFrequency": "0"
},
"expirations": {
"expireEnabled": "true",
"expireAfter": "120",
"expireWarn": "0"
}
},
"enforceSignerVisibility": "false",
"enableWetSign": "true",
"allowMarkup": "false",
"allowReassign": "true",
"recipients": {
"signers": [
{
"defaultRecipient": "false",
"tabs": {
"signHereTabs": [
{
"stampType": "signature",
"name": "SignHere",
"tabLabel": "Signature _redacted_",
"scaleValue": 1.0,
"optional": "false",
"documentId": "46677269",
"recipientId": "94043042",
"pageNumber": "1",
"xPosition": "193",
"yPosition": "682",
"tabId": "_redacted_",
"templateLocked": "false",
"templateRequired": "false"
}
],
"dateSignedTabs": [
{
"name": "DateSigned",
"value": "",
"tabLabel": "Date Signed _redacted_",
"font": "lucidaconsole",
"fontColor": "black",
"fontSize": "size9",
"documentId": "46677269",
"recipientId": "94043042",
"pageNumber": "1",
"xPosition": "480",
"yPosition": "713",
"tabId": "_redacted_",
"templateLocked": "false",
"templateRequired": "false"
}
],
"ssnTabs": [
{
"validationPattern": "",
"validationMessage": "",
"shared": "false",
"requireInitialOnSharedChange": "false",
"requireAll": "false",
"value": "",
"width": 144,
"required": "true",
"locked": "false",
"concealValueOnDocument": "true",
"disableAutoSize": "false",
"maxLength": 4000,
"tabLabel": "Text _redacted_",
"font": "lucidaconsole",
"fontColor": "black",
"fontSize": "size9",
"documentId": "46677269",
"recipientId": "94043042",
"pageNumber": "1",
"xPosition": "442",
"yPosition": "563",
"tabId": "_redacted_",
"templateLocked": "false",
"templateRequired": "false"
}
]
},
"signInEachLocation": "false",
"name": "",
"email": "",
"recipientId": "94043042",
"accessCode": "",
"requireIdLookup": "false",
"routingOrder": "1",
"note": "",
"roleName": "New Employee",
"deliveryMethod": "email",
"templateLocked": "false",
"templateRequired": "false",
"inheritEmailNotificationConfiguration": "false"
}
],
"agents": [ ],
"editors": [ ],
"intermediaries": [ ],
"carbonCopies": [ ],
"certifiedDeliveries": [ ],
"inPersonSigners": [ ],
"recipientCount": "1"
}
}
What I want to do is apply this template to a PDF that's already partially filled out such that when the signer get's it the tabs defined in the server template are used for the sining.
As it stands now, there's nothing. Just the partially filled out PDF I passed in below as base64 data, with none of the server template tabs to fill out or sign. Here's my json for the API call (in PHP).
$data = array (
"emailBlurb" => "Test Email Body",
"emailSubject" => "Test Email Subject",
"status" => "sent",
"compositeTemplates" => array(array(
"document" => array(
"documentId" => 46677269,
"name" => $documentName,
"documentBase64" => $document
),
"serverTemplates" => array(array(
"sequence" => 1,
"templateId" => "_redacted_"
)),
"inlineTemplates" => array(array(
"sequence" => 2,
"recipients" => array(
"signers" => array(array(
"email" => $recipientEmail,
"name" => $recipientName,
"recipientId" => $recipientID,
"roleName" => "New Employee"
))
)
))
))
); //$data = array...
I suspect that I'm simply missing some appropriate reference to the tabs defined in the server template. But documentation is atrocious and I've already spent several hours combing the web. Any help would be much appreciated.
UPDATE1
As requested, here's the code that generates the envelope successfully:
function c_requestSignature($templateID, $recipientName, $recipientEmail, $recipientID, $document){
//function sets up the passed document for signing using the specified template
$documentName = "W4"; //FIXME fetch document name using templateID
$baseURL = c_docusignBaseURL();
$accountId = c_docusignAccountId();
$header = c_docusignHeader();
$data = array (
"emailSubject" => "Please sign " . $documentName,
//"emailBlurb" => "Test Email Body",
"status" => "sent",
"compositeTemplates" => array(
"compositeTemplate" => array(
"serverTemplates" => array(
"serverTemplate" => array(
"sequence" => "1",
"templateId" => "_redacted_"
)
),
"inlineTemplates" => array(
"inlineTemplate" => array(
"sequence" => "2",
"recipients" => array(
"signers" => array(
"signer" => array(
"name" => $recipientName,
"email" => $recipientEmail,
"roleName" => "NewHire"
)
)
)
)
),
"document" => array(
"documentId" => "1",
"name" => $documentName,
"fileExtension" => "pdf",
"documentBase64" => $document
)
)
)
);
// Send to the /envelopes end point, which is relative to the baseUrl received above.
$curl = curl_init($baseURL . "/envelopes" );
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string),
"X-DocuSign-Authentication: $header" )
);
$json_response = curl_exec($curl); // Do it!
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ( $status != 201 ) {
echo "Error calling DocuSign, status is:" . $status . "\nerror text: ";
print_r($json_response); echo "\n";
exit(-1);
}
$response = json_decode($json_response, true);
$envelopeId = $response["envelopeId"];
error_log ("successfully created envelope: $envelopeId");
$url = getSignatureURL($envelopeId, $recipientName, $recipientEmail, $recipientID);
return $url;
}//c_requestSignature()...
The function getSignatureURL() has code as follows:
function getSignatureURL($envelopeId, $recipientName, $recipientEmail, $recipientID){
//function retrieves the signing ceremony UX URL from DocuSign
$baseURL = c_docusignBaseURL();
$accountId = c_docusignAccountId();
$header = c_docusignHeader();
//set up the data we'll send to the Docusign server
$data = array("returnUrl" => "http://_redacted_",
"authenticationMethod" => "none",
"email" => $recipientEmail,
"name" => $recipientName,
"recipientId" => $recipientID,
//"recipientId" => "1",
//"clientUserId" => $recipientID,
"userName" => $recipientName
);
$data_string = json_encode($data);
//set up curl
$curl = curl_init($baseURL . "/envelopes/$envelopeId/views/recipient" );
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string),
"X-DocuSign-Authentication: $header" )
);
//make the API call
$json_response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ( $status != 201 ) {
echo "error calling webservice, status is:" . $status . "\nerror text is --> ";
print_r($json_response); echo "\n";
exit(-1);
}
//retrieve and process the response
$response = json_decode($json_response, true);
return $response["url"];
}
UPDATE 2
Here's the raw json as requested...
{
"emailSubject": "some subject",
"status": "sent",
"compositeTemplates": [
{
"serverTemplates": [
{
"sequence": "1",
"templateId": "_redacted_"
}
],
"inlineTemplates": [
{
"sequence": "2",
"recipients": {
"signers": [
{
"name": "Andrew Tester1",
"email": "my_email_address",
"roleName": "NewHire",
"recipientId": "1234",
"clientUserId": "1234"
}
]
}
}
],
"document": {
"documentId": "1",
"name": "W4",
"fileExtension": "pdf",
"documentBase64": "_redacted_"
}
}
]
}
Update 3
I had a problem which was preventing me from seeing the proper output of the above json. With that fixed, now I'm getting the following error:
Error calling DocuSign, status is:400 error text: { "errorCode":
"TAB_REFERS_TO_MISSING_DOCUMENT", "message": "The DocumentId specified
in the tab element does not refer to a document in this envelope. Tab
refers to DocumentId 46677269 which is not present." }
If I change the document stanza above as follows:
"document": {
"documentId": "46677269",
"name": "W4",
"fileExtension": "pdf",
"documentBase64": "_redacted_"
}
The error goes away, but I still get a signing ceremony with no tabs.
The following request can be used to create an envelope from the Server Template. The server templates document will be replaced with the new document that is specified in the request.
{
"emailSubject": "Test Email Subject",
"emailBlurb" : "Test Email Body",
"status": "sent",
"compositeTemplates": [
{
"serverTemplates": [
{
"sequence": "1",
"templateId": "86841739-f12d-460e-9807-23a9b90cff6b"
}
],
"inlineTemplates": [
{
"sequence": "1",
"recipients": {
"signers": [
{
"name": "Jane Doe",
"email": "janedoe#acme.com",
"roleName": "New Employee"
}
]
}
}
],
"document": {
"documentId": "1",
"name": "Your Doc Name",
"fileExtension": "pdf",
"documentBase64": ""
}
}
]
}
Well this was a thorny problem, but I found the solution (finally), with some help form the folk at Docusign. I thought I'd post it here for those who might run into this in the future.
Solution
The json structure passed to the DocuSign API is extremely important. If there are any errors in it at all, it won't work right, and the error message is not always helpful. Furthermore, in what I was doing, the specific parameters passed were also crucial. The envelope generation code above is correct, though with the caveat that you must specify the PDF document ID that is part of the template stored on DocuSign's servers. You can query this via their API given the envelope ID (which is what I'm doing). Apparently this didn't use to be required, but now it is and there's no documentation anywhere stating that.
My fundamental problem with the signature tabs though, was the code requesting the signing ceremony URL. I had extra parameters which were being accepted without error, but were messing things up. Discovered this, by using DocuSign's REST API explorer to generate a working signing ceremony URL (complete with all the proper signature tabs), and comparing the output json whith what I was trying to pass in my code.
Here's the working PHP code that generates the correct json:
$data = array(
"authenticationMethod" => "email",
"clientUserId" => $recipientID,
"email" => $recipientEmail,
"returnUrl" => "_redacted_",
"userName" => $recipientName
);
$data_string = json_encode($data);
The Docusign REST API describes a way to control document visibility when creating envelopes from documents:
https://docs.docusign.com/esign/restapi/Envelopes/Envelopes/create/
The option is enforceSignerVisibility. However when I attempt to use this option, the visibility is NOT limited. Am I doing something wrong?
I am using a modified version of the PHP DocuSign helper library (some features/options added).
Here is a test case I created. In this case, there are two signers and two documents. Signer 1 has a signature on Document 1 and Signer 2 has a signature on Document 2. The goal is that Signer 1 would only see Document 1 and Signer 2 would only see Document 2. However, the below example results in both signers seeing both documents:
Code
<?php
$client = new DocuSign_Client;
$service = new DocuSign_RequestSignatureService($client);
$documents = array(
new DocuSign_Document(
"TestDoc1",
1,
file_get_contents( $_SERVER['DOCUMENT_ROOT'].'/sandbox/ncg/test1.pdf' )
),
new DocuSign_Document(
"TestDoc2",
2,
file_get_contents( $_SERVER['DOCUMENT_ROOT'].'/sandbox/ncg/test2.pdf' )
)
);
$signer1 = new DocuSign_Recipient(
1,
1,
"Signer 1",
"test.tfcornerstone+t1#gmail.com",
NULL
);
$signer1->setTab("signHereTabs",array(
"anchorYOffset" => "0",
"anchorXOffset" => "0",
"anchorString" => "[__[Signer1]__]",
"anchorIgnoreIfNotPresent" => true,
) );
$signer2 = new DocuSign_Recipient(
1,
2,
"Signer 2",
"test.tfcornerstone+t2#gmail.com",
NULL
);
$signer2->setTab("signHereTabs",array(
"anchorYOffset" => "0",
"anchorXOffset" => "0",
"anchorString" => "[__[Signer2]__]",
"anchorIgnoreIfNotPresent" => true,
) );
$recipients = array( $signer1, $signer2 );
$emailSubject = "Test Doc";
$emailBlurb = "Testing Visibility";
$status = 'sent'; // can be "created" or "sent"
$eventNotifications = new DocuSign_EventNotification(
$url, //url
false, //loggingEnabled
false, //requireAcknowledgment,
false, //useSoapInterface,
NULL, //soapNameSpace,
false, //includeCertificateWithSoap,
false, //signMessageWithX509Cert,
false, //includeDocuments,
false, //includeTimeZone,
false, //includeSenderAccountAsCustomField,
NULL, //envelopeEvents,
array( "Completed", "Sent" ) //recipientEvents
);
$options = array(
"enforceSignerVisibility" => true,
);
$response = $service->signature->createEnvelopeFromDocument(
$emailSubject,
$emailBlurb,
$status,
$documents,
$recipients,
$eventNotifications,
$options
);
d($response);
CURL request
Url: https://www.docusign.net/restapi/v2/accounts/XXXXX/envelopes
Method: POST
Headers:
--myboundary
Content-Type: application/json
Content-Disposition: form-data
{"emailSubject":"Test Doc","emailBlurb":"Testing Visibility","documents":[{"name":"TestDoc1","documentId":1},{"name":"TestDoc2","documentId":2}],"status":"sent","enforceSignerVisibility":true,"recipients":{"signers":[{"routingOrder":1,"recipientId":1,"name":"Signer 1","email":"test.tfcornerstone+t1#gmail.com","clientUserId":null,"tabs":{"signHereTabs":[{"anchorYOffset":"0","anchorXOffset":"0","anchorString":"[__[Signer1]__]","anchorIgnoreIfNotPresent":true}]},"embeddedRecipientStartUrl":null,"excludedDocuments":null},{"routingOrder":1,"recipientId":2,"name":"Signer 2","email":"test.tfcornerstone+t2#gmail.com","clientUserId":null,"tabs":{"signHereTabs":[{"anchorYOffset":"0","anchorXOffset":"0","anchorString":"[__[Signer2]__]","anchorIgnoreIfNotPresent":true}]},"embeddedRecipientStartUrl":null,"excludedDocuments":null}]},"eventNotification":{"loggingEnabled":false,"requireAcknowledgment":false,"useSoapInterface":false,"includeCertificateWithSoap":false,"signMessageWithX509Cert":false,"includeDocuments":false,"includeTimeZone":false,"includeSenderAccountAsCustomField":false,"recipientEvents":[{"recipientEventStatusCode":"Completed"},{"recipientEventStatusCode":"Sent"}]}}
<<PDF CONTENT>>--myboundary--
Formatted JSON data
{
"emailSubject": "Test Doc",
"emailBlurb": "Testing Visibility",
"documents": [
{
"name": "TestDoc1",
"documentId": 1
},
{
"name": "TestDoc2",
"documentId": 2
}
],
"status": "sent",
"enforceSignerVisibility": true,
"recipients": {
"signers": [
{
"routingOrder": 1,
"recipientId": 1,
"name": "Signer 1",
"email": "test.tfcornerstone+t1#gmail.com",
"clientUserId": null,
"tabs": {
"signHereTabs": [
{
"anchorYOffset": "0",
"anchorXOffset": "0",
"anchorString": "[__[Signer1]__]",
"anchorIgnoreIfNotPresent": true
}
]
},
"embeddedRecipientStartUrl": null,
"excludedDocuments": null
},
{
"routingOrder": 1,
"recipientId": 2,
"name": "Signer 2",
"email": "test.tfcornerstone+t2#gmail.com",
"clientUserId": null,
"tabs": {
"signHereTabs": [
{
"anchorYOffset": "0",
"anchorXOffset": "0",
"anchorString": "[__[Signer2]__]",
"anchorIgnoreIfNotPresent": true
}
]
},
"embeddedRecipientStartUrl": null,
"excludedDocuments": null
}
]
},
"eventNotification": {
"loggingEnabled": false,
"requireAcknowledgment": false,
"useSoapInterface": false,
"includeCertificateWithSoap": false,
"signMessageWithX509Cert": false,
"includeDocuments": false,
"includeTimeZone": false,
"includeSenderAccountAsCustomField": false,
"recipientEvents": [
{
"recipientEventStatusCode": "Completed"
},
{
"recipientEventStatusCode": "Sent"
}
]
}
}
I don't believe DocVis can be set to "Off" in order to enforce it in the API. In Preferences -> Features change the DocVis dropdown to "Sender Can Set Must Sign To View Unless Sender Account" and give the same request another shot.