This reference implementation demonstrates data input and output functionality for an extension app.
To test a read-only data IO extension app, modify one of the ReadOnlyManifest.json files.
To test a data IO extension app with both read and write capabilities, modify one of the ReadWriteManifest.json files.
This reference implementation supports two authentication flows:
- Authorization Code Grant – required for public extension apps
- Client Credentials Grant – available to private extension apps. See Choosing private distribution instead of public
Private extension apps can use either authentication method, but public extension apps must use Authorization Code Grant.
manifests/
authorizationCode/
ReadOnlyManifest.json
ReadWriteManifest.json
clientCredentials/
ReadOnlyManifest.json
ReadWriteManifest.json
hosted/
authorizationCode.ReadOnlyManifest.json
authorizationCode.ReadWriteManifest.json
clientCredentials.ReadOnlyManifest.json
clientCredentials.ReadWriteManifest.json
Run the following command to clone the repository:
git clone https://github.com/docusign/extension-app-data-io-reference-implementation-csharp.git- If you already have values for
JwtSecretKey,OAuthClientId,OAuthClientSecret, andAuthorizationCode, you may skip this step.
The easiest way to generate a secret value is to run the following command:
[Convert]::ToHexString([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(64))openssl rand -hex 64Each command generates a 128-character hexadecimal string (64 random bytes) that can be used as a secret value. Generate separate values for JwtSecretKey, OAuthClientId, OAuthClientSecret, and AuthorizationCode.
- If you're running this in a development environment, create a copy of
appsettings.Development.jsonand save it asappsettings.json. - Replace
JwtSecretKey,OAuthClientId,OAuthClientSecret, andAuthorizationCodeinappsettings.jsonwith your generated values. These values will be used to configure the sample proxy's mock authentication server. - Set the
clientIdvalue in the manifest file to the same value asOAuthClientId. - Set the
clientSecretvalue in the manifest file to the same value asOAuthClientSecret.
Start the proxy server in development mode by running the command:
dotnet runThis will create a local server on the port 5245 by default.
Run the following command to create a publicly accessible tunnel to your localhost:
ngrok http 5245Copy the Forwarding address from the response. You’ll need this address in your manifest file.
ngrok
Send your ngrok traffic logs to Datadog: https://ngrok.com/blog-post/datadog-log
Session Status online
Account email@domain.com (Plan: Free)
Update update available (version 3.3.1, Ctrl-U to update)
Version 3.3.0
Region United States (us)
Latency 60ms
Web Interface http://127.0.0.1:4040
Forwarding https://bbd7-12-202-171-35.ngrok-free.app -> http:
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00In this example, the Forwarding address to copy is https://bbd7-12-202-171-35.ngrok-free.app.
Replace <PROXY_BASE_URL> in your manifest file with the ngrok forwarding address in the following sections:
connections.params.customConfig.tokenUrlin all manifestsconnections.params.customConfig.authorizationUrlin the authorization code manifestsactions.params.urifor all actions
2. Navigate to the Developer Console
Log in with your Docusign developer credentials and create a new app.
To create your extension app, open the Developer Console and select +New App. In the app manifest editor that opens, upload your manifest file or paste into the editor itself; then select Validate. Once the editor validates your manifest, select Create App.
This reference implementation uses mock data to simulate how data can be verified against a database. Test your extension using the sample data in ExtensionAppDataIO/Data/MockDb. Extension app tests include integration tests (connection tests and extension tests), functional tests, and App Center preview.
The Developer Console offers five extension tests to verify that a data IO extension app can connect to and exchange data with third-party APIs (or an API proxy that in turn connects with those APIs).
Note: These instructions only apply if you use the mock data in ExtensionAppDataIO/Data/MockDb. If you use your own database, you’ll need to construct your requests based on your own schema. Queries for extension tests in the Developer Console are built using the IQuery structure.
To begin the extension test process, run the CreateRecord test using the sample query below. The test should return a response containing the record ID.
{
"typeName": "Account",
"idempotencyKey": "NOT_USED_CURRENTLY",
"data": {
"Name": "Test Account",
"ShippingLatitude": 10,
"PushCount": 6
}
}All record types are located in the ExtensionAppDataIO/Data/MockDb/ folder of this repository.
Open ExtensionAppDataIO/Data/MockDb/Account.json and check that the records were created.
This query searches the records that have been created. You do not have to use the same sample values used here; the search should work with a valid attribute in Account.json.
Open the SearchRecords test and create a new query based on Account.json:
- The
fromattribute maps to the value oftypeNamein the CreateRecord query; in this case,Account. - The
dataobject from the CreateRecord query maps to theattributesToSelectarray; in this case,Name. - The
nameproperty of theleftOperandobject should be the value ofName; in this case,Test Account. - The
operatorvalue should beEQUALS. - The
nameproperty of therightOperandobject should be the same as what's inattributesToSelectarray; in this case,Name.
The query below has been updated based on the directions above. You can copy and paste this into the SearchRecords test input box.
{
"query": {
"$class": "com.docusign.connected.data.queries@1.0.0.Query",
"attributesToSelect": [
"Name"
],
"from": "Account",
"queryFilter": {
"$class": "com.docusign.connected.data.queries@1.0.0.QueryFilter",
"operation": {
"$class": "com.docusign.connected.data.queries@1.0.0.ComparisonOperation",
"leftOperand": {
"$class": "com.docusign.connected.data.queries@1.0.0.Operand",
"name": "Test Account",
"type": "STRING",
"isLiteral": true
},
"operator": "EQUALS",
"rightOperand": {
"$class": "com.docusign.connected.data.queries@1.0.0.Operand",
"name": "Name",
"type": "INTEGER",
"isLiteral": false
}
}
}
},
"pagination": {
"limit": 10,
"skip": 10
}
}Running the test will return the record you queried.
The recordId property in the sample input maps to an Id in Account.json. Any valid record ID can be used in this field.
In the data object, include any attributes and values to be added to the record. In this query, a new property will be added, and the original data in the record will be updated.
{
"recordId": "2",
"typeName": "Account",
"idempotencyKey": "NOT_USED_CURRENTLY",
"data": {
"Name": "updatedTestAccount",
"ShippingLatitude": 11,
"PushCount": 7,
"MasterRecordId": "ABCD"
}
}Running the test should return the response "success": true.
Rerun the SearchRecords extension test to search for the new patched values.
Input query:
{
"query": {
"$class": "com.docusign.connected.data.queries@1.0.0.Query",
"attributesToSelect": [
"Name"
],
"from": "Account",
"queryFilter": {
"$class": "com.docusign.connected.data.queries@1.0.0.QueryFilter",
"operation": {
"$class": "com.docusign.connected.data.queries@1.0.0.ComparisonOperation",
"leftOperand": {
"$class": "com.docusign.connected.data.queries@1.0.0.Operand",
"name": "updatedTestAccount",
"type": "STRING",
"isLiteral": true
},
"operator": "EQUALS",
"rightOperand": {
"$class": "com.docusign.connected.data.queries@1.0.0.Operand",
"name": "Name",
"type": "INTEGER",
"isLiteral": false
}
}
}
},
"pagination": {
"limit": 10,
"skip": 10
}
}Results:
Alternatively, Account.json will contain the updated records.







