We have a flow where we want to generate completed contracts with data pre-filled from our application. In some cases, the documents then need to be signed by a human, but in other cases they don't actually need a human signature (e.g. we don't need a human review of the generated contract). Can I use Docusign to implement both kinds of documents? We'd like to have one person set up all the tabs for all of our templates, regardless if they need human signatures or not.
DocuSign completed envelopes must be "acted" upon by at least one recipient. That means that your second scenario (no human signature) will require some other type of human interaction. For example, you have a single recipient that must view the envelope/documents. They don't have to sign, but they have to interact with the envelope. That's a requirement that cannot be avoided at this time.
And yes, pre-fill fields can be used in both cases, and you can do this programmatically as I explain this this blog post. Here is how to do this in C# after the envelope was already created:
// You need to obtain an access token using your chosen authentication flow
var apiClient = new ApiClient(basePath);
apiClient.Configuration.DefaultHeader.Add("Authorization", "Bearer " + accessToken);
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);
PrefillTabs prefillTabs = new PrefillTabs();
prefillTabs.TextTabs = new List<Text>();
prefillTabs.TextTabs.Add(new Text { PageNumber = "1", DocumentId = "1", Value = "MyValue" });
Tabs tabs = new Tabs();
tabs.PrefillTabs = prefillTabs;
envelopesApi.CreateDocumentTabs(accountId, envelopeId, "1", tabs);
Yes, you can create an envelope that will not be seen by a person.
Do this by including a carbon copy recipient to a non-existing email address (eg fake#example.com).
You will need to use prefill tabs.
Here's an example JSON followed by example C#
{
"emailSubject": "Envelope for the record",
"status": "sent",
"documents": [
{
"filename": "anchorfields.pdf",
"name": "Example document",
"fileExtension": "pdf",
"documentId": "1",
"tabs": {
"prefillTabs": {
"textTabs": [
{
"anchorString": "/field1/ ",
"bold": "true",
"font": "Garamond",
"fontColor": "BrightBlue",
"fontSize": "Size14",
"value": "This field and the following are all \"prefill\" fields: "
},
{
"anchorString": "/field1/ ",
"anchorXOffset": "0",
"anchorYOffset": "30",
"bold": "true",
"font": "Garamond",
"fontColor": "Black",
"fontSize": "Size12",
"value": "Checkbox:"
},
{
"anchorString": "/field1/ ",
"anchorXOffset": "90",
"anchorYOffset": "30",
"bold": "true",
"font": "Garamond",
"fontColor": "Black",
"fontSize": "Size12",
"value": "Radio button:"
},
{
"anchorString": "/field1/ ",
"anchorXOffset": "0",
"anchorYOffset": "60",
"bold": "true",
"font": "Garamond",
"fontColor": "Black",
"fontSize": "Size12",
"value": "Sender name:"
},
{
"anchorString": "/field1/ ",
"anchorXOffset": "190",
"anchorYOffset": "60",
"bold": "true",
"font": "Garamond",
"fontColor": "Black",
"fontSize": "Size12",
"value": "Sender company:"
}
],
"checkboxTabs": [
{
"anchorString": "/field1/ ",
"anchorXOffset": "70",
"anchorYOffset": "30",
"fontSize": "Size14",
"selected": "true"
}
],
"radioGroupTabs": [
{
"radios": [
{
"anchorString": "/field1/",
"anchorXOffset": "185",
"anchorYOffset": "30",
"selected": "true",
"fontSize": "Size14"
}
]
}
],
"senderNameTabs": [
{
"anchorString": "/field1/ ",
"anchorXOffset": "90",
"anchorYOffset": "58",
"font": "Garamond",
"fontColor": "DarkGreen",
"fontSize": "Size12"
}
],
"senderCompanyTabs": [
{
"anchorString": "/field1/ ",
"anchorXOffset": "310",
"anchorYOffset": "58",
"font": "Garamond",
"fontColor": "DarkGreen",
"fontSize": "Size12"
}
]
}
}
}
],
"recipients": {
"carbonCopies": [
{
"email": "fakeEmail#example.com",
"name": "Envelope created for the records",
"recipientId": "1"
}
]
}
}
C#
// DocuSign Builder example. Generated: Mon, 22 Nov 2021 21:17:29 GMT
// DocuSign (c) 2021. MIT License -- https://opensource.org/licenses/MIT
// #see https://developers.docusign.com -- DocuSign Developer Center
using System.Collections.Generic;
using System.IO;
using System;
using DocuSign.eSign.Api;
using DocuSign.eSign.Client;
using DocuSign.eSign.Model;
namespace CSharp_example
{
class Program
{
// obtain the accessToken from an oauth flow
// obtain the accountId and basePath from /oauth/userinfo
private const string accessToken = ;
private const string accountId =
private const string basePath = "https://demo.docusign.net/restapi";
// Create the envelope request and send it to DocuSign
// Returns the resulting envelopeId or ""
static string SendDocuSignEnvelope()
{
CarbonCopy carbonCopy1 = new CarbonCopy
{
Email = "fakeEmail#example.com",
Name = "Envelope created for the records",
RecipientId = "1"
};
List<CarbonCopy> carbonCopies1 = new List<CarbonCopy> {carbonCopy1};
Recipients recipients1 = new Recipients
{
CarbonCopies = carbonCopies1
};
Text textTab1 = new Text
{
AnchorString = "/field1/ ",
Bold = "true",
Font = "Garamond",
FontColor = "BrightBlue",
FontSize = "Size14",
Value = "This field and the following are all ""prefill"" fields: "
};
Text textTab2 = new Text
{
AnchorString = "/field1/ ",
AnchorXOffset = "0",
AnchorYOffset = "30",
Bold = "true",
Font = "Garamond",
FontColor = "Black",
FontSize = "Size12",
Value = "Checkbox:"
};
Text textTab3 = new Text
{
AnchorString = "/field1/ ",
AnchorXOffset = "90",
AnchorYOffset = "30",
Bold = "true",
Font = "Garamond",
FontColor = "Black",
FontSize = "Size12",
Value = "Radio button:"
};
Text textTab4 = new Text
{
AnchorString = "/field1/ ",
AnchorXOffset = "0",
AnchorYOffset = "60",
Bold = "true",
Font = "Garamond",
FontColor = "Black",
FontSize = "Size12",
Value = "Sender name:"
};
Text textTab5 = new Text
{
AnchorString = "/field1/ ",
AnchorXOffset = "190",
AnchorYOffset = "60",
Bold = "true",
Font = "Garamond",
FontColor = "Black",
FontSize = "Size12",
Value = "Sender company:"
};
List<Text> textTabs1 = new List<Text> {textTab1, textTab2, textTab3, textTab4, textTab5};
Checkbox checkboxTab1 = new Checkbox
{
AnchorString = "/field1/ ",
AnchorXOffset = "70",
AnchorYOffset = "30",
FontSize = "Size14",
Selected = "true"
};
List<Checkbox> checkboxTabs1 = new List<Checkbox> {checkboxTab1};
Radio radio1 = new Radio
{
AnchorString = "/field1/",
AnchorXOffset = "185",
AnchorYOffset = "30",
FontSize = "Size14",
Selected = "true"
};
List<Radio> radios1 = new List<Radio> {radio1};
RadioGroup radioGroupTab1 = new RadioGroup
{
Radios = radios1
};
List<RadioGroup> radioGroupTabs1 = new List<RadioGroup> {radioGroupTab1};
SenderName senderNameTab1 = new SenderName
{
AnchorString = "/field1/ ",
AnchorXOffset = "90",
AnchorYOffset = "58",
Font = "Garamond",
FontColor = "DarkGreen",
FontSize = "Size12"
};
List<SenderName> senderNameTabs1 = new List<SenderName> {senderNameTab1};
SenderCompany senderCompanyTab1 = new SenderCompany
{
AnchorString = "/field1/ ",
AnchorXOffset = "310",
AnchorYOffset = "58",
Font = "Garamond",
FontColor = "DarkGreen",
FontSize = "Size12"
};
List<SenderCompany> senderCompanyTabs1 = new List<SenderCompany> {senderCompanyTab1};
PrefillTabs prefillTabs1 = new PrefillTabs
{
CheckboxTabs = checkboxTabs1,
RadioGroupTabs = radioGroupTabs1,
SenderCompanyTabs = senderCompanyTabs1,
SenderNameTabs = senderNameTabs1,
TextTabs = textTabs1
};
Tabs tabs1 = new Tabs
{
PrefillTabs = prefillTabs1
};
Document document1 = new Document
{
DocumentId = "1",
FileExtension = "pdf",
DocumentBase64 = ReadContent("anchorfields.pdf"), // filename is anchorfields.pdf
Name = "Example document",
Tabs = tabs1
};
List<Document> documents1 = new List<Document> {document1};
EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition
{
Documents = documents1,
EmailSubject = "Envelope for the record",
Recipients = recipients1,
Status = "sent"
};
ApiClient apiClient = new ApiClient(basePath);
apiClient.Configuration.AddDefaultHeader("Authorization", "Bearer " + accessToken);
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);
try
{
EnvelopeSummary results = envelopesApi.CreateEnvelope(accountId, envelopeDefinition);
Console.WriteLine($"Envelope status: {results.Status}. Envelope ID: {results.EnvelopeId}");
return results.EnvelopeId;
}
catch (ApiException e)
{
Console.WriteLine("Exception while creating envelope!");
Console.WriteLine($"Code: {e.ErrorCode}\nContent: {e.ErrorContent}");
//Console.WriteLine(e.Message);
return "";
}
}
/// <summary>
/// This method read bytes content from files in the project's Resources directory
/// </summary>
/// <param name="fileName">resource path</param>
/// <returns>return Base64 encoded content as string</returns>
internal static string ReadContent(string fileName)
{
byte[] buff = null;
string path = Path.Combine(Directory.GetCurrentDirectory(), #"..\..\..\Resources", fileName);
using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
using (BinaryReader br = new BinaryReader(stream))
{
long numBytes = new FileInfo(path).Length;
buff = br.ReadBytes((int)numBytes);
}
}
return Convert.ToBase64String(buff);
}
// The mainline
static void Main(string[] args)
{
Console.WriteLine("Starting...");
string envelopeId = SendDocuSignEnvelope();
Console.WriteLine("Done.");
}
}
}
Related
I am using composite templates to create an envelope. Every template will only have one document. I need to individually process every document that comes back signed. In order to do that I need to add metadata to each document that is being sent. How can I do that while also using composite templates before I send the envelope?
I know all about envelope metadata and custom fields but what if I need it more specific to documents and I'm not working with the actual documents? Here I'm working with the templates that contain the documents.
Here is the c# code I have so far
List<CompositeTemplate> compositeTemplates = new List<CompositeTemplate>();
Recipients recipientsServerTemplate = new Recipients();
List<Signer> signers = new List<Signer>();
List<CarbonCopy> carbonCopies = new List<CarbonCopy>();
Signer signer1 = new Signer();
signer1.Email = signerEmail;
signer1.Name = signerName;
signer1.RoleName = "signer";
signer1.RecipientId = "1";
signer1.Tabs = tabs;
signers.Add(signer1);
CarbonCopy cc1 = new CarbonCopy();
cc1.Email = ccEmail;
cc1.Name = ccName;
cc1.RoleName = "cc";
cc1.RecipientId = "2";
carbonCopies.Add(cc1);
recipientsServerTemplate.Signers = signers;
recipientsServerTemplate.CarbonCopies = carbonCopies;
int i = 1;
foreach (string templateId in templateIds)
{
//add custom fields //this is per envelope. I need it more specific in my case
TextCustomField textcustomField = new TextCustomField
{
Name = "MyCustomField" + i.ToString(),
Required = "false",
Show = "false",
Value = "653022"
};
CustomFields cf = new CustomFields
{
TextCustomFields = new List<TextCustomField> { textcustomField }
};
List<ServerTemplate> ServerTemplates = new List<ServerTemplate>();
List<InlineTemplate> InlineTemplates = new List<InlineTemplate>();
CompositeTemplate CT = new CompositeTemplate
{
CompositeTemplateId = i.ToString()
};
ServerTemplate ST = new ServerTemplate
{
Sequence = i.ToString(),
TemplateId = templateId
};
InlineTemplate IT = new InlineTemplate
{
Recipients = recipientsServerTemplate,
Sequence = (i+1).ToString(),
CustomFields = cf//this is for the whole envelope
};
InlineTemplates.Add(IT);
ServerTemplates.Add(ST);
CT.ServerTemplates = ServerTemplates;
CT.InlineTemplates = InlineTemplates;
compositeTemplates.Add(CT);
i++;
}
EnvelopeDefinition env = new EnvelopeDefinition
{
Status = "sent",
CompositeTemplates = compositeTemplates
};
I suggest adding metadata at the envelope level about the envelope's documents. I don't think you can add document-level metadata when using composite templates.
Added
Templates are usually created via the DocuSign web app. But the web app does not support document-level metadata.
When you use the API to create the envelope, you could create add additional fields to the documents. You could make those additional fields only readable to your company recipients and store document metadata there.
But I would not recommend that. Better to store the document-specific data at the envelope level. For example, include the document-specific metadata as a JSON string in the envelope-level metadata:
...
"compositeTemplates": [
{
"serverTemplates": [
{
"sequence": "1",
"templateId": "61973947-7d29-490d-9bac-5432154321"
}
],
"inlineTemplates": [
{
"sequence": "2",
"customFields": {
"textCustomFields": [
{
"name": "Main document",
"show": "false",
"value": "{\"value\":34500, \"office\": \"SF\"}"
}
]
},
Adding metadata on the envelope about my documents was not a robust option as I knew nothing about the document that I could connect my metadata to
What I ended up doing to track individual documents within templates was adding a read-only text tab with "MetaDataId" as the tab label for each of my documents that I had in templates.
Read-only tabs don't replicate data across the envelope.
The JSON request would look like
{
"compositeTemplates": [
{
"compositeTemplateId": "FirstTemplate",
"serverTemplates": [
{
"sequence": "1",
"templateId": "templateId1"
}
],
"inlineTemplates": [
{
"sequence": "1",
"recipients": {
"signers": [
{
"email": "blah#blah.com",
"name": "Blah Blah",
"recipientId": "1",
"roleName": "Signer",
"routingOrder": "1",
"tabs": {
"textTabs": [
{
"tabLabel": "MetaDataId",
"value": "121"
}
]
}
}
]
}
}
]
},
{
"compositeTemplateId": "SecondTemplate",
"serverTemplates": [
{
"sequence": "2",
"templateId": "templateId2"
}
],
"inlineTemplates": [
{
"sequence": "2",
"recipients": {
"signers": [
{
"email": "blah#blah.com",
"name": "Blah Blah",
"recipientId": "1",
"roleName": "Signer",
"routingOrder": "1",
"tabs": {
"textTabs": [
{
"tabLabel": "MetaDataId",
"value": "122"
}
]
}
}
]
}
}
]
},
{
"compositeTemplateId": "ThirdTemplate",
"serverTemplates": [
{
"sequence": "3",
"templateId": "templateId3"
}
],
"inlineTemplates": [
{
"sequence": "3",
"recipients": {
"signers": [
{
"email": "blah#blah.com",
"name": "Blah Blah",
"recipientId": "1",
"roleName": "Signer",
"routingOrder": "1",
"tabs": {
"textTabs": [
{
"tabLabel": "MetaDataId",
"value": "123"
}
]
}
}
]
}
}
]
}
],
"emailSubject": "Please Sign",
"emailBlurb": "This is an email Example",
"status": "sent"
}
Incidentally, if you're using the C# SDK and you're creating the templates with a loop as I am doing in the question for this post then you may have to clone the object that you're setting for the Recipients the in Inline Template so that the value you set for the MetaDataId text tab for the signer doesn't just hold the last one that you assigned, which will happed if you reuse the same Recipients object.
I'm using the nuget c# REST package. When I try to add a document with new recipients to an existing envelope, the recipients & the document show in the envelope but there are no tabs associated with the new doc even though the tabs are listed for the new recipients when I call UpdateRecipients.
I've checked to make sure the DocID matches in the tabs with the new DocID sent in the EnvelopeDefinition to UpdateDocuments & all the other data seems to be correct but no tabs show for the new recipients.
I've tried this passing in just the 2 new recipients or passing in the whole list of recipients (3 previous + 2 new). In both cases all the recipients return a success message from UpdateRecipients but no tabs are added.
Here is some of my code.
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.Documents = new List<Document>();
foreach (DSDocumentIN docIN in documents)
{
// Add a document to the envelope
Document doc = new Document();
doc.Name = docIN.Name;
doc.DocumentBase64 = docIN.FileBase64;
doc.DocumentId = docIN.DocID;
envDef.Documents.Add(doc);
}
int iCounter = 1;
List<Signer> signerList = new List<Signer>();
List<DSSigner> fullSignerList = new List<DSSigner>();
fullSignerList = GetDSSigners(existingEnvelopeId);
foreach (DSSigner newSigner in signers)
{
fullSignerList.Add(newSigner);
}
foreach (DSSigner s in fullSignerList)
{
Signer docSigner = new Signer();
docSigner.Email = s.Email;
docSigner.Name = s.FirstName + " " + s.LastName;
docSigner.RoleName = s.Role;
if (string.IsNullOrEmpty(s.RecipientId))
docSigner.RecipientId = Guid.NewGuid().ToString();
else
docSigner.RecipientId = s.RecipientId;
if (string.IsNullOrEmpty(s.RoutingOrder))
docSigner.RoutingOrder = iCounter.ToString();
else
docSigner.RoutingOrder = s.RoutingOrder;
if (string.IsNullOrEmpty(s.State))
docSigner.Status = "Created";
else
docSigner.Status = s.State;
docSigner.Tabs = AllTabs(docSigner, documents);
signerList.Add(docSigner);
iCounter++;
}
envDef.Recipients = new Recipients();
envDef.Recipients.Signers = new List<Signer>();
envDef.Recipients.Signers = signerList;
RecipientsUpdateSummary recipientsUpdateSummary = apiInstance.UpdateRecipients(accountId, existingEnvelopeId, envDef.Recipients);
EnvelopesApi.UpdateDocumentsOptions UpdateDocOptions = new EnvelopesApi.UpdateDocumentsOptions();
UpdateDocOptions.applyDocumentFields = "True"; // string | When true, Document fields can be added or modified while adding or modifying envelope documents. (optional)
// Adds one or more documents to an existing envelope document.
EnvelopeDocumentsResult result = apiInstance.UpdateDocuments(accountId, existingEnvelopeId, envDef, UpdateDocOptions);
Debug.WriteLine(result);
//Get recipients to find RecipientIdGuid (which becomes RecipientId when sending). RecipientId is key field in db. -- DocuSign does not appear to have this RecipientIdGuid/RecipientId documented
Recipients recips = apiInstance.ListRecipients(accountId, existingEnvelopeId);
06/12/18 UPDATE: Currently, I'm adding the new doc & then the recipients. I've included a link to the logs for this approach:
DocuSign Log
(see specifically 04_OK_AddDocumentsToEnvelope.txt & 03_OK_UpdateEnvelopeRecipients.txt)
I think I have this figured out.
Going to clean it up & make sure it is working as desired & then I'll post more details here.
06/13/2018 UPDATE:
This answer is specifically for using the nuget c# REST package. Since the tabs are part of the Recipient object, I expected that including the tabs in the recipient object & then adding the recipient to the envelope would work. However this is not the case when calling the UpdateRecipients method. The new recipients get added but no new tabs. Instead the tabs must be added separately.
Also interesting was the names of the functions that had to be called as follows:
Add new doc = UpdateDocuments
Add new recipients = UpdateRecipients
Add new tabs = CreateTabs
There are probably other ways that work but here is the meat of the code that works for me:
public string AddDocumentsToEnvelope(List<DSDocumentIN> documents, List<DSSigner> signers, string clientAppNo, int PDFRequestID, string envelopeID)
{
string username = "randys#balboacapital.com";
string password = "555Tmppwd";
string integratorKey = "321e15cb-89a1-4958-a444-6b2d33fc7005";
string retEnvelopeID = "";
ExceptionDispatchInfo exInfo = null;
try
{
var pdfDocCtrl = new BalboaLeaseCL.PDFDocController();
var apiInstance = new EnvelopesApi();
string existingEnvelopeId = envelopeID;
// initialize client for desired environment (for production change to www)
ApiClient apiClient = new ApiClient("https://demo.docusign.net/restapi");
Configuration.Default.ApiClient = apiClient;
// configure 'X-DocuSign-Authentication' header
string authHeader = "{\"Username\":\"" + username + "\", \"Password\":\"" + password + "\", \"IntegratorKey\":\"" + integratorKey + "\"}";
Configuration.Default.AddDefaultHeader("X-DocuSign-Authentication", authHeader);
// we will retrieve this from the login API call
string accountId = null;
/////////////////////////////////////////////////////////////////
// STEP 1: LOGIN API
/////////////////////////////////////////////////////////////////
// login call is available in the authentication api
AuthenticationApi authApi = new AuthenticationApi();
LoginInformation loginInfo = authApi.Login();
// parse the first account ID that is returned (user might belong to multiple accounts)
accountId = loginInfo.LoginAccounts[0].AccountId;
// Update ApiClient with the new base url from login call
string[] separatingStrings = { "/v2" };
apiClient = new ApiClient(loginInfo.LoginAccounts[0].BaseUrl.Split(separatingStrings, StringSplitOptions.RemoveEmptyEntries)[0]);
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.Documents = new List<Document>();
foreach (DSDocumentIN docIN in documents)
{
// Add a document to the envelope
Document doc = new Document();
doc.Name = docIN.Name;
doc.DocumentBase64 = docIN.FileBase64;
doc.DocumentId = docIN.DocID;
envDef.Documents.Add(doc);
}
int iCounter = 1;
List<Signer> signerList = new List<Signer>();
List<DSSigner> fullSignerList = new List<DSSigner>();
fullSignerList = GetDSSigners(existingEnvelopeId);
foreach (DSSigner newSigner in signers)
{
fullSignerList.Add(newSigner);
}
foreach (DSSigner s in fullSignerList)
{
Signer docSigner = new Signer();
docSigner.Email = s.Email;
docSigner.Name = s.FirstName + " " + s.LastName;
docSigner.RoleName = s.Role;
if (string.IsNullOrEmpty(s.RecipientId))
docSigner.RecipientId = Guid.NewGuid().ToString();
else
docSigner.RecipientId = s.RecipientId;
if (string.IsNullOrEmpty(s.RoutingOrder))
docSigner.RoutingOrder = iCounter.ToString();
else
docSigner.RoutingOrder = s.RoutingOrder;
if (string.IsNullOrEmpty(s.State))
docSigner.Status = "Created";
else
docSigner.Status = s.State;
docSigner.Tabs = AllTabs(docSigner, documents);
signerList.Add(docSigner);
iCounter++;
}
envDef.Recipients = new Recipients();
envDef.Recipients.Signers = new List<Signer>();
envDef.Recipients.Signers = signerList;
EnvelopesApi.UpdateDocumentsOptions UpdateDocOptions = new EnvelopesApi.UpdateDocumentsOptions();
UpdateDocOptions.applyDocumentFields = "True"; // string | When true, Document fields can be added or modified while adding or modifying envelope documents. (optional)
// Adds one or more documents to an existing envelope
EnvelopeDocumentsResult result = apiInstance.UpdateDocuments(accountId, existingEnvelopeId, envDef, UpdateDocOptions);
if (string.IsNullOrEmpty(result.EnvelopeId))
throw new DocuSignAPIException("No EnvelopeID returned.");
else
retEnvelopeID = result.EnvelopeId;
//Adds recipients to envelope (but won't create tabs)
RecipientsUpdateSummary recipientsUpdateSummary = apiInstance.UpdateRecipients(accountId, existingEnvelopeId, envDef.Recipients);
foreach (DSSigner newSigner in signers)
{
foreach (Signer s in envDef.Recipients.Signers)
{
if (newSigner.RecipientId.Equals(s.RecipientId, StringComparison.InvariantCultureIgnoreCase))
{
//Adds the tabs for the recipients for the new doc
apiInstance.CreateTabs(accountId, existingEnvelopeId, s.RecipientId, s.Tabs);
break;
}
}
}
After you have created an envelope, then you need to make below calls to add a document and recipients with correct tabs on the document, I have JSON code sample req only, cannot test with CSharp code, but you find out exact call for the same:
To add a document, call below endpoint,
no need to add recipients in this call, just add document
PUT /restapi/v2/accounts/{accountId}/envelopes/{envelopeId}/documents/2
with below headers:
Accept:application/json
Authorization:Bearer <AccessToken>
Content-Type:application/pdf
Content-Disposition:file; filename="Cross Company Guaranty"; fileExtension=pdf; documentId=2
Content-Transfer-Encoding:base64
Body:
base64bytes
Both UpdateDocuments() and UpdateDocument() can add document to the envelope.
To add recipients and its tabs you need to call below endpoint,
instead of EnvelopeRecipients: update, it should be Envelopes: update with a query parameter advanced_update=true
PUT /restapi/v2/accounts/{accountId}/envelopes/{envelopeId}?advanced_update=true
Body:
{
"recipients": {
"signers": [{
"email": "FakeTempEmailAddressRequired#bccfaketemp.com",
"name": "Mary GS",
"recipientId": "78647ecb-ee2e-4910-92ef-c9bf18710657",
"roleName": "Guarantor_Signor",
"routingOrder": "4",
"status": "Created",
"tabs": {
"dateSignedTabs": [],
"initialHereTabs": [],
"signHereTabs": [{
"documentId": "2",
"optional": "false",
"pageNumber": "1",
"recipientId": "78647ecb-ee2e-4910-92ef-c9bf18710657",
"scaleValue": "0.9",
"tabOrder": "2",
"xPosition": "60",
"yPosition": "343"
}],
"textTabs": [{
"disableAutoSize": "true",
"documentId": "2",
"fontSize": "Size8",
"height": 10,
"pageNumber": "1",
"recipientId": "78647ecb-ee2e-4910-92ef-c9bf18710657",
"required": "true",
"tabLabel": "SealDate",
"tabOrder": "1",
"validationMessage": "Please enter valid date using \"mm/dd/yyyy\" format",
"validationPattern": "^((0?[1-9]|1[012])[/](0?[1-9]|[12][0-9]|3[01])[/]?[0-9]{4})*$",
"width": 100,
"xPosition": "248",
"yPosition": "319"
},
{
"disableAutoSize": "true",
"documentId": "2",
"fontSize": "Size8",
"height": 10,
"pageNumber": "1",
"recipientId": "78647ecb-ee2e-4910-92ef-c9bf18710657",
"required": "true",
"tabLabel": "Guarantor_SignorTitle",
"tabOrder": "3",
"validationMessage": "",
"validationPattern": "",
"width": 210,
"xPosition": "71",
"yPosition": "408"
},
{
"disableAutoSize": "true",
"documentId": "2",
"fontSize": "Size8",
"height": 10,
"pageNumber": "1",
"recipientId": "78647ecb-ee2e-4910-92ef-c9bf18710657",
"required": "true",
"tabLabel": "Guarantor_SignorTitle",
"tabOrder": "4",
"validationMessage": "",
"validationPattern": "",
"width": 210,
"xPosition": "114",
"yPosition": "522"
}]
}
},
{
"email": "FakeTempEmailAddressRequired#bccfaketemp.com",
"name": "Tony GW",
"recipientId": "259e95e8-b774-47c1-b2dd-acab0d3b965e",
"roleName": "GuarantorWitness",
"routingOrder": "5",
"status": "Created",
"tabs": {
"dateSignedTabs": [{
"documentId": "2",
"fontSize": "Size8",
"pageNumber": "1",
"recipientId": "259e95e8-b774-47c1-b2dd-acab0d3b965e",
"tabOrder": "4",
"xPosition": "80",
"yPosition": "660"
}],
"initialHereTabs": [],
"signHereTabs": [{
"documentId": "2",
"optional": "false",
"pageNumber": "1",
"recipientId": "259e95e8-b774-47c1-b2dd-acab0d3b965e",
"scaleValue": "0.9",
"tabOrder": "2",
"xPosition": "84",
"yPosition": "579"
}],
"textTabs": [{
"disableAutoSize": "true",
"documentId": "2",
"fontSize": "Size8",
"height": 10,
"pageNumber": "1",
"recipientId": "259e95e8-b774-47c1-b2dd-acab0d3b965e",
"required": "true",
"tabLabel": "GuarantorWitnessTitle",
"tabOrder": "1",
"validationMessage": "",
"validationPattern": "",
"width": 200,
"xPosition": "425",
"yPosition": "464"
},
{
"disableAutoSize": "true",
"documentId": "2",
"fontSize": "Size8",
"height": 10,
"pageNumber": "1",
"recipientId": "259e95e8-b774-47c1-b2dd-acab0d3b965e",
"required": "true",
"tabLabel": "GuarantorWitnessTitle",
"tabOrder": "3",
"validationMessage": "",
"validationPattern": "",
"width": 210,
"xPosition": "80",
"yPosition": "645"
}]
}
}]
}
}
Update method should work, but make sure to pass/set advancedUpdate as true in UpdateOptions parameters
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);
We're looking at is downloading the combined pdf and then extracting the individual documents ( not my idea ). I tried using pdfsharp against my combined pdf, but I don't see any title information on the individual documents. I was just wondering if this is even possible. If I use pdfsharp, it can pull out all the pages, but I have no way of really knowing which pages belong with with document.
After you create a DocuSign envelope you can read the document information from the envelope and remember the page counts for each document. This should also work if you are using Templates.
You can use the EnvelopeDocuments: list API to get the document information, which includes pages for each doc. Then when you need to extract the individual documents from the combined PDF you'll know where to separate the individual docs.
Here's a sample API response for the list documents call:
{
"envelopeDocuments": [
{
"availableDocumentTypes": [
{
"isDefault": "true",
"type": "electronic"
}
],
"display": "inline",
"documentId": "1",
"includeInDownload": "true",
"name": "NDA.pdf",
"order": "1",
"pages": "3",
"signerMustAcknowledge": "no_interaction",
"type": "content",
"uri": "/envelopes/44efc9e6-915e-4b1d-9b54-801410d6922d/documents/1"
},
{
"availableDocumentTypes": [
{
"isDefault": "true",
"type": "electronic"
}
],
"display": "inline",
"documentId": "2",
"includeInDownload": "true",
"name": "House.pdf",
"order": "2",
"pages": "1",
"signerMustAcknowledge": "no_interaction",
"type": "content",
"uri": "/envelopes/44efc9e6-915e-4b1d-9b54-801410d6922d/documents/2"
},
{
"availableDocumentTypes": [
{
"isDefault": "true",
"type": "electronic"
}
],
"display": "inline",
"documentId": "3",
"includeInDownload": "true",
"name": "contractor_agreement.docx",
"order": "3",
"pages": "2",
"signerMustAcknowledge": "no_interaction",
"type": "content",
"uri": "/envelopes/44efc9e6-915e-4b1d-9b54-801410d6922d/documents/3"
},
{
"availableDocumentTypes": [
{
"isDefault": "true",
"type": "electronic"
}
],
"display": "inline",
"documentId": "certificate",
"includeInDownload": "true",
"name": "Summary",
"order": "999",
"pages": "4",
"signerMustAcknowledge": "no_interaction",
"type": "summary",
"uri": "/envelopes/44efc9e6-915e-4b1d-9b54-801410d6922d/documents/certificate"
}
],
"envelopeId": "44efc9e6-915e-4b1d-9b54-801410d6922d"
}
The getEnvelopeDocuments api has an option to download all the documents in an envelope as a zip file. You can just unzip the file and get individual documents.
GET /v2/accounts/{accountId}/envelopes/{envelopeId}/documents/archive
Retrieve a ZIP archive that contains all of the PDF documents, the certificate, and any .WAV files used for voice authentication.
Here is a sample code to download zip file and unzip it using the Docusign C# SDK.
Full Example here.
var envApi = new EnvelopesApi();
// GetDocument() API call returns a MemoryStream
var docStream = envApi.GetDocument(accountId, envelopeId, "archive");
// let's save the document to local file system
string zipName = Path.GetRandomFileName();
string zipfilePath = #"C:\temp\" + zipName + ".zip";
using (var fs = new FileStream(zipfilePath, FileMode.Create))
{
docStream.Seek(0, SeekOrigin.Begin);
docStream.CopyTo(fs);
}
string extractPath = #"c:\temp\" + zipName;
System.IO.Compression.ZipFile.ExtractToDirectory(zipfilePath, extractPath);
Edit(By Kathy-Lori)
For some reason, I had a problem casting the Stream to a FileStream object. What I did instead was the following:
Stream docStream = envApi.GetDocument(AccountId, envelopeId, "archive");
MemoryStream ret = new MemoryStream();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = docStream.Read(buffer, 0, buffer.Length)) > 0)
{
ret.Write(buffer, 0, bytesRead);
}
ret.Position = 0;
using (var fs = new FileStream(#"C:\temp\envelopeDocuments.zip", FileMode.Create))
{
ret.CopyTo(fs);
}
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.