I'm trying to create a new envelope using the DocuSign REST API version 2. This is the response I'm getting:
{
"errorCode" : "UNSPECIFIED_ERROR",
"message" : "Index was outside the bounds of the array."
}
Here's the (sanitized and word wrapped) data that's being sent. Note that we're using OAuth2.
POST https://na2.docusign.net/restapi/v2/accounts/{account_id}/envelopes
Content-Length: 72523
Content-Type: application/json
Accept: application/json
Authorization: bearer{oauth2_token}
{"status": "created",
"documents": [
{"documentBase64": "<DELETED_BASE64_DATA>", "name": "Sample Document",
"documentId": "1"}
],
"emailSubject": "Documents to Sign",
"recipients": {
"signers": [
{"recipientId": "1", "email": "user#example.com",
"routingOrder": "1", "name": "Example User"
}
]
}
}
Any thoughts as to which index is out of bounds?
Thanks
You have the document bytes in the wrong location, and you need to make a proper multipart/form-data request to create a new DocuSign envelope. Here is a full code sample in C#, copy a document to the same directory and fill in the variables at the top and it should work for you.
Note that this was copied from the DocuSign API Walkthroughs:
// DocuSign API Walkthrough 04 in C# - Request Signature on a Document
//
// To run this sample:
// 1) Create a new .NET project.
// 2) Add 4 assembly references to the project: System, System.Net, System.XML, and System.XML.Linq
// 3) Update the email, password, integrator key, recipient name, and document name in the code
// 4) Copy a sample PDF file into project directory with same name as the document name you set in the code
// 5) Compile and Run
//
// NOTE 1: The DocuSign REST API accepts both JSON and XML formatted http requests. These C# API walkthroughs
// demonstrate the use of XML format, whereas the other walkthroughs show examples in JSON format.
using System;
using System.IO;
using System.Net;
using System.Xml;
using System.Xml.Linq;
namespace DocuSignAPIWalkthrough04
{
public class RequestSignatureOnDocument
{
public static void Main ()
{
//---------------------------------------------------------------------------------------------------
// ENTER VALUES FOR THE FOLLOWING 6 VARIABLES:
//---------------------------------------------------------------------------------------------------
string username = "***"; // your account email
string password = "***"; // your account password
string integratorKey = "***"; // your account Integrator Key (found on Preferences -> API page)
string recipientName = "***"; // recipient (signer) name
string recipientEmail = "***"; // recipient (signer) email
string documentName = "***"; // copy document with same name and extension into project directory (i.e. "test.pdf")
string contentType = "application/pdf"; // default content type is PDF
//---------------------------------------------------------------------------------------------------
// additional variable declarations
string baseURL = ""; // - we will retrieve this through the Login API call
try {
//============================================================================
// STEP 1 - Login API Call (used to retrieve your baseUrl)
//============================================================================
// Endpoint for Login api call (in demo environment):
string url = "https://demo.docusign.net/restapi/v2/login_information";
// set request url, method, and headers. No body needed for login api call
HttpWebRequest request = initializeRequest( url, "GET", null, username, password, integratorKey);
// read the http response
string response = getResponseBody(request);
// parse baseUrl from response body
baseURL = parseDataFromResponse(response, "baseUrl");
//--- display results
Console.WriteLine("\nAPI Call Result: \n\n" + prettyPrintXml(response));
//============================================================================
// STEP 2 - Send Signature Request from Template
//============================================================================
/*
This is the only DocuSign API call that requires a "multipart/form-data" content type. We will be
constructing a request body in the following format (each newline is a CRLF):
--AAA
Content-Type: application/xml
Content-Disposition: form-data
<XML BODY GOES HERE>
--AAA
Content-Type:application/pdf
Content-Disposition: file; filename="document.pdf"; documentid=1
<DOCUMENT BYTES GO HERE>
--AAA--
*/
// append "/envelopes" to baseURL and use for signature request api call
url = baseURL + "/envelopes";
// construct an outgoing XML formatted request body (JSON also accepted)
// .. following body adds one signer and places a signature tab 100 pixels to the right
// and 100 pixels down from the top left corner of the document you supply
string xmlBody =
"<envelopeDefinition xmlns=\"http://www.docusign.com/restapi\">" +
"<emailSubject>DocuSign API - Signature Request on Document</emailSubject>" +
"<status>sent</status>" + // "sent" to send immediately, "created" to save as draft in your account
// add document(s)
"<documents>" +
"<document>" +
"<documentId>1</documentId>" +
"<name>" + documentName + "</name>" +
"</document>" +
"</documents>" +
// add recipient(s)
"<recipients>" +
"<signers>" +
"<signer>" +
"<recipientId>1</recipientId>" +
"<email>" + recipientEmail + "</email>" +
"<name>" + recipientName + "</name>" +
"<tabs>" +
"<signHereTabs>" +
"<signHere>" +
"<xPosition>100</xPosition>" + // default unit is pixels
"<yPosition>100</yPosition>" + // default unit is pixels
"<documentId>1</documentId>" +
"<pageNumber>1</pageNumber>" +
"</signHere>" +
"</signHereTabs>" +
"</tabs>" +
"</signer>" +
"</signers>" +
"</recipients>" +
"</envelopeDefinition>";
// set request url, method, headers. Don't set the body yet, we'll set that separelty after
// we read the document bytes and configure the rest of the multipart/form-data request
request = initializeRequest( url, "POST", null, username, password, integratorKey);
// some extra config for this api call
configureMultiPartFormDataRequest(request, xmlBody, documentName, contentType);
// read the http response
response = getResponseBody(request);
//--- display results
Console.WriteLine("\nAPI Call Result: \n\n" + prettyPrintXml(response));
}
catch (WebException e) {
using (WebResponse response = e.Response) {
HttpWebResponse httpResponse = (HttpWebResponse)response;
Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
using (Stream data = response.GetResponseStream())
{
string text = new StreamReader(data).ReadToEnd();
Console.WriteLine(prettyPrintXml(text));
}
}
}
} // end main()
//***********************************************************************************************
// --- HELPER FUNCTIONS ---
//***********************************************************************************************
public static HttpWebRequest initializeRequest(string url, string method, string body, string email, string password, string intKey)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create (url);
request.Method = method;
addRequestHeaders( request, email, password, intKey );
if( body != null )
addRequestBody(request, body);
return request;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static void addRequestHeaders(HttpWebRequest request, string email, string password, string intKey)
{
// authentication header can be in JSON or XML format. XML used for this walkthrough:
string authenticateStr =
"<DocuSignCredentials>" +
"<Username>" + email + "</Username>" +
"<Password>" + password + "</Password>" +
"<IntegratorKey>" + intKey + "</IntegratorKey>" +
"</DocuSignCredentials>";
request.Headers.Add ("X-DocuSign-Authentication", authenticateStr);
request.Accept = "application/xml";
request.ContentType = "application/xml";
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static void addRequestBody(HttpWebRequest request, string requestBody)
{
// create byte array out of request body and add to the request object
byte[] body = System.Text.Encoding.UTF8.GetBytes (requestBody);
Stream dataStream = request.GetRequestStream ();
dataStream.Write (body, 0, requestBody.Length);
dataStream.Close ();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static void configureMultiPartFormDataRequest(HttpWebRequest request, string xmlBody, string docName, string contentType)
{
// overwrite the default content-type header and set a boundary marker
request.ContentType = "multipart/form-data; boundary=BOUNDARY";
// start building the multipart request body
string requestBodyStart = "\r\n\r\n--BOUNDARY\r\n" +
"Content-Type: application/xml\r\n" +
"Content-Disposition: form-data\r\n" +
"\r\n" +
xmlBody + "\r\n\r\n--BOUNDARY\r\n" + // our xml formatted envelopeDefinition
"Content-Type: " + contentType + "\r\n" +
"Content-Disposition: file; filename=\"" + docName + "\"; documentId=1\r\n" +
"\r\n";
string requestBodyEnd = "\r\n--BOUNDARY--\r\n\r\n";
// read contents of provided document into the request stream
FileStream fileStream = File.OpenRead(docName);
// write the body of the request
byte[] bodyStart = System.Text.Encoding.UTF8.GetBytes(requestBodyStart.ToString());
byte[] bodyEnd = System.Text.Encoding.UTF8.GetBytes(requestBodyEnd.ToString());
Stream dataStream = request.GetRequestStream();
dataStream.Write(bodyStart, 0, requestBodyStart.ToString().Length);
// Read the file contents and write them to the request stream. We read in blocks of 4096 bytes
byte[] buf = new byte[4096];
int len;
while ((len = fileStream.Read(buf, 0, 4096) ) > 0) {
dataStream.Write(buf, 0, len);
}
dataStream.Write(bodyEnd, 0, requestBodyEnd.ToString().Length);
dataStream.Close();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static string getResponseBody(HttpWebRequest request)
{
// read the response stream into a local string
HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse ();
StreamReader sr = new StreamReader(webResponse.GetResponseStream());
string responseText = sr.ReadToEnd();
return responseText;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static string parseDataFromResponse(string response, string searchToken)
{
// look for "searchToken" in the response body and parse its value
using (XmlReader reader = XmlReader.Create(new StringReader(response))) {
while (reader.Read()) {
if((reader.NodeType == XmlNodeType.Element) && (reader.Name == searchToken))
return reader.ReadString();
}
}
return null;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static string prettyPrintXml(string xml)
{
// print nicely formatted xml
try {
XDocument doc = XDocument.Parse(xml);
return doc.ToString();
}
catch (Exception) {
return xml;
}
}
} // end class
} // end namespace
Related
I have been trying to solve this bad request error. I am able to make the request call and Azure reports total calls correctly and also reports total errors.
I can not get this code example to work; however if I send this via their online console all is fine:
static async void MakeRequest()
{
string key1 = "YourKey"; // azure the one should work
string data = "https://pbs.twimg.com/profile_images/476054279438868480/vvv5YG0Q.jpeg";
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request parameters
queryString["returnFaceId"] = "true";
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key1);
Console.Beep();
var uri = "https://westus.api.cognitive.microsoft.com/face/v1.0/detect?" + queryString;
//string statusURL = HttpContext.Current.Request.Url.Host;
//console.WriteLine("Your Status URL address is :" + statusURL);
HttpResponseMessage response;
// Request body
// byte[] byteData = Encoding.UTF8.GetBytes("{url: https://pbs.twimg.com/profile_images/476054279438868480/vvv5YG0Q.jpeg}");
byte[] byteData = Encoding.UTF8.
GetBytes("{"+ "url"+":"+"https://pbs.twimg.com/profile_images/476054279438868480/vvv5YG0Q.jpeg" + "}");
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType =
new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(uri, content);
}
HttpRequestMessage request =
new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = new StringContent("{body}",
Encoding.UTF8,
"application/json");
//CONTENT-TYPE header
await client.SendAsync(request)
.ContinueWith(responseTask =>
{
Console.WriteLine("Response: {0}", responseTask.Result);
Console.WriteLine("-----------------------------------");
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("End of Post return from MS");
Console.WriteLine("Hit ENTER to exit...");
Console.ReadKey();
});
}// end of Make request
Your JSON is malformed. Your fields and non-scalar fields must be quoted. You also have some unnecessary code. Here's code that works:
static async void MakeRequest()
{
string key1 = "YourKey"; // azure the one should work
string imageUri = "https://pbs.twimg.com/profile_images/476054279438868480/vvv5YG0Q.jpeg";
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request parameters
queryString["returnFaceId"] = "true";
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key1);
var uri = "https://westus.api.cognitive.microsoft.com/face/v1.0/detect?" + queryString;
string body = "{\"url\":\"" + imageUri + "\"}";
using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
{
await client.PostAsync(uri, content)
.ContinueWith(async responseTask =>
{
var responseBody = await responseTask.Result.Content.ReadAsStringAsync();
Console.WriteLine("Response: {0}", responseBody);
Console.WriteLine("-----------------------------------");
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("End of Post return from MS");
Console.WriteLine("Hit ENTER to exit...");
Console.ReadKey();
});
}
}// end of Make request
If you're using Visual Studio, I would recommend the NuGet package as this will handle much of the mundane details for you, including C# types for responses.
In the IBM Connections user interface, it's possible to attach files directly to wiki pages.
I want to attach files to wiki pages programatically, but can't find a documented way to do so.
I've been looking at the Connections 4.5 API documentation here:
http://www-10.lotus.com/ldd/appdevwiki.nsf/xpDocViewer.xsp?lookupName=IBM+Connections+4.5+API+Documentation#action=openDocument&content=catcontent&ct=prodDoc
Looking specifically at the APIs for Files and Wikis, there seems to be nothing about wiki-page attachments. While I'm mainly looking to upload attachments, I can't even see a documented API to retrieve attachments, despite the user-interface having a link to a feed (on each wiki page) that does that.
Is there any (possibly undocumented) API to attach files to a wiki page?
Here is some sample code to show how to do it in a supported way.
First, you need two URLs.
private static String apiUrlWikiNonce = "https://sdkdev.swg.usma.ibm.com:444/wikis/basic/api/nonce";
private static String apiUrlWikiAttachment = "https://sdkdev.swg.usma.ibm.com:444/wikis/form/api/wiki/{WIKI_LABEL_OR_WIKI_UUID}/page/{PAGE_LABEL_OR_PAGE_UUID}/feed";
Get the Nonce
/**
* gets the nonce value only valid return type is text, anything else throws
* an error
*
* #param httpClient
* #param user
* #param password
* #return
*/
public static String getNonce(CloseableHttpClient httpClient, String user,
String password) {
String result = "";
HttpGet httpGet = new HttpGet(apiUrlWikiNonce);
String combo = user + ":" + password;
String hash = org.apache.commons.codec.binary.Base64
.encodeBase64String(combo.getBytes());
System.out.println(user + " is logging in with " + hash);
String auth = "Basic " + hash;
httpGet.setHeader("Authorization", auth.replace("=", ""));
HttpClientContext context = HttpClientContext.create();
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet, context);
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
StringWriter writer = new StringWriter();
IOUtils.copy(is, writer);
String responseString = writer.toString();
// System.out.println(responseString);
result = responseString;
EntityUtils.consume(entity);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
Now that you have the nonce such as... b1911872-36f1-4767-956d-214759886406
Then upload a file with X-Update-Nonce
/**
* uploads a file to a given wiki page.
*
* #param httpClient
* #param user
* #param password
* #param wikiLabel
* #param wikiPage
* #param file
* #param nonce
* #return uuid <empty or uuid of file>
*/
public static String uploadFileToWiki(CloseableHttpClient httpClient, String user,
String password, String wikiLabel, String wikiPage, File file, String nonce){
String uuid = "";
String apiUrl = apiUrlWikiAttachment.replace("{WIKI_LABEL_OR_WIKI_UUID}", wikiLabel);
apiUrl = apiUrl.replace("{PAGE_LABEL_OR_PAGE_UUID}", wikiPage);
//Mandatory Parameter
apiUrl += "?category=attachment";
System.out.println("API Url : " + apiUrl);
HttpPost post = new HttpPost(apiUrl);
post.addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:32.0) Gecko/20100101 Firefox/32.0");
post.addHeader("Content-Type", "image/png");
String combo = user + ":" + password;
String hash = org.apache.commons.codec.binary.Base64
.encodeBase64String(combo.getBytes());
System.out.println(user + " is logging in with " + hash);
String auth = "Basic " + hash;
post.setHeader("Authorization", auth.replace("=", ""));
HttpClientContext context = HttpClientContext.create();
CloseableHttpResponse response = null;
try {
EntityBuilder builder = EntityBuilder.create();
builder.setFile(file);
HttpEntity postentity = builder.build();
post.setHeader("Slug","NominalWorkItem-v1.png");
post.setHeader("title","Test Title");
post.setHeader("label","Test Test");
post.setEntity(postentity);
response = httpClient.execute(post, context);
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
StringWriter writer = new StringWriter();
IOUtils.copy(is, writer);
String responseString = writer.toString();
System.out.println(responseString);
uuid = responseString;
EntityUtils.consume(entity);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return uuid;
}
The file is updated, and you get the response code 201 when created, and the XML
> <?xml version="1.0" encoding="UTF-8"?><entry
> xmlns:thr="http://purl.org/syndication/thread/1.0"
> xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"
> xmlns:snx="http://www.ibm.com/xmlns/prod/sn" xmlns:td="urn:ibm.com/td"
> xmlns="http://www.w3.org/2005/Atom"><id>urn:lsid:ibm.com:td:7780e11c-7240-41b3-8f0c-00a10ea69c93</id><td:uuid>7780e11c-7240-41b3-8f0c-00a10ea69c93</td:uuid><td:label>NominalWorkItem-v1.png</td:label><link
> href="https://sdkdev.swg.usma.ibm.com:444/wikis/form/api/wiki/744ccaeb-e9af-434c-9fa6-78831fb94846/page/3b017473-2dbe-4920-85a2-bf908a8e1475/attachment/7780e11c-7240-41b3-8f0c-00a10ea69c93/entry"
> rel="self"></link><link
> href="https://sdkdev.swg.usma.ibm.com:444/wikis/form/anonymous/api/wiki/744ccaeb-e9af-434c-9fa6-78831fb94846/page/3b017473-2dbe-4920-85a2-bf908a8e1475/attachment/7780e11c-7240-41b3-8f0c-00a10ea69c93/media/NominalWorkItem-v1.png"
> rel="alternate"></link><link
> href="https://sdkdev.swg.usma.ibm.com:444/wikis/form/api/wiki/744ccaeb-e9af-434c-9fa6-78831fb94846/page/3b017473-2dbe-4920-85a2-bf908a8e1475/attachment/7780e11c-7240-41b3-8f0c-00a10ea69c93/entry"
> rel="edit"></link><link
> href="https://sdkdev.swg.usma.ibm.com:444/wikis/form/api/wiki/744ccaeb-e9af-434c-9fa6-78831fb94846/page/3b017473-2dbe-4920-85a2-bf908a8e1475/attachment/7780e11c-7240-41b3-8f0c-00a10ea69c93/media"
> rel="edit-media"></link><link
> href="https://sdkdev.swg.usma.ibm.com:444/wikis/form/anonymous/api/wiki/744ccaeb-e9af-434c-9fa6-78831fb94846/page/3b017473-2dbe-4920-85a2-bf908a8e1475/attachment/7780e11c-7240-41b3-8f0c-00a10ea69c93/media/NominalWorkItem-v1.png"
> rel="enclosure" type="image/png" title="NominalWorkItem-v1.png"
> length="107763"></link><category term="attachment"
> scheme="tag:ibm.com,2006:td/type"
> label="attachment"></category><summary
> type="text"></summary><td:documentUuid>3b017473-2dbe-4920-85a2-bf908a8e1475</td:documentUuid><td:libraryId>744ccaeb-e9af-434c-9fa6-78831fb94846</td:libraryId><author><name>Frank
> Adams</name><snx:userid>6B54D23A-0A70-C7A4-8525-7CA50082A393</snx:userid><email>fadams#renovations.com</email><snx:userState>active</snx:userState></author><td:modifier><name>Frank
> Adams</name><snx:userid>6B54D23A-0A70-C7A4-8525-7CA50082A393</snx:userid><email>fadams#renovations.com</email><snx:userState>active</snx:userState></td:modifier><title
> type="text">NominalWorkItem-v1.png</title><published>2014-09-29T20:29:16.210Z</published><updated>2014-09-29T20:29:16.210Z</updated><td:created>2014-09-29T20:29:16.210Z</td:created><td:modified>2014-09-29T20:29:16.210Z</td:modified><td:lastAccessed></td:lastAccessed><content
> type="image/png"
> src="https://sdkdev.swg.usma.ibm.com:444/wikis/form/api/wiki/744ccaeb-e9af-434c-9fa6-78831fb94846/page/3b017473-2dbe-4920-85a2-bf908a8e1475/attachment/7780e11c-7240-41b3-8f0c-00a10ea69c93/media"></content></entry>
I think it is possible to do this with a multi-part post, but in my own code I'm doing it in steps:
POST the XML-Atom entry:
Url:
https://<Server>/<Wikis-Context-Root>/basic/api/wiki/<wikiUuid>/page/<pageUuid>/feed
Content:
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:td="urn:ibm.com/td">
<category label="attachment" scheme="tag:ibm.com,2006:td/type" term="attachment"/>
<td:label>file_name.extension</td:label>
</entry>
Headers:
Content-Type: <file mime-type, e.g. IMAGE/JPG>
Slug: <file_name.extension>
Response: 201; Location: URL to attachment entry
PUT the attachment content
Url: Location Response-Header of previous response, only exchange the last "/entry" for "/media"
Content: File-content stream
Headers:
Content-Type: <Mime-Type>
Response: 200 or 201
Optional: PUT a description for the file
Url: Location Response-Header of request from 1.)
Content:
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:td="urn:ibm.com/td">
<category label="attachment" scheme="tag:ibm.com,2006:td/type" term="attachment"/>
<summary type="text">MY ATTACHMENT DESCRIPTION TEXT</summary>
<td:label>file_name.extension</td:label>
</entry>
Headers:
Content-Type: application/atom+xml
Response: 200 or 201
I want to upload an image to docusign by c#, but it does not work. Below is my code that I wrote with mvc4.
Please help me,
Thanks!
public class UploadFileController : Controller
{
static string email = "***"; // your account email
static string password = "***"; // your account password
static string integratorKey = "***"; // your account Integrator Key (found on Preferences -> API page)
static string baseURL = ""; // - we will retrieve this
static string accountId = "***"; // - we will retrieve this
static string userId = "***";
static string signatureName = "signature";
#region FormUpload
public static class FormUpload
{
private static readonly Encoding encoding = Encoding.UTF8;
public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
{
string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
string contentType = "multipart/form-data; boundary=" + formDataBoundary;
byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);
return PostForm(postUrl, userAgent, contentType, formData);
}
private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
{
HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
if (request == null)
{
throw new NullReferenceException("request is not a http request");
}
// Set up the request properties.
request.Method = "PUT";
request.ContentType = contentType;
// request.UserAgent = userAgent;
request.CookieContainer = new CookieContainer();
request.ContentLength = formData.Length;
string authenticateStr =
"<DocuSignCredentials>" +
"<Username>" + email + "</Username>" +
"<Password>" + password + "</Password>" +
"<IntegratorKey>" + integratorKey + "</IntegratorKey>" + // global (not passed)
"</DocuSignCredentials>";
request.Headers.Add("X-DocuSign-Authentication", authenticateStr);
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(formData, 0, formData.Length);
requestStream.Close();
}
return request.GetResponse() as HttpWebResponse;
}
private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
{
Stream formDataStream = new System.IO.MemoryStream();
bool needsCLRF = false;
foreach (var param in postParameters)
{
// Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
// Skip it on the first parameter, add it to subsequent parameters.
if (needsCLRF)
formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));
needsCLRF = true;
if (param.Value is FileParameter)
{
FileParameter fileToUpload = (FileParameter)param.Value;
// Add just the first part of this param, since we will write the file data directly to the Stream
StringBuilder headerbuilder = new StringBuilder();
String header1, header2, header3, header4, header5, header6, header7, header8, header9, header10, header11, header12, header13;
header1 = String.Format("--{0}\r\nContent-Disposition: form-data; name =\"HTTPMethod\"", boundary);
header2 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"MethodName\"", boundary);
header3 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"MethodURI\"", boundary);
header4 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"BaseURL\"", boundary);
header5 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"PublicPath\"", boundary);
header6 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"Protocol\"", boundary);
header7 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"Version\"", boundary);
header8 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"Username\"", boundary);
header9 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"Password\"", boundary);
header10 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"IntegratorKey\"", boundary);
header11 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"pram[accountId]\"", boundary);
header12 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"param[userId]\"", boundary);
header13 = String.Format("\n--{0}\r\nContent-Disposition: form-data; name =\"signatureName\"", boundary);
//headerbuilder.Append(
string header = string.Format("\n--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
boundary,
param.Key,
fileToUpload.FileName ?? param.Key,
fileToUpload.ContentType ?? "application/octet-stream");
headerbuilder.Append(header1);
headerbuilder.Append(header2);
headerbuilder.Append(header3);
headerbuilder.Append(header4);
headerbuilder.Append(header5);
headerbuilder.Append(header6);
headerbuilder.Append(header7);
headerbuilder.Append(header8);
headerbuilder.Append(header9);
headerbuilder.Append(header10);
headerbuilder.Append(header11);
headerbuilder.Append(header12);
headerbuilder.Append(header13);
headerbuilder.Append(header);
String a = headerbuilder.ToString();
formDataStream.Write(encoding.GetBytes(headerbuilder.ToString()), 0, encoding.GetByteCount(headerbuilder.ToString()));
// Write the file data directly to the Stream, rather than serializing it to a string.
formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
}
else
{
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
boundary,
param.Key,
param.Value);
formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
}
}
// Add the end of the request. Start with a newline
string footer = "\r\n--" + boundary + "--\r\n";
formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));
// Dump the Stream into a byte[]
formDataStream.Position = 0;
byte[] formData = new byte[formDataStream.Length];
formDataStream.Read(formData, 0, formData.Length);
formDataStream.Close();
return formData;
}
public class FileParameter
{
public byte[] File { get; set; }
public string FileName { get; set; }
public string ContentType { get; set; }
public FileParameter(byte[] file) : this(file, null) { }
public FileParameter(byte[] file, string filename) : this(file, filename, null) { }
public FileParameter(byte[] file, string filename, string contenttype)
{
File = file;
FileName = filename;
ContentType = contenttype;
}
}
}
#endregion
public ActionResult Index()
{
// Read file data
FileStream fs = new FileStream("D:\\signature3.jpg", FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
fs.Close();
// Generate post objects
Dictionary<string, object> postParameters = new Dictionary<string, object>();
postParameters.Add("filename", "signature3.jpg");
postParameters.Add("fileformat", "jpg");
postParameters.Add("file", new FormUpload.FileParameter(data, "signature3.jpg", "image/jpg"));
// Create request and receive response
string postURL = "https://demo.docusign.net/restapi/v2/accounts/"+accountId+"/users/"+userId+"/signatures/"+signatureName+"/signature_image";
string userAgent = "Someone";
HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters);
// Process response
StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
string fullResponse = responseReader.ReadToEnd();
webResponse.Close();
Response.Write(fullResponse);
return View();
}
}
DocuSign imposes a 200K limit on signature image files. See the DocuSign REST API guide for more info. Page 247 describes the set signature image call:
http://docusign.com/sites/default/files/REST_API_Guide_v2.pdf
A confirmation and status pdf(certificate maybe) gets downloaded when i try to download the real pdf document that the user signed. I have a need to keep local copies of signed documents on my server.
public class DownloadEnvelopeDocs
{
static string email = "***";
static string password = "*****";
static string integratorKey = "*************";
public static string envelopeId = "******";
static string baseURL = "";
// main()
public static void Main()
{
try
{
//============================================================================
// STEP 1 - Login API Call (used to retrieve your baseUrl)
//============================================================================
// Endpoint for Login api call (in demo environment):
string url = "https://demo.docusign.net/restapi/v2/login_information";
// set request url, method, and headers. No body needed for login api call
HttpWebRequest request = initializeRequest(url, "GET", null, email, password);
// read the http response
string response = getResponseBody(request);
// parse baseUrl from response body
baseURL = parseDataFromResponse(response, "baseUrl");
//--- display results
Console.WriteLine("\nAPI Call Result: \n\n" + prettyPrintXml(response));
//============================================================================
// STEP 2 - Get Envelope Document(s) List and Info
//============================================================================
// append "/envelopes/{envelopeId}/documents" to to baseUrl and use for next endpoint
url = baseURL + "/envelopes/" + envelopeId + "/documents";
// set request url, method, body, and headers
request = initializeRequest(url, "GET", null, email, password);
// read the http response
response = getResponseBody(request);
// store each document name and uri locally, so that we can subsequently download each one
Dictionary<string, string> docsList = new Dictionary<string, string>();
string uri, name;
using (XmlReader reader = XmlReader.Create(new StringReader(response)))
{
while (reader.Read())
{
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "envelopeDocument"))
{
XmlReader reader2 = reader.ReadSubtree();
uri = ""; name = "";
while (reader2.Read())
{
if ((reader2.NodeType == XmlNodeType.Element) && (reader2.Name == "name"))
{
name = reader2.ReadString();
}
if ((reader2.NodeType == XmlNodeType.Element) && (reader2.Name == "uri"))
{
uri = reader2.ReadString();
}
}// end while
docsList.Add(name, uri);
}
}
}
//--- display results
Console.WriteLine("\nAPI Call Result: \n\n" + prettyPrintXml(response));
//============================================================================
// STEP 3 - Download the Document(s)
//============================================================================
foreach (KeyValuePair<string, string> kvp in docsList)
{
// append document uri to baseUrl and use to download each document(s)
url = baseURL + kvp.Value;
// set request url, method, body, and headers
request = initializeRequest(url, "GET", null, email, password);
request.Accept = "application/pdf"; // documents are converted to PDF in the DocuSign cloud
// read the response and store into a local file:
HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
string path = HttpContext.Current.Server.MapPath("~/Documents/") + envelopeId + ".pdf";
using (MemoryStream ms = new MemoryStream())
using (FileStream outfile = new FileStream(path, FileMode.Create))
{
webResponse.GetResponseStream().CopyTo(ms);
if (ms.Length > int.MaxValue)
{
throw new NotSupportedException("Cannot write a file larger than 2GB.");
}
outfile.Write(ms.GetBuffer(), 0, (int)ms.Length);
}
}
Console.WriteLine("\nDone downloading document(s), check local directory.");
}
catch (WebException e)
{
using (WebResponse response = e.Response)
{
HttpWebResponse httpResponse = (HttpWebResponse)response;
Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
using (Stream data = response.GetResponseStream())
{
string text = new StreamReader(data).ReadToEnd();
Console.WriteLine(prettyPrintXml(text));
}
}
}
} // end main()
//***********************************************************************************************
// --- HELPER FUNCTIONS ---
//***********************************************************************************************
public static HttpWebRequest initializeRequest(string url, string method, string body, string email, string password)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = method;
addRequestHeaders(request, email, password);
if (body != null)
addRequestBody(request, body);
return request;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static void addRequestHeaders(HttpWebRequest request, string email, string password)
{
// authentication header can be in JSON or XML format. XML used for this walkthrough:
string authenticateStr =
"<DocuSignCredentials>" +
"<Username>" + email + "</Username>" +
"<Password>" + password + "</Password>" +
"<IntegratorKey>" + integratorKey + "</IntegratorKey>" + // global (not passed)
"</DocuSignCredentials>";
request.Headers.Add("X-DocuSign-Authentication", authenticateStr);
request.Accept = "application/xml";
request.ContentType = "application/xml";
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static void addRequestBody(HttpWebRequest request, string requestBody)
{
// create byte array out of request body and add to the request object
byte[] body = System.Text.Encoding.UTF8.GetBytes(requestBody);
Stream dataStream = request.GetRequestStream();
dataStream.Write(body, 0, requestBody.Length);
dataStream.Close();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static string getResponseBody(HttpWebRequest request)
{
// read the response stream into a local string
HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
StreamReader sr = new StreamReader(webResponse.GetResponseStream());
string responseText = sr.ReadToEnd();
return responseText;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static string parseDataFromResponse(string response, string searchToken)
{
// look for "searchToken" in the response body and parse its value
using (XmlReader reader = XmlReader.Create(new StringReader(response)))
{
while (reader.Read())
{
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == searchToken))
return reader.ReadString();
}
}
return null;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
public static string prettyPrintXml(string xml)
{
// print nicely formatted xml
try
{
XDocument doc = XDocument.Parse(xml);
return doc.ToString();
}
catch (Exception)
{
return xml;
}
}
} // end class
The response to the Get Document List request (GET /accounts/{accountId}/envelopes/{envelopeId}/documents) will have an envelopeDocuments array that contains an object for each document in the Envelope, plus an object for the "Certificate of Completion" pdf (i.e., the certificate that DocuSign produces for each and every transaction, which contains audit trail information about the transaction). For example, the following Get Document List response is for an Envelope that contained 3 documents:
{
"envelopeId": "37A2A639-E9EF-4FD3-A390-3E6621897CD9",
"envelopeDocuments": [
{
"documentId": "1",
"name": "Test_1.pdf",
"type": "content",
"uri": "/envelopes/37A2A639-E9EF-4FD3-A390-3E6621897CD9/documents/1",
"order": "1",
"pages": "1"
},
{
"documentId": "2",
"name": "Test_2.pdf",
"type": "content",
"uri": "/envelopes/37A2A639-E9EF-4FD3-A390-3E6621897CD9/documents/2",
"order": "2",
"pages": "1"
},
{
"documentId": "3",
"name": "Test_3.pdf",
"type": "content",
"uri": "/envelopes/37A2A639-E9EF-4FD3-A390-3E6621897CD9/documents/3",
"order": "3",
"pages": "1"
},
{
"documentId": "certificate",
"name": "Summary",
"type": "summary",
"uri": "/envelopes/37A2A639-E9EF-4FD3-A390-3E6621897CD9/documents/certificate",
"order": "999",
"pages": "4"
}
]
}
Notice that the envelopeDocuments array contains four document objects -- one for each of the three documents in the Envelope, and one representing the "Certificate of Completion" pdf. If your issue is just that you do not want to download the Certificate of Completion, you can simply ignore (i.e., not process/download) the document object that has documentId = certificate.
I am using this code to upload some text directly to azure blob using rest api. I am getting an webexception 403 forbidden. Can someone plaese tell me where am i going wrong in my code
private String CreateAuthorizationHeader(String canonicalizedString, CloudBlob blob)
{
String signature = string.Empty;
using (HMACSHA256 hmacSha256 = new HMACSHA256())
{
Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
String authorizationHeader = String.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", "SharedKeyLite", myaccount, signature);
return authorizationHeader;
}
private void PutBlob(String containerName, String blobName , CloudBlob blob)
{
String requestMethod = "PUT";
String urlPath = String.Format("{0}", blobName);
String storageServiceVersion = "2009-10-01";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
// if (uploadPST.HasFile)
// {
string content = "Sample file";
// Stream content = uploadPST.FileBytes;
UTF8Encoding utf8Encoding = new UTF8Encoding();
Byte[] blobContent = utf8Encoding.GetBytes(content);
Int32 blobLength = blobContent.Length;
const String blobType = "BlockBlob";
/* String canonicalizedHeaders = String.Format(
"x-ms-blob-type:{0}\nx-ms-date:{1}\nx-ms-version:{2}",
blobType,
dateInRfc1123Format,
storageServiceVersion);*/
String canonicalizedHeaders = String.Format(
"x-ms-date:{0}\nx-ms-meta-m1:{1}\nx-ms-meta-m1:{2}",
dateInRfc1123Format,
"v1",
"v2");
String canonicalizedResource = String.Format("/{0}/{1}", myaccount, urlPath);
String stringToSign = String.Format(
"{0}\n\n{1}\n\n{2}\n{3}",
requestMethod,
"text/plain; charset=UTF-8",
canonicalizedHeaders,
canonicalizedResource);
String authorizationHeader = CreateAuthorizationHeader(stringToSign, blob);
Uri uri = new Uri(CloudStorageAccount.FromConfigurationSetting("DataConnectionString").BlobEndpoint + "/" + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
// request.Headers.Add("x-ms-blob-type", blobType);
request.Headers.Add("x-ms-date", dateInRfc1123Format);
// request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.ContentLength = blobLength;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(blobContent ,0 ,blobLength);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
String ETag = response.Headers["ETag"];
}
// }
}
First, you construct an HMACSHA256 object using the default constructor -- this causes a random key to be generated and used for signing. What you want is the overload which accepts a string - and pass the azure account key.
Still, signing the request 'manually' can be tricky, as there are a lot of things to do and it's easy to mess up or forget something. Instead, I recommend you use the SignRequest method of StorageCredentialsAccountAndKey class (msdn doc) For instance;
// ...there exists a request object, and strings for the account name and key
var creds = new StorageCredentialsAccountAndKey(accountName, accountKey);
creds.SignRequest(request);
This will do everything needed to sign the request properly, including creating a canonicalized headers string, creating a date with the correct format, etc.