Dotnet Migration Guide

View as Markdown

Migrating the Dotnet SDK from HelloSign to Dropbox.Sign

Welcome! Dropbox Sign's new Dotnet SDK is generated from our officially maintained OpenAPI spec. In this release, we've made important updates that introduce new functionality and create feature parity between the Dropbox Sign API and the Dotnet SDK. However, some of these changes are considered "breaking" in the sense that they'll require you to update your existing code in order to continue using the SDK.

In this migration guide, we'll cover core concepts in the new SDK, highlight differences from legacy versions, and provide example code showing how past implementations map to the new version's syntax. We'll link out to supporting documents that offer more context behind the breaking changes and why we made them. We apologize for any inconvenience these changes may cause, but are confident the new features will add value to your integration.

Remember that we are here to help if you have any questions or need assistance along the way. Thank you for using Dropbox Sign's Dotnet SDK. We hope you enjoy the improvements!

Architecture and Tooling

As mentioned above, the new Dotnet SDK (Dropbox.Sign) is generated from our OpenAPI Spec. Some of the architectural changes impact the tools and locations you use to interact with the SDK.

SDK Resources

  • Download -- using this new Nuget repo. New Dotnet SDK versions will now be published here.
  • Development -- active development against the Dotnet SDK happens here: hellosign-openapi/sdks/dotnet.
  • SDK GitHub Repo -- dropbox-sign-dotnet is updated based on changes to hellosign-openapi/sdks/dotnet, but no active development work happens there.
  • Reference Docs -- the automatically generated Reference Docs are a great way to explore the SDK.
  • Examples -- our full suite of ready to use examples will help you get started quickly.
  • Engagement -- use the OpenAPI repo to submit Issues or Pull Requests for the Dotnet SDK.

Core Concepts and Patterns

This section contains the core concepts and patterns of the new SDK and highlights differences from the legacy SDK.

Installation

There are two methods for installing the new OpenAPI Dotnet SDK:

Install with Nuget Package

  1. Install using this NuGet package: Dropbox.Sign:

    dotnet add package Dropbox.Sign —version 1.0.0

Build from Source

  1. Clone the Dotnet SDK repo: git clone https://github.com/hellosign/dropbox-sign-dotnet.git
  2. Ensure Docker is installed and running
  3. cd into dropbox-sign-dotnet
  4. Run: dotnet pack

    • This should generate a .nupkg with the path: src/Dropbox.Sign/bin/Debug/Dropbox.Sign.1.0.0-*.nupkg
    • Note: the version will be different depending on when you have cloned the repo
  5. Create a local NuGet.Config file at the root level of the existing project
  6. Add the following code to your NuGet.Config file:

    &lt;configuration&gt;<br />
    &nbsp;&nbsp;&lt;packageSources&gt;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;&lt;add key=&quot;local&quot; value=&quot;/absolute/path/to/Dropbox.Sign.1.0.0-*.nupkg&quot; /&gt;<br />
    &nbsp;&nbsp;&lt;/packageSources&gt;<br />
    &lt;/configuration&gt;
  7. Reference the package within the .csproj file:

    &lt;ItemGroup&gt;<br />
    &nbsp;&nbsp;&lt;PackageReference Include=&quot;Dropbox.Sign&quot; Version=&quot;1.0.0*&quot; /&gt;<br />
    &lt;/ItemGroup&gt;
  8. Add code to your project
  9. Run: dotnet run

Importing the SDK

Legacy SDK

  • Pulled in everything from a single namespace.

New Dotnet SDK

  • Requires 3 separate namespaces
1using HelloSign;

The New Dotnet SDK organizes methods and classes across multiple namespaces; each with a specific purpose.

NamespacePurpose
Dropbox.Sign.ApiA collection of functions to interact with the API endpoints
Dropbox.Sign.ClientA collection of classes that focus mainly on set-up, configuration, and exceptions
Dropbox.Sign.ModelA collection of models for all endpoint classes and sub classes

Authentication

To set-up your configuration and set the HTTP basic authenticator:

Legacy SDK

  • Option 1: Instantiate a new Client class with your API key.
  • Option 2: Instantiate a new Client class and pass OAuth Access Token in the .UseOAuth2Authentication() method.

New Dotnet SDK

  • Option 1: Instantiate a new Configuration class and set your API Key as the Username.
  • Option 2: Instantiate a new Configuration class and set bearer authorization: oauth 2 token as the AccessToken.
1var client = new Client(apiKey);
2
3// Include OAuth token if using OAuth
4// client.UseOAuth2Authentication("OAUTH ACCESS TOKEN HERE");
5
6// Use one the Client class methods to execute a call to an endpoint
7var account = client.GetAccount("ACCOUNT_CLIENT_ID);
8Console.Writeline(account.AccountId)

Endpoints Grouped into Classes

The new SDK divides endpoints across unique Classes:


Using Models to Pass Parameters

Models are used to define the structure and value of the parameters being passed. The fully assembled model is passed to the API endpoint method.

New SDK Using Models to Pass Parameters

1using Dropbox.Sign.Api;
2using Dropbox.Sign.Client;
3using Dropbox.Sign.Model;
4
5public class Example
6{
7 public static void Main()
8 {
9 var config = new Configuration();
10 config.Username = "YOUR_API_KEY";
11
12 var apiAppApi = new ApiAppApi(config);
13
14 var oauth = new SubOAuth(
15 callbackUrl: "https://example.com/oauth",
16 scopes: new List<SubOAuth.ScopesEnum>() {
17 SubOAuth.ScopesEnum.BasicAccountInfo,
18 SubOAuth.ScopesEnum.RequestSignature
19 }
20 );
21
22 var whiteLabelingOptions = new SubWhiteLabelingOptions(
23 primaryButtonColor: "#00b3e6",
24 primaryButtonTextColor: "#ffffff"
25 );
26
27 var customLogoFile = new FileStream(
28 "CustomLogoFile.png",
29 FileMode.Open
30 );
31
32 var data = new ApiAppCreateRequest(
33 name: "My Production App",
34 domains: new List<string>(){"example.com"},
35 oauth: oauth,
36 whiteLabelingOptions: whiteLabelingOptions,
37 customLogoFile: customLogoFile
38 );
39
40 try
41 {
42 var result = apiAppApi.ApiAppCreate(data);
43 Console.WriteLine(result);
44 }
45 catch (ApiException e)
46 {
47 Console.WriteLine("Exception when calling Dropbox Sign API: " + e.Message);
48 Console.WriteLine("Status Code: " + e.ErrorCode);
49 Console.WriteLine(e.StackTrace);
50 }
51 }
52}

Path and Query Parameters

In the legacy SDK you would pass Path and Query parameters alongside any POST data to the API endpoint:

Legacy SDK - Path and Query Parameters

1var client = new Client(apiKey);
2
3var emailAddress = "john@example.com";
4var signatureRequestId = "2f9781e1a8e2045224d808c153c2e1d3df6f8f2f";
5
6client.RemindSignatureRequest(signatureRequestId, emailAddress);

The new SDK now requires POST data be an object when calling any API endpoint. Path and Query parameters must be passed individually to these methods.

New SDK - Path and Query Parameters

1using Dropbox.Sign.Api;
2using Dropbox.Sign.Client;
3using Dropbox.Sign.Model;
4
5public class Example
6{
7 public static void Main()
8 {
9 var config = new Configuration();
10 // Configure HTTP basic authorization: api_key
11 config.Username = "YOUR_API_KEY";
12
13 // or, configure Bearer authorization: oauth2
14 // config.AccessToken = "YOUR_BEARER_TOKEN";
15
16 var signatureRequestApi = new SignatureRequestApi(config);
17
18 var data = new SignatureRequestRemindRequest(
19 emailAddress: "john@example.com"
20 );
21
22 var signatureRequestId = "2f9781e1a8e2045224d808c153c2e1d3df6f8f2f";
23
24 try
25 {
26 var result = signatureRequestApi.SignatureRequestRemind(signatureRequestId, data);
27 Console.WriteLine(result);
28 }
29 catch (ApiException e)
30 {
31 Console.WriteLine("Exception when calling Dropbox Sign API: " + e.Message);
32 Console.WriteLine("Status Code: " + e.ErrorCode);
33 Console.WriteLine(e.StackTrace);
34 }
35 }
36}

Error Handling and Warnings

The New SDK handles errors and warnings differently. See the table below for a breakdown of the changes:

TypeLegacy SDKNew SDK
ErrorsUses ErrorException class
Provides:
  • Error Name
  • Error Message
Uses ApiException class
Provides:
  • Error Code
  • Error Message
  • Headers
WarningsUses Client class list of warnings objects (Client.Warnings)
  • Logged based on user preference
Uses WarningResponse class
  • Passed in response object and and can be looped through and logged when warnings are present

Error Handling

Errors are an instance of Dropbox.Sign.Client.ApiException with its ErrorContent getter returning an instance of Dropbox.Sign.Model.ErrorResponse class and should be handled using Try/Catch blocks.

1var client = new Client(apiKey);
2// Set invalid email address to cause error
3string emailAddress = "dropbox.com";
4try
5{
6 var account = client.VerifyAccount(emailAddress);
7 Console.WriteLine(account);
8}
9// Use the ErrorException class to display error name and message
10catch(HelloSign.ErrorException e)
11{
12 Console.WriteLine("Status Code: " + e.ErrorName);
13 Console.WriteLine("Exception when calling Dropbox Sign API: " + e.Message);
14}

Warnings

Warnings are a list of Dropbox.Sign.Model.WarningResponse.

1// Client setup
2var client = new Client(apiKey);
3var request = new SignatureRequest();
4request.Subject = "The NDA we talked about";
5request.Message = "Please sign this NDA and then we can discuss more. Let me know if you have any questions.";
6request.Title = "NDA with Acme Co.";
7request.AddSigner("jack@dropboxsign.com", "Jack");
8request.AddSigner("jill@dropboxsign.com", "Jill");
9request.AddFile("c:\users\me\My Documents\nda.txt");
10// Adjusted parameters so that form field is out of bounds of the document to cause warning
11FormField formField1 = new FormField("uniqueIeHere_1", "signature", 1, 45, 985, 270, 40, true, 0, null, null, null);
12request.AddFormField(formField1);
13FormField formField2 = new FormField("uniqueIdHere_2", "signature", 1, 16, 100, 112, 40, true, 1, null, null, null);
14request.AddFormField(formField2);
15request.TestMode = true;
16// Send request
17var response = client.SendSignatureRequest(request, clientId);
18Console.WriteLine("New Embedded Signature Request ID: " + response.SignatureRequestId);
19requestId = response.SignatureRequestId;
20int num = 1;
21// Loop through warnings in the Client instance
22foreach (var warning in client.Warnings)
23{
24 Console.WriteLine($"{num}: {warning.WarningName} - {warning.WarningMsg}");
25 num++;
26}

Instantiating Objects From Data

There are three ways to instantiate an object.

  • You can instantiate a class directly and use constructor arguments
  • You can use setter methods
  • You can use the Init() static method
1var signer1 = new SubSignatureRequestSigner(
2 emailAddress: "jack@example.com",
3 name: "Jack",
4 order: 0
5);
6
7var attachment1 = new SubAttachment(
8 name: "Attachment 1",
9 instructions: "Please download this file",
10 signerIndex: 0
11);
12attachment1.Required = true;
Note

Init() creates a full object using all the data you pass, including nested data to instantiate nested objects. Any parameters that you do not pass data for will be set to their default value (including null).


Event Callback Helper

A callback helper class is included in the New SDK repo to assist in verifying callbacks. The helper simplifies:

  1. Checking event authenticity with built in event hash check
  2. Displaying event types (account callback vs. app callback)
  3. Displaying event messages

The EventCallbackHelper and EventCallbackRequest classes facilitate parsing of event data and assist in validating that a callback originated from Dropbox Sign.

We will send event callback payloads to you as a multipart/form-data request with a single json formfield that contains your event callback as a JSON string.

Example Event Callback Request From US to YOU

$curl -X POST 'https://example.com/YOUR_EVENT_CALLBACK_URL' \
> -F 'json={"event":{"event_type":"account_confirmed","event_time":"1669926463","event_hash":"ff8b03439122f9160500c3fb855bdee5a9ccba5fff27d3b258745d8f3074832f","event_metadata":{"related_signature_id":null,"reported_for_account_id":"6421d70b9bd45059fa207d03ab8d1b96515b472c","reported_for_app_id":null,"event_message":null}}}'

Example JSON Payload

1{
2 "event": {
3 "event_type": "account_confirmed",
4 "event_time": "1669926463",
5 "event_hash": "ff8b03439122f9160500c3fb855bdee5a9ccba5fff27d3b258745d8f3074832f",
6 "event_metadata": {
7 "related_signature_id": null,
8 "reported_for_account_id": "6421d70b9bd45059fa207d03ab8d1b96515b472c",
9 "reported_for_app_id": null,
10 "event_message": null
11 }
12 }
13}

How to use the EventCallbackHelper

1using Newtonsoft.Json;
2using Dropbox.Sign.Model;
3using Dropbox.Sign;
4
5public class Example
6{
7 public static void Main()
8 {
9 // use your API key
10 var apiKey = "324e3b0840f065eb51f3fd63231d0d33daa35d4ed10d27718839e81737065782";
11
12 // callbackData represents data we send to you
13 var callbackData = Request.Form["json"];;
14
15 var callbackEvent = EventCallbackRequest.Init(callbackData);
16
17 // verify that a callback came from HelloSign.com
18 if (EventCallbackHelper.IsValid(apiKey, callbackEvent))
19 {
20 // one of "account_callback" or "api_app_callback"
21 var callbackType = EventCallbackHelper.GetCallbackType(callbackEvent);
22
23 // do your magic below!
24 }
25 }
26}

Differences from Legacy SDK

This section highlights larger changes to be aware of when migrating to the new SDK.

Form Fields per Document

The Form Fields per Document parameter has changed from a two dimensional array, to a one dimensional array—allowing you to designate which file you to add the field to using document_index. You can learn more about this change here: Form Fields per Document.

1var request = new SignatureRequest();
2request.Title = "NDA with Acme Co.";
3request.Subject = "The NDA we talked about";
4request.Message = "Please sign this NDA and then we can discuss more. Let me know if you have any questions.";
5request.AddSigner("jack@example.com", "Jack");
6request.AddSigner("jill@example.com", "Jill");
7request.AddFile("https://app.hellosign.com/docs/example_signature_request.pdf").WithFields(
8// id type page x y w h req signer
9 new FormField("sig1", FormField.TypeSignature, 1, 140, 72, 225, 52, true, 0),
10 new FormField("sig2", FormField.TypeSignature, 1, 140, 144, 225, 52, true, 1)
11);
12request.TestMode = true;
13var response = client.CreateEmbeddedSignatureRequest(request, clientId);

Instantiating the Correct Field Class

There are several different types of form fields you can define, identified by the value of the type field and a few ways to instantiate the correct object when making an API request.

The different classes for each type are:

You can use ::init() on the base request class

1var dataDict = new Dictionary<string, object>()
2{
3 ["signers"] = new List<Dictionary<string, object>>() {
4 new Dictionary<string, object>()
5 {
6 ["email_address"] = "example+1@dropbox.com",
7 ["name"] = "Jack",
8 ["order"] = 0,
9 },
10 },
11 ["form_fields_per_document"] = new List<Dictionary<string, object>>() {
12 new Dictionary<string, object>()
13 {
14 ["type"] = "signature",
15 ["document_index"] = 0,
16 ["api_id"] = "4688957689",
17 ["name"] = "signature1",
18 ["x"] = 5,
19 ["y"] = 7,
20 ["width"] = 60,
21 ["height"] = 30,
22 ["required"] = true,
23 ["signer"] = 0,
24 ["page"] = 1,
25 }
26 },
27};
28
29var data = SignatureRequestSendRequest.Init(JsonConvert.SerializeObject(dataDict));

You can use ::init() on the field class

1var formField1Dict = new Dictionary<string, object>()
2{
3 ["type"] = "signature",
4 ["document_index"] = 0,
5 ["api_id"] = "4688957689",
6 ["name"] = "signature1",
7 ["x"] = 5,
8 ["y"] = 7,
9 ["width"] = 60,
10 ["height"] = 30,
11 ["required"] = true,
12 ["signer"] = 0,
13 ["page"] = 1,
14};
15
16var formField1 = SubFormFieldsPerDocumentSignature.Init(
17 JsonConvert.SerializeObject(formField1Dict)
18);

You can instantiate the class directly

1var formField1 = new SubFormFieldsPerDocumentSignature(
2 documentIndex: 0,
3 apiId: "4688957689",
4 name: "signature1",
5 x: 5,
6 y: 7,
7 width: 60,
8 height: 30,
9 required: true,
10 signer: 0,
11 page: 1
12);

Form Fields per Document Examples using the new SDK:

1var formField1Dict = new Dictionary<string, object>()
2{
3 ["document_index"] = 0,
4 ["api_id"] = "4688957689",
5 ["name"] = "signature1",
6 ["x"] = 5,
7 ["y"] = 7,
8 ["width"] = 60,
9 ["height"] = 30,
10 ["required"] = true,
11 ["signer"] = 0,
12 ["page"] = 1,
13};
14
15var formField1 = SubFormFieldsPerDocumentSignature.Init(
16 JsonConvert.SerializeObject(formField1Dict)
17);

“role” Value in signers Object

In the Legacy SDK when making a Signature Request using a Template the signers property was an object with the role name as the key. In the new SDK the role value has been moved into the signer object itself.

For example for the /signature_request/send_with_template endpoint the signers property could be represented as:

1{
2 "signers": {
3 "Client": {
4 "name": "George",
5 "email_address": "george@example.com"
6 },
7 "Manager": {
8 "name": "Bob",
9 "email_address": "bob@example.com"
10 }
11 }
12}

Using the new SDK you would now send this data as follows:

1using Dropbox.Sign.Api;
2using Dropbox.Sign.Client;
3using Dropbox.Sign.Model;
4using Newtonsoft.Json;
5
6public class Example
7{
8 public static void Main()
9 {
10 var config = new Configuration();
11 config.Username = "YOUR_API_KEY";
12
13 var signatureRequestApi = new SignatureRequestApi(config);
14
15 var signer1Dict = new Dictionary<string, object>()
16 {
17 ["role"] = "Client",
18 ["name"] = "George",
19 ["email_address"] = "george@example.com",
20 };
21
22 var signer1 = SubSignatureRequestTemplateSigner.Init(
23 JsonConvert.SerializeObject(signer1Dict)
24 );
25
26 var signer2Dict = new Dictionary<string, object>()
27 {
28 ["role"] = "Manager",
29 ["name"] = "Bob",
30 ["email_address"] = "bob@example.com",
31 };
32
33 var signer2 = SubSignatureRequestTemplateSigner.Init(
34 JsonConvert.SerializeObject(signer2Dict)
35 );
36
37 var data = new SignatureRequestSendWithTemplateRequest(
38 templateIds: new List<string>(){"c26b8a16784a872da37ea946b9ddec7c1e11dff6"},
39 subject: "Purchase Order",
40 message: "Glad we could come to an agreement.",
41 signers: new List<SubSignatureRequestTemplateSigner>(){signer1, signer2}
42 );
43
44 try
45 {
46 var result = signatureRequestApi.SignatureRequestSendWithTemplate(data);
47 Console.WriteLine(result);
48 }
49 catch (ApiException e)
50 {
51 Console.WriteLine("Exception when calling Dropbox Sign API: " + e.Message);
52 Console.WriteLine("Status Code: " + e.ErrorCode);
53 Console.WriteLine(e.StackTrace);
54 }
55 }
56}

“role” Value in ccs Property

In the Legacy SDK when making a Signature Request using a Template the ccs property was an object with the role name as the key. In the new SDK the role value has been moved into the cc object itself, alongside a new email_address property.

For example for the /signature_request/send_with_template endpoint the ccs property could be represented as:

1{
2 "ccs": {
3 "Client": "george@example.com",
4 "Manager": "bob@example.com"
5 }
6}

Using the new SDK you would now send this data as follows:

1using Dropbox.Sign.Api;
2using Dropbox.Sign.Client;
3using Dropbox.Sign.Model;
4using Newtonsoft.Json;
5
6public class Example
7{
8 public static void Main()
9 {
10 var config = new Configuration();
11 config.Username = "YOUR_API_KEY";
12
13 var signatureRequestApi = new SignatureRequestApi(config);
14
15 var signer1Dict = new Dictionary<string, object>()
16 {
17 ["role"] = "Client",
18 ["name"] = "George",
19 ["email_address"] = "george@example.com",
20 };
21
22 var signer1 = SubSignatureRequestTemplateSigner.Init(
23 JsonConvert.SerializeObject(signer1Dict)
24 );
25
26 var signer2Dict = new Dictionary<string, object>()
27 {
28 ["role"] = "Manager",
29 ["name"] = "Bob",
30 ["email_address"] = "bob@example.com",
31 };
32
33 var signer2 = SubSignatureRequestTemplateSigner.Init(
34 JsonConvert.SerializeObject(signer2Dict)
35 );
36
37 var cc1Dict = new Dictionary<string, object>()
38 {
39 ["role"] = "Client",
40 ["email_address"] = "george@example.com",
41 };
42
43 var cc1 = SubCC.Init(
44 JsonConvert.SerializeObject(cc1Dict)
45 );
46
47 var cc2Dict = new Dictionary<string, object>()
48 {
49 ["role"] = "Manager",
50 ["email_address"] = "bob@example.com",
51 };
52
53 var cc2 = SubCC.Init(
54 JsonConvert.SerializeObject(cc2Dict)
55 );
56
57 var data = new SignatureRequestSendWithTemplateRequest(
58 templateIds: new List<string>(){"c26b8a16784a872da37ea946b9ddec7c1e11dff6"},
59 subject: "Purchase Order",
60 message: "Glad we could come to an agreement.",
61 signers: new List<SubSignatureRequestTemplateSigner>(){signer1, signer2},
62 ccs: new List<SubCC>(){cc1, cc2}
63 );
64
65 try
66 {
67 var result = signatureRequestApi.SignatureRequestSendWithTemplate(data);
68 Console.WriteLine(result);
69 }
70 catch (ApiException e)
71 {
72 Console.WriteLine("Exception when calling Dropbox Sign API: " + e.Message);
73 Console.WriteLine("Status Code: " + e.ErrorCode);
74 Console.WriteLine(e.StackTrace);
75 }
76 }
77}

“name” Value in custom_fields Property

In the Legacy SDK when making a Signature Request with the custom_fields property it was an object with the name as the key. In the new SDK the name value has been moved into the custom_field object itself.

For example for the /signature_request/send_with_template endpoint the custom_fields property could be represented as:

1{
2 "custom_fields": {
3 "company": {
4 "value": "ABC Corp",
5 "required": true
6 }
7 }
8}

Using the new SDK you would now send this data as follows:

1using Dropbox.Sign.Api;
2using Dropbox.Sign.Client;
3using Dropbox.Sign.Model;
4using Newtonsoft.Json;
5
6public class Example
7{
8 public static void Main()
9 {
10 var config = new Configuration();
11 config.Username = "YOUR_API_KEY";
12
13 var signatureRequestApi = new SignatureRequestApi(config);
14
15 var signer1Dict = new Dictionary<string, object>()
16 {
17 ["role"] = "Client",
18 ["name"] = "George",
19 ["email_address"] = "george@example.com",
20 };
21
22 var signer1 = SubSignatureRequestTemplateSigner.Init(
23 JsonConvert.SerializeObject(signer1Dict)
24 );
25
26 var signer2Dict = new Dictionary<string, object>()
27 {
28 ["role"] = "Manager",
29 ["name"] = "Bob",
30 ["email_address"] = "bob@example.com",
31 };
32
33 var signer2 = SubSignatureRequestTemplateSigner.Init(
34 JsonConvert.SerializeObject(signer2Dict)
35 );
36
37 var customField1Dict = new Dictionary<string, object>()
38 {
39 ["name"] = "company",
40 ["value"] = "ABC Corp",
41 ["required"] = true,
42 };
43
44 var customField1 = SubCustomField.Init(
45 JsonConvert.SerializeObject(customField1Dict)
46 );
47
48 var data = new SignatureRequestSendWithTemplateRequest(
49 templateIds: new List<string>(){"c26b8a16784a872da37ea946b9ddec7c1e11dff6"},
50 subject: "Purchase Order",
51 message: "Glad we could come to an agreement.",
52 signers: new List<SubSignatureRequestTemplateSigner>(){signer1, signer2},
53 customFields: new List<SubCustomField>(){customField1}
54 );
55
56 try
57 {
58 var result = signatureRequestApi.SignatureRequestSendWithTemplate(data);
59 Console.WriteLine(result);
60 }
61 catch (ApiException e)
62 {
63 Console.WriteLine("Exception when calling Dropbox Sign API: " + e.Message);
64 Console.WriteLine("Status Code: " + e.ErrorCode);
65 Console.WriteLine(e.StackTrace);
66 }
67 }
68}

template_id to template_ids

The template_id parameter has been removed. You must now use template_ids.

Legacy SDK versionNew SDK Version
Template ID (template_id) is passed as a singular string:

template_id : “1234567890”
Template ID is passed as an array of strings (template_ids):

template_ids:[“1234567890”]

file to files

The file parameter has been renamed to files. Usage remains the same.

Use .Files([...]);


file_url to file_urls

The file_url parameter has been renamed to file_urls. Usage remains the same.

Use .FileUrls([...]);


Interacting with Files

The new SDK version introduces some new patterns around uploading and downloading files. You can read about them more in depth here: Interacting with Files.

Uploading Files

The file upload process changed with the newly-adopted OpenAPI-specification — it is no longer possible to pass file paths into file upload methods. You can learn more about this update here: New Patterns: Interacting with Files.

The New Dotnet SDK only accepts files passed as binary. For more information, see Interacting with Files.

BehaviorLegacy SDKNew SDK
File Upload
  • Pass file path with file parameter
  • file parameter does not accept file path
  • Requires a binary (List of System.IO.Stream)

Please see the comments in the New Dotnet SDK code sample below for detailed description of passing the file as binary.

1var request = new SignatureRequest();
2request.Title = "NDA with Acme Co.";
3request.Subject = "The NDA we talked about";
4request.Message = "Please sign this NDA and then we can discuss more. Let me know if you have any questions.";
5request.AddSigner("jack@example.com", "Jack");
6request.AddFile("c:\users\me\My Documents\nda.pdf");
7request.TestMode = true;
8var response = client.SendSignatureRequest(request);
9Console.WriteLine("New Signature Request ID: " + response.SignatureRequestId);

Downloading Files

The New Dotnet SDK downloads files differently. You can learn more about why this change was made here: Interacting with Files.

Legacy SDK

    Switch between file format using parameters sent to a single endpoint for either downloading files or downloading templates.

New Dotnet SDK

    Switch between file formats by calling an endpoint specific to that format. You can see how those are split up in the table below.

1// 1. Download a merged PDF
2var bytes = client.DownloadSignatureRequestFiles("SIGNATURE REQUEST ID HERE");
3// 2. Download a ZIP containing individual unmerged PDFs
4var bytes = client.DownloadSignatureRequestFiles(
5 "SIGNATURE REQUEST ID HERE",
6 SignatureRequest.FileType.ZIP
7);

Downloading Templates

1// 1. Download a merged PDF
2var bytes = client.DownloadTemplateFiles("TEMPLATE ID HERE");
3// 2. Download a ZIP containing individual unmerged PDFs
4var bytes = client.DownloadTemplateFiles(
5 "TEMPLATE ID HERE",
6 SignatureRequest.FileType.ZIP
7);

Endpoint Mapping

Get a head start migrating or planning your migration using the code examples in this section. We created examples for our most-used endpoints. Reach out if you think we’re missing an important one.

Update API App

1// The Legacy SDK did not support the Update API App endpoint, but here is an example of how oauth and white labeling options were passed in the Create Api App endpoint
2// Inject white_labeling_options into AdditionalParameters
3client.AdditionalParameters.Add("white_labeling_options", "{ \"header_background_color\":\"#1A1A1A\", \"page_background_color\": \"#F7F8F9\", \"legal_version\":\"terms2\"}");
4// Create an instance of Oauth and set the new Oauth callback URL and scopes
5var oauth = new Oauth()
6{
7 CallbackUrl = "https://example.com/oauth",
8 Scopes = new List<string>()
9 {
10 "basic_account_info",
11 "request_signature"
12 }
13};
14// Create new instance of ApiApp
15var apiAppApi = new ApiApp();
16apiAppApi.Name = "Test App 1";
17apiAppApi.Domain = "test1.com";
18apiAppApi.Oauth = oauth;
19apiAppApi.CallbackUrl = "https://example.com/";
20// Send request
21var response = client.CreateApiApp(apiAppApi);
22Console.WriteLine(response.ClientId);

Send Signature Request with Custom Fields

1// Create new instance of TemplateSignatureRequest
2var request = new TemplateSignatureRequest();
3request.AddTemplate("1cba0c693fe6fc2bdd3b9967f704740e7631696c");
4request.Title = "Rental Agreement";
5request.Subject = "Contract for new apartment";
6request.Message = "Please sign this Rental Agreement.";
7request.AddSigner("Client 1", "jack@dropboxsign.com", "Jack");
8// Add custom field
9request.AddCustomField("Address", "123 Dropbox Drive", "Client 1", true);
10request.TestMode = true;
11// Send request
12var response = client.SendSignatureRequest(request);
13Console.WriteLine("New Signature Request ID: " + response.SignatureRequestId);

Send Signature Request with Form Fields Per Document

1var request = new SignatureRequest();
2request.Title = "NDA with Acme Co.";
3request.Subject = "The NDA we talked about";
4request.Message = "Please sign this NDA and then we can discuss more. Let me know if you have any questions.";
5request.AddSigner("jack@example.com", "Jack");
6request.AddSigner("jill@example.com", "Jill");
7request.AddFile("https://app.hellosign.com/docs/example_signature_request.pdf").WithFields(
8// id type page x y w h req signer
9 new FormField("sig1", FormField.TypeSignature, 1, 140, 72, 225, 52, true, 0),
10 new FormField("sig2", FormField.TypeSignature, 1, 140, 144, 225, 52, true, 1)
11);
12request.TestMode = true;
13var response = client.CreateEmbeddedSignatureRequest(request, clientId);

List Signature Requests with query parameter

1var client = new Client(apiKey);
2// Inject additional parameters for account_id and query
3client.AdditionalParameters.Add("account_id", "all");
4client.AdditionalParameters.Add("query", "complete:true");
5var queryList = client.ListSignatureRequests(0, 100);
6// Remove injected parameters before next request
7client.AdditionalParameters.Remove("account_id");
8client.AdditionalParameters.Remove("query");
9Console.WriteLine("Found this many signature requests: " + allRequests.NumResults);
10foreach (var result in queryList)
11{
12 Console.WriteLine("Signature request: " + result.SignatureRequestId);
13 if (result.IsComplete) == true) { Console.WriteLine("Signature request is complete.");}
14 else { Console.WriteLine("Signature request is not complete");}
15}

Create Embedded Template Draft with Merge Fields

1var draft = new EmbeddedTemplateDraft();
2draft.TestMode = true;
3draft.AddFile(file1, "NDA.txt");
4draft.Title = "Test Template";
5draft.Subject = "Please sign this document";
6draft.Message = "For your approval.";
7draft.AddSignerRole("Client", 0);
8draft.AddSignerRole("Witness", 1);
9draft.AddCcRole("Manager");
10draft.AddMergeField("Full Name", MergeField.FieldType.Text);
11draft.AddMergeField("Is Registered?", MergeField.FieldType.Checkbox);
12var response = client.CreateEmbeddedTemplateDraft(draft, "CLIENT ID HERE");

Create Embedded Signature Request

1var request = new SignatureRequest();
2request.Title = "NDA with Acme Co.";
3request.Subject = "The NDA we talked about";
4request.Message = "Please sign this NDA and then we can discuss more. Let me know if you have any questions.";
5request.AddSigner("jack@example.com", "Jack");
6request.AddSigner("jill@example.com", "Jill");
7request.AddFile("c:\users\me\My Documents\example_signature_request.pdf");;
8request.Metadata.Add("custom_id", "1234");
9request.Metadata.Add("custom_text", "NDA #9");
10request.TestMode = true;
11var response = client.CreateEmbeddedSignatureRequest(request, "CLIENT ID HERE");
12Console.WriteLine("New Embedded Signature Request ID: " + response.SignatureRequestId);

Get Embedded Sign URL

1var response = client.GetSignUrl("SIGNATURE ID HERE");
2Console.WriteLine("Signature URL for HelloSign.open(): " + response.SignUrl);

Create Embedded Unclaimed Draft with file

1var draft = new SignatureRequest();
2draft.AddFile("DOCUMENT A.pdf");
3draft.RequesterEmailAddress = "EMAIL HERE";
4draft.TestMode = true;
5var response = client.CreateUnclaimedDraft(draft, myClientId);
6Console.WriteLine("Embedded Unclaimed Draft Signature Request ID: " + response.SignatureRequestId);
7Console.WriteLine("Claim URL: " + response.ClaimUrl);

Supporting Legacy SDKs

Following the official release of the new SDK version, we'll be preparing to deprecate all legacy versions of the Dotnet SDK for one year after launch. That means, once fully launched, we'll only support critical vulnerabilities and bug fixes for legacy SDK versions 1.5.3 for 12 months. After that, legacy versions are considered officially deprecated and are no longer supported. You can find more information in our SDK Versioning Policy.

We encourage you to start migrating (or planning to migrate) to the new SDK as soon as possible. Please don't hesitate to reach out if you have questions or need assistance.


Feedback and Assistance

We know that dealing with "breaking" changes is inconvenient for those of you currently using the legacy SDK, but believe the new SDK offers a better experience while providing access to more features. If you need help or get stuck, please reach out to API support: Submit a Ticket

We warmly welcome your feedback, feature requests, and bug reports in our OpenAPI repo. All of the engineering work on the Dotnet SDK happens in the Dotnet folder of the repo.