--- Updated for clarity & Solution ---
As it pertains to the ability to add documents to an existing envelope we noticed we had an issue uploading documents that contain Unicode characters in the file name. Our call is a multi-part PUT request to the /restapi/v2/accounts/[account id]/envelopes/[envelope id]/documents endpoint which sends the document data as binary content within one of the parts of the message rather then base64 encoded the document and embedded it as part of the json payload.
Quickly we identified that the portion of our code which writes to the request stream was using Encoding.Ascii and should change to use Encoding.UTF8.
using (var writer = new StreamWriter(stream, Encoding.ASCII, 1024, true))
{
// write boundary, content type, content disposition & json content
}
However, this change produced a number of error messages depending on what we tried, for example:
{
"errorCode": "ENVELOPE_IS_INCOMPLETE",
"message": "The Envelope is not Complete. A Complete Envelope Requires Documents, Recipients, Tabs, and a Subject Line. Envelope definition missing."
}
{
"envelopeId": "XXXXXXX-1869-4555-8831-eb3ed90850c2",
"envelopeDocuments": [
{
"documentId": "1",
"order": "1",
"errorDetails": {
"errorCode": "NO_DOCUMENT_RECEIVED",
"message": "Bytes for document corresponding to documentId 1 not found in request."
}
}
]
}
Using tools like Fiddler & Postman we could now see the outbound REST calls which for all intents and purposes were virtually identical to the previous calls except they now allowed for Unicode characters.
PUT https://demo.docusign.net/restapi/v2/accounts/xxxxx/envelopes/xxxxxx-f772-48af-82ac-f0c2e95eaac1/documents HTTP/1.1
Content-Type: multipart/form-data; boundary=soucecode
Accept: application/json
X-DocuSign-Authentication: [Creds Here]
Host: demo.docusign.net
Content-Length: 11691
Expect: 100-continue
--soucecode
Content-Type: application/json
Content-Disposition: form-data
{
"documents": [
{
"order": 1,
"name": "Specíal Character.docx",
"documentId": "1"
}
]
}
--soucecode
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Content-Disposition: file; filename="Specíal Character.docx"; documentid=1
[binary content removed for brevity]
--soucecode--
Docusign Rest Api also has an additional option to pass the document bytes as base64 encoded. This is alternate to the multi-part HTTP request that you have used.
From Documentation:
documentBase64 : This field can be used to include a base64 version of the document bytes within an envelope definition instead of sending the document using a multi-part HTTP request. The maximum document size is smaller if this field is used due to the overhead of the base64 encoding.
Here is a sample updateEnvelopeDocuments request
PUT /restapi/v2/accounts/{acccountId}/envelopes/{envelopeId}/documents HTTP/1.1
Host: demo.docusign.net
X-DocuSign-Authentication: <DocuSignCredentials><IntegratorKey>{key}</IntegratorKey><Username>{userName}</Username><Password>{pwd}</Password></DocuSignCredentials>
Content-Type: application/json
{
"documents": [
{
"documentId": "1",
"name" : "Doc Name",
"fileExtension": "pdf",
"documentBase64": "Add your Base64 encoded document bytes here"
}
]
}
After numerous red herrings DocuSign support was finally able to provide us with a working example call which we could replay from Fiddler/Postman on our side. Again they looked virtually identical. Using WinDiff which shows content not visible in normal editors we were able to see subtle differences. The following characters  were noticed around the boundaries in the non functioning call. These characters are known as BOMs (byte order markers). Turns out by default most of the Stream Writers in the .net framework output these BOM markers when using UTF8 encoding so it was not immediately clear they were there or how to turn them off. The DocuSign service is expecting they are not present in the request and will fail when they are.
To resolve : Instantiate a new instance of UTF8Encoding passing false to the constructor as below. This will prevent the BOMs from being written to the stream.
using (var writer = new StreamWriter(stream, new UTF8Encoding(false),1024, true))
{
// new UTF8Encoding(false)
// the false flags instructs the writer to NOT emit the UTF8 Preamble (BOM)
}
Incredibly simple fix for a very elusive issue.
Document names, and any other part of the DocuSign data handles Unicode characters just fine, including left to right.
The key is to use UTF-8 encoding. Here's the JSON and a screenshot of a quick test of using Hebrew in a document name.
JSON request:
{
"documents": [
{
"documentId": "1",
"name": "שלום Agreement",
"fileExtension": "html",
"documentBase64": "[Contents elided]"
}
],
"emailSubject": "NewCo agreement for signature",
"status": "sent",
"recipients": {
"signers": [
{
"recipientId": "1",
"name": "Larry",
"email": ... etc
Result shown in the sender's DocuSign web tool:
I just completed a test of using multi-part mime with a filename that uses Hebrew. After a number of issues, it worked fine.
Issues to check for:
Ensure that each separator line ends with "\r\n" (this is according to the specification)
Check that the boundary setting in the header properly matches the separator used in the request body. Note that the dashes (of the separator format) are not used in the boundary setting!
Check that each of the "definition" lines for each part has a "\r\n" line ending.
Check that the blank line after each separator consists of "\r\n"
You should give the right Content-Type in the file part of your request and give the file extension in the documents part of your json. Eg: "fileExtension": "docx"
Ensure that the file name (including any UTF-8 characters) is the same in the documents object and in the header for the mime part. (See below.)
Remember that the mime part which includes the file is sent in binary. No need to base64 encode it.
Once I got past the above, it all worked fine. See below.
Headers
{
"Authorization": "Bearer eyJ0eXAQXzvHysYca9V_47SUsBlahBlahBlah",
"Accept": "application/json",
"Content-Type": "multipart/form-data; boundary=AA1234"
}
Body Pretty-printed
--AA1234
Content-Disposition: form-data
Content-Type: application/json
{
"emailSubject": "NewCo agreement for signature",
"status": "sent",
"recipients": {
"signers": [
{
"recipientId": "1",
"name": "Joe Testor",
"email": "bvbv#gfg.ccvb",
"routingOrder": "1",
"tabs": {
"signHereTabs": [
{
"documentId": "1",
"optional": "false",
"recipientId": "1",
"name": "Please sign here",
"tabLabel": "signer1sig",
"xPosition": "100",
"yPosition": "100",
"pageNumber": "1"
}
]
}
}
]
},
"documents": [
{
"name": "שלום Agreement.docx",
"documentId": "1",
"order": "1",
"fileExtension": "docx"
}
],
"eventNotification": {
"includeDocumentFields": "false",
"envelopeEvents": [
{
"envelopeEventStatusCode": "Completed"
},
{
"envelopeEventStatusCode": "Declined"
},
{
"envelopeEventStatusCode": "Voided"
}
],
"url": "http://20f52d5f.proxy.webhookapp.com",
"requireAcknowledgment": "true",
"includeSenderAccountAsCustomField": "true",
"loggingEnabled": "true",
"includeDocuments": "false",
"signMessageWithX509Cert": "true"
}
}
--AA1234
Content-Disposition: file; filename="שלום Agreement.docx"; documentId=1
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
[Contents elided]
--AA1234--
Related
I am working with predefined template. Template has checkboxes to capture user's communication preference with following data labels -
COMMUNICATION_PREF-FAX
COMMUNICATION_PREF-EMAIL
COMMUNICATION_PREF-PHONE
In create envelop API call if I want to select a particular checbox for example EMAIL then I have to pass following in request body
POST /envelopes
"checkboxTabs": [
{
"tabLabel": "COMMUNICATION_PREF-EMAIL",
"selected": "true"
}
]
And after signing is complete when I read form fields using formdata api /envelopes/{{envelopeId}}/form_data I get following
{
"name": "COMMUNICATION_PREF-EMAIL",
"value": "X",
"originalValue": "X"
}
As you can see that checkbox state (checked or unchecked) is represented differently in both case. Value is set selected=true is not consistent with how its read back value = "X" I tried passing value = "X" in create envelope api but it doesn't work.
This inconsistency is problem for the calling application. Should it store checkbox state as true/false or X / empty.
Its not possible to apply translation logic (like treat X as selected) because while reading formdata, field type information (whether it's checkbox or not) is not available.
Any advice is much appreciated.
Instead of form_data try this endpoint and include recipients,tabs as a qp:
GET /accounts/[account_id]/envelopes/[envelope_id]include=recipients,tabs.
The response object will contain exactly what you're looking for. Here is a sample:
{
"status": "completed",
...
"recipients": {
"signers": [
{
"tabs": {
"checkboxTabs": [
{
"name": "",
"tabLabel": "Checkbox 105f25...0b2",
"selected": "true",
"shared": "false",
"requireInitialOnSharedChange": "false",
...
}
If you are hellbent on using the formData API, keep reading:
That formData API should return field type information within the name property. Basic string manipulation will enable you to discern the type. Here's a sample response object from an envelope I created...
{
"formData": [...]
"envelopeId": "7719639c-xxxx-xxxx-xxxx-c847ebebb9c6",
"status": "completed",
"sentDateTime": "2020-05-29T00:13:00.0000000Z",
"recipientFormData": [
{
"formData": [
{
"name": "Checkbox 105f257e-xxxx-xxxx-xxxx-03bae25e70b2 | tabGroups: [\"Checkbox Group 9620de9a-xxxx-xxxx-xxxx-dbf6c90e98af\"]",
"value": "X"
},
...
],
"recipientId": "85c97d0b-xxxx-xxxx-xxxx-fcc000c6400e",
"name": "Name",
"email": "test#test.com",
"SignedTime": "...",
"DeliveredTime": "..."
}
]
}
I am using the DocuSign REST API to created an embedded signing for a predefined document template using the /envelopes/{envelopeID}/views/recipient call. I know it is possible to pre-populate tag values when creating the envelope, but is it possible to retrieve the value the recipient actually provides in a given tag field for use in the application after signing is complete? If so, does anyone have an example?
Sounds like you are trying to Get Envelope Recipient Status with the optional query include_tabs
https://www.docusign.com/sites/default/files/REST_API_Guide_v2.pdf page 133
Example Request
GET https://{server}/restapi/{apiVersion}/accounts/{accountId}/envelopes/{envelopeId
}/recipients/?include_tabs=true
X-DocuSign-Authentication:
<DocuSignCredentials><Username>{name}</Username><Password>{password}</Password><Integrato
rKey>{integrator_key}</IntegratorKey></DocuSignCredentials>
Accept: application/json
Content-Type: application/json
Response
The response returns the recipient types and current routing order. The recipient types includes the
recipient name, email, ID, recipient type, routing order, authentication status (including the date/time
and result of the authentication) status (including the date/time of the status changes) and, if the
recipient status is declined and a reason is required, a decline reason added by the recipient.If the
optional query include_tabs is set to true, the
tabs associated with the recipient are returned.
The following example shows the response json body
Example Response
{
"signers": [
{
"tabs": {
"textTabs": [
{
"height": 11,
"name": "Text",
"value": "this is a test",
"width": 66,
"required": "false",
"locked": "false",
"disableAutoSize": "false",
"tabLabel": "TAB1",
"documentId": "1",
"recipientId": "ed0e8744-6243-4708-9186-0e3ccf4cb3a4",
"pageNumber": "1",
"xPosition": "93",
"yPosition": "142",
"tabId": "2c7b4d94-d958-44df-b5a7-2b530ce914ed"
}
Yes there are at least a couple of ways to retrieve tab values from the signed (completed) docs. I believe Justin's answer is one way. You can also use the Get Tab Information for a Recipient API call, which will return tabs information (values included):
URL:
/accounts/{accountId}/envelopes/{envelopeId}/recipients/{recipientId}/tabs
Method:
GET
Example from DocuSign API Docs:
GET https://{server}/restapi/{apiVersion}/accounts/{accountId}/envelopes/{envelopeId}/recipients/{recipientId}/tabs
X-DocuSign-Authentication: <DocuSignCredentials><Username>{name}</Username><Password>{password}</Password><IntegratorKey>{integrator_key}</IntegratorKey></DocuSignCredentials>
Accept: application/json
Content-Type: application/json
Sample Response:
{
"approveTabs":[{
<Tab information removed>
}],
"textTabs":[{
<Tab information removed>
}],
"signHereTabs":[{
...
}]
}
Can you tell me whether my code below is fine?
--MYBOUNDARY
Content-Type: application/pdf
Content-Disposition: file; filename="sample.pdf";documentid=1
{
[B#17f7be7b
}
--MYBOUNDARY--
The [B#17f7be7b is the byteArray returned by java for my pdf (pdf file with just "Just a sample" as content). I just converted that byteArray to String and I have pasted it here.
I get
"errorCode": "UNABLE_TO_LOAD_DOCUMENT",
"message": "Unable to load the document. Unable to load Document(1). Error: the document is corrupt, rebuilding failed"
Pls help me with the right way of doing this.
A few comments regarding format/contents of what you posted in your Question:
The byte stream should not be surrounded with braces in the request. i.e., remove { and }
DO NOT convert the byte stream to String before adding it to the DocuSign request -- the byte stream should be written directly into the request.
Where you have [B#17f7be7b the request should instead include the complete and un-encoded *byte stream* for the file (NOT the String that represents the byte stream, but rather, the byte stream itself). The value you posted in your question is way too short to be a valid/complete byte stream for a PDF file.
You haven't posted the relevant code which makes it harder to debug, but on the surface there are at least two problems with the text you've posted. One is that the document bytes should not be enclosed with brackets { }.
Next it does not look like you are encoding the document bytes correctly. The PDF raw bytes will not start with [B#1.......... Without seeing your code I can only say you need to review how you are writing the PDF bytes to the request.
The reason why so many posts show "Document Bytes Go Here" is that it's pretty useless having a bunch of random, non-human readable characters in the sample request. If you are programmer and you understand what it means to write document bytes to a stream then you should understand no problem. Here is a sample request with once again the document bytes omitted:
POST https://demo.docusign.net/restapi/v2/accounts/123456/envelopes HTTP/1.1
X-DocuSign-Authentication: {"Username":"USERNAME","Password":"PASSWORD","IntegratorKey":"INTEGRATOR_KEY"}
Content-Type: multipart/form-data; boundary=BOUNDARY
Accept: application/json
Host: demo.docusign.net
Content-Length: 23414
Expect: 100-continue
Connection: Keep-Alive
--BOUNDARY
Content-Type: application/json
Content-Disposition: form-data
{
"status": "sent",
"emailBlurb": "Test Email Body",
"emailSubject": "Test Email Subject",
"documents": [
{
"name": "test.pdf",
"documentId": "1",
"order": "1"
}
],
"recipients": {
"signers": [
{
"email": "test#domain.com ",
"name": "John Doe",
"recipientId": "1",
"tabs": {
"signHereTabs": [
{
"xPosition": "100",
"yPosition": "100",
"documentId": "1",
"pageNumber": "1"
}
]
}
}
]
}
}
--BOUNDARY
Content-Type: application/pdf
Content-Disposition: file; filename="document.pdf"; documentid="1"
[document bytes removed]
--BOUNDARY--
The above request will send a signature request with one recipient of type signer and will place one signature tab on the document for them at location 100 pixels to the right and 100 pixels down from the top left of the document. Check out the DocuSign API Walkthroughs for code samples on how to accomplish this, in particular the 4th walkthrough Request Signature on a Document shows you exactly how to do this in 6 different languages (PHP, Javascript, Java, C#, Python, Objective-C) with instructions:
http://iodocs.docusign.com/APIWalkthroughs
We are trying to create an envelope from a pdf document using the docusign restapi v2. We can create an envelope using XML but when we try using JSON we receive the following error from docusign.
"errorCode": "ENVELOPE_IS_INCOMPLETE",
"message": "The Envelope is not Complete. A Complete Envelope Requires Documents, Recipients, Tabs, and a Subject Line. Envelope definition missing."
The entire POST we are sending is below from fiddler (with the file content removed).
POST https://demo.docusign.net/restapi/v2/accounts/xxxxx/envelopes HTTP/1.1
X-DocuSign-Authentication: {"Username":"xxxxxx","Password":"xxxxx","IntegratorKey":"xxxxxx"}
Content-Type: multipart/form-data; boundary=AAA
Accept: application/json
Host: demo.docusign.net
Content-Length: 90500
Expect: 100-continue
--AAA
Content-Type: application/json
Content-Disposition: form-data
{
"emailBlurb": "Blurb",
"emailSubject": "Subhject",
"documents": [
{
"name": "NDA.pdf",
"documentId": "1"
}
],
"recipients": {
"signers": [
{
"tabs": {
"signHereTabs": [
{
"pageNumber": "1",
"yPosition": "1",
"xPosition": "1",
"documentId": "1",
"tabId": "1",
"name": "TabName"
}
]
},
"routingOrder": "1",
"recipientId": "1",
"name": "Ben",
"email": "ben#test.com"
}
]
},
"status": "created"
}
--AAA
Content-Type: application/pdf
Content-Disposition: file; filename="NDA.pdf"; documentId="1"
<pdf file image content goes here>
--AAA--
As far as I can tell the JSON looks correct. Is there anything wrong that we are missing here?
Your JSON looks ok, this might be due to you having an extra CRLF character or two separating your boundaries in your request body. In general this is how things need to be spaced out (each newline is a \r\n):
--AAA
Content-Type: application/json
Content-Disposition: form-data
<YOUR VALID JSON GOES HERE>
--AAA
Content-Type:application/pdf
Content-Disposition: file; filename="document.pdf"; documentid=1
<DOCUMENT BYTES GO HERE>
--AAA--
It's quite possible that extra newline you have after your document bytes is causing your issue.
I had the same problem symptoms.
My problem was with the "Boundary terminator". Be sure to use:
--AAA
Content-Type: application/json
Content-Disposition: form-data
<YOUR VALID JSON GOES HERE>
--AAA--
if you have no document in your multipart attachement
I have the following requirement:
Five of my documents are generated from our system dynamically. The sixth document is an appendix. It's just a static pdf file. That appendix is 55 pages long.
So far I've been creating the envelope with the five documents, then I pull the appendix from a shared folder on our server, then I send it to docusign. The appendix takes about half of the upload time.
It would be more efficient if I had a template on Docusign that only includes the appendix, then I call it and add my five documents. My concerns are:
Is mixing and matching templates with stand alone documents supported?
The number of recipients and the signing tags change dynamically depending on our business rules. A template requires predefined roles and tags on the template. Is there a way to inject more recipients and tags to an Envelope that uses a template?
Using a "Composite Template" structure in your Create Envelope API request will allow you to create an Envelope using a combination of document(s) from DocuSign template(s) and document(s) specified at runtime via the API request, and gives you fairly dynamic control over recipients as well. For more information about using Composite Templates in a Create Envelope API request, search the API guide (either REST or SOAP) for "Composite".
When you use Composite Templates in your Create Envelope request, each individual Composite Template object in your request has to provide information required for a complete envelope (i.e., at least 1 recipient and 1 document...or a Server Template that defines the documents and/or recipients). Then DocuSign essentially combines all of the recipient and document info from all of your Composite Template objects in the request to form the Envelope. In the case of the example JSON request below, the resulting envelope contains:
3 recipients (populated dynamically via the API request: 1-Abby, 2-Bob, 3-Charlie)
3 documents (the first specified via the API request, the second specified via the API request, the third specified via a DocuSign template)
POST https://demo.docusign.net/restapi/v2/accounts/YOUR_ACCOUNT_NUMBER/envelopes
X-DocuSign-Authentication: {"Username":"YOUR_USER_NAME","Password":"YOUR_PASSWORD","IntegratorKey":"YOUR_INTEGRATOR_KEY"}
Content-Type: multipart/form-data; boundary=MY_BOUNDARY
Accept: application/json
--MY_BOUNDARY
Content-Type: application/json
Content-Disposition: form-data
{
"status" : "sent",
"emailSubject" : "Test Envelope",
"compositeTemplates": [
{
"inlineTemplates": [
{
"sequence" : 1,
"recipients": {
"signers" : [{
"email": "abbysEmailAddr#outlook.com",
"name": "Abby Abbott",
"recipientId": "1"
}, {
"email": "bobsEmailAddr#outlook.com",
"name": "Bob Burns",
"recipientId": "2",
"routingOrder":"2"
},
{
"email": "charliesEmailAddr#outlook.com",
"name": "Charlie Carlson",
"recipientId": "3",
"routingOrder":"3"
}]
}
}],
"document": {
"documentId": 1,
"name": "Customer Agreement",
"fileExtension": "pdf"
}
},
{
"inlineTemplates": [
{
"sequence" : 2,
"recipients": {
"signers" : [{
"email": "abbysEmailAddr#outlook.com",
"name": "Abby Abbott",
"recipientId": "1"
}]
}
}],
"document": {
"documentId": 2,
"name": "Test File",
"fileExtension": "pdf"
}
},
{
"serverTemplates": [
{
"sequence" : 1,
"templateId": "YOUR_TEMPLATE_ID"
}],
"inlineTemplates": [
{
"sequence" : 2,
"recipients": {
"signers" : [{
"email": "abbysEmailAddr#outlook.com",
"name": "Abby Abbott",
"recipientId": "1",
"roleName": "Customer",
"routingOrder":"1"
}
]
}
}]
}
]}
--MY_BOUNDARY
Content-Type: application/pdf
Content-Disposition: file; filename="CustomerAgreement.pdf"; documentid="1"
**pdf bytes removed for brevity**
--MY_BOUNDARY
Content-Type: application/pdf
Content-Disposition: file; filename="TestFile.pdf"; documentid="2"
**pdf bytes removed for brevity**
--MY_BOUNDARY--
Using Composite Templates is kind of tricky (and not well documented), so it may require some trial and error to get things to work exactly as you require -- but hopefully this example sheds some light on how Composite Templates can be used to create an envelope from a combination of DocuSign templates and documents specified dynamically via the API request.