diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 2542f7b..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Code Analysis -on: [push] -jobs: - lint: - name: Run PSScriptAnalyzer - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Run PSScriptAnalyzer - uses: devblackops/github-action-psscriptanalyzer@master - with: - sendComment: false - failOnErrors: true - failOnWarnings: true - failOnInfos: true diff --git a/.gitignore b/.gitignore index 79d24ac..ca3507e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,11 @@ *.key ENVELOPE_ID TEMPLATE_ID +FORM_GROUP_ID +API_ACCOUNT_ID +ds_access_token.txt .vs/* .vscode/* debug.log config/settings.json +demo_documents/web-form-config.json \ No newline at end of file diff --git a/LICENSE b/LICENSE index 390a206..e769623 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2018 DocuSign, Inc. (https://www.docusign.com) +Copyright (c) 2018 Docusign, Inc. (https://www.docusign.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/OAuth/code_grant.ps1 b/OAuth/code_grant.ps1 index 7a63b45..70ed118 100644 --- a/OAuth/code_grant.ps1 +++ b/OAuth/code_grant.ps1 @@ -4,7 +4,10 @@ param( [Parameter(Mandatory = $true)] [string]$clientSecret, [Parameter(Mandatory = $true)] - [string]$apiVersion) + [string]$apiVersion, + [Parameter(Mandatory = $true)] + [string]$targetAccountId + ) $PORT = '8080' $IP = 'localhost' @@ -17,77 +20,136 @@ $state = [Convert]::ToString($(Get-Random -Maximum 1000000000), 16) if($apiVersion -eq "rooms"){ $scopes = "signature%20dtr.rooms.read%20dtr.rooms.write%20dtr.documents.read%20dtr.documents.write%20dtr.profile.read%20dtr.profile.write%20dtr.company.read%20dtr.company.write%20room_forms" } -elseif ($apiVersion -eq "eSignature") { +elseif (($apiVersion -eq "eSignature") -or ($apiVersion -eq "idEvidence")){ $scopes = "signature" } elseif ($apiVersion -eq "click") { - $scopes = "click.manage" + $scopes = "click.manage%20click.send%20signature" +} +elseif ($apiVersion -eq "monitor") { + $scopes = "signature impersonation" +} +elseif ($apiVersion -eq "admin") { + $scopes = "signature%20organization_read%20group_read%20permission_read%20user_read%20user_write%20account_read%20domain_read%20identity_provider_read%20user_data_redact%20asset_group_account_read%20asset_group_account_clone_write%20asset_group_account_clone_read%20organization_sub_account_write%20organization_sub_account_read" +} +elseif ($apiVersion -eq "notary") { + $scopes = "signature%20organization_read%20notary_read%20notary_write" +} +elseif ($apiVersion -eq "maestro") { + $scopes = "signature%20aow_manage" +} +elseif ($apiVersion -eq "webForms") { + $scopes = "signature%20webforms_read%20webforms_instance_read%20webforms_instance_write" +} +elseif ($apiVersion -eq "navigator") { + $scopes = "signature%20adm_store_unified_repo_read" +} +elseif ($apiVersion -eq "connectedFields") { + $scopes = "signature adm_store_unified_repo_read" +} +elseif ($apiVersion -eq "workspaces") { + $scopes = "signature%20impersonation%20dtr.company.read%20dtr.rooms.read%20dtr.rooms.write%20dtr.documents.write" +} + +function GenerateCodeVerifier { + return -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 43 | ForEach-Object {[char]$_}) +} +function GenerateCodeChallenge($verifier) { + $sha256 = [System.Security.Cryptography.SHA256]::Create() + $hash = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($verifier)) + return [Convert]::ToBase64String($hash).TrimEnd('=').Replace('+', '-').Replace('/', '_') +} + +function StartHttpListenerAndAuthorize { + param ( + [string]$authorizationURL, + [string]$redirectURI + ) + + $http = New-Object System.Net.HttpListener + $http.Prefixes.Add("$redirectURI/") + + try { + $http.Start() + } catch { + Write-Error "OAuth listener failed. Is port 8080 in use by another program?" -ErrorAction Stop + return $null + } + + if ($http.IsListening) { + # Notify the user to open the authorization URL + Write-Output "Open the following URL in a browser to continue: $authorizationURL" + Start-Process $authorizationURL + } + + while ($http.IsListening) { + $context = $http.GetContext() + + if ($context.Request.HttpMethod -eq 'GET' -and $context.Request.Url.LocalPath -match '/authorization-code/callback') { + # Prepare the HTML response + [string]$html = ' + + + + + + + Ok. You may close this tab and return to the shell. This window closes automatically in five seconds. + + + ' + $buffer = [System.Text.Encoding]::UTF8.GetBytes($html) + $context.Response.ContentLength64 = $buffer.Length + $context.Response.OutputStream.Write($buffer, 0, $buffer.Length) + $context.Response.OutputStream.Close() + + $url = $context.Request.Url.ToString() + $http.Stop() + + return $url + } + } + + return $null # Fallback if the function exits without returning +} + +function ExtractAuthorizationCode { + param ( + [Uri]$requestUrl + ) + + # Extract the authorization code using regex + $Regex = [Regex]::new("(?<=code=)([^&]*)") + $Match = $Regex.Match($requestUrl.ToString()) + + if ($Match.Success) { + return $Match.Value + } else { + return $null + } } +$codeVerifier = GenerateCodeVerifier +$code_challenge = GenerateCodeChallenge($codeVerifier) +[System.Environment]::SetEnvironmentVariable("codeVerifier", $codeVerifier, "Process") + +$usePkce = $true; $authorizationEndpoint = "https://account-d.docusign.com/oauth/" $redirectUri = "http://${IP}:${PORT}/authorization-code/callback" $redirectUriEscaped = [Uri]::EscapeDataString($redirectURI) -$authorizationURL = "${authorizationEndpoint}auth?response_type=code&scope=$scopes&client_id=$clientId&state=$state&redirect_uri=$redirectUriEscaped" - -Write-Output "The authorization URL is:" -Write-Output $authorizationURL - -# Request the authorization code -# Use Http Server -$http = New-Object System.Net.HttpListener - -# Hostname and port to listen on -$http.Prefixes.Add($redirectURI + "/") - -# Start the Http Server -$http.Start() - -if ($http.IsListening) { - Write-Output "Open the following URL in a browser to continue:" $authorizationURL - Start-Process $authorizationURL -} - -while ($http.IsListening) { - $context = $http.GetContext() - - if ($context.Request.HttpMethod -eq 'GET' -and $context.Request.Url.LocalPath -match '/authorization-code/callback') { - # write-host "Check context" - # write-host "$($context.Request.UserHostAddress) => $($context.Request.Url)" -f 'mag' - [string]$html = ' - - - - - - - Ok. You may close this tab and return to the shell. This window closes automatically in five seconds. - - - - ' - # Respond to the request - $buffer = [System.Text.Encoding]::UTF8.GetBytes($html) # Convert HTML to bytes - $context.Response.ContentLength64 = $buffer.Length - $context.Response.OutputStream.Write($buffer, 0, $buffer.Length) # Stream HTML to browser - $context.Response.OutputStream.Close() # Close the response - - # Get context - $Regex = [Regex]::new("(?<=code=)(.*)(?=&state)") - $Match = $Regex.Match($context.Request.Url) - if ($Match.Success) { - $authorizationCode = $Match.Value - } +$authorizationURL = "${authorizationEndpoint}auth?response_type=code&scope=$scopes&client_id=$clientId&state=$state&redirect_uri=$redirectUriEscaped&code_challenge=$code_challenge&code_challenge_method=S256" - $http.Stop() - } +$requestUrl = StartHttpListenerAndAuthorize -authorizationURL $authorizationURL -redirectURI $redirectURI +if ($requestUrl -is [System.Object[]] -and $requestUrl.Count -gt 0) { + $requestUrl = $requestUrl[-1] # Get the last element of the array } +$authorizationCode = ExtractAuthorizationCode -requestUrl $requestUrl + # Obtain the access token # Preparing an Authorization header which contains your integration key and secret key $authorizationHeader = "${clientId}:${clientSecret}" @@ -96,16 +158,48 @@ $authorizationHeader = "${clientId}:${clientSecret}" $authorizationHeaderBytes = [System.Text.Encoding]::UTF8.GetBytes($authorizationHeader) $authorizationHeaderKey = [System.Convert]::ToBase64String($authorizationHeaderBytes) -try { - Write-Output "Getting an access token..." - $accessTokenResponse = Invoke-RestMethod ` - -Uri "$authorizationEndpoint/token" ` - -Method "POST" ` - -Headers @{ "Authorization" = "Basic $authorizationHeaderKey" } ` - -Body @{ - "grant_type" = "authorization_code"; - "code" = "$authorizationCode" + try { + Write-Output "Getting an access token..." + $accessTokenResponse = Invoke-RestMethod ` + -Uri "$authorizationEndpoint/token" ` + -Method "POST" ` + -Headers @{ "Authorization" = "Basic $authorizationHeaderKey" } ` + -Body @{ + "grant_type" = "authorization_code"; + "code" = "$authorizationCode" + "code_verifier" = "$codeVerifier" + } + } catch { + Write-Output "Error fetching access token" + $usePkce = $false + Write-Output "PKCE failed" } + + if (-not $usePkce) { + $authorizationURL = "${authorizationEndpoint}auth?response_type=code&scope=$scopes&client_id=$clientId&state=$state&redirect_uri=$redirectUriEscaped" + + $requestUrl = StartHttpListenerAndAuthorize -authorizationURL $authorizationURL -redirectURI $redirectURI + if ($requestUrl -is [System.Object[]] -and $requestUrl.Count -gt 0) { + $requestUrl = $requestUrl[-1] # Get the last element of the array + } + $authorizationCode = ExtractAuthorizationCode -requestUrl $requestUrl + + $authorizationHeader = "${clientId}:${clientSecret}" + $authorizationHeaderBytes = [System.Text.Encoding]::UTF8.GetBytes($authorizationHeader) + $authorizationHeaderKey = [System.Convert]::ToBase64String($authorizationHeaderBytes) + + Write-Output "Getting an access token..." + $accessTokenResponse = Invoke-RestMethod ` + -Uri "$authorizationEndpoint/token" ` + -Method "POST" ` + -Headers @{ "Authorization" = "Basic $authorizationHeaderKey" } ` + -Body @{ + "grant_type" = "authorization_code"; + "code" = "$authorizationCode" + } + } + +try { $accessToken = $accessTokenResponse.access_token Write-Output "Access token: $accessToken" Write-Output $accessToken > $accessTokenFile @@ -116,7 +210,29 @@ try { -Uri "$authorizationEndpoint/userinfo" ` -Method "GET" ` -Headers @{ "Authorization" = "Bearer $accessToken" } - $accountId = $userInfoResponse.accounts[0].account_id + + if ($targetAccountId -ne "TARGET_ACCOUNT_ID" -and $targetAccountId -ne "{TARGET_ACCOUNT_ID}") { + $targetAccountFound = "false"; + foreach ($account_info in $userInfoResponse.accounts) { + if ($account_info.account_id -eq $targetAccountId) { + $accountId = $account_info.account_id; + $targetAccountFound = "true"; + break; + } + } + + if ($targetAccountFound -eq "false") { + Write-Error "Targeted Account with Id $targetAccountId not found." -ErrorAction Stop; + } + } else { + foreach ($account_info in $userInfoResponse.accounts) { + if ($account_info.is_default -eq "true") { + $accountId = $account_info.account_id; + break; + } + } + } + Write-Output "Account id: $accountId" Write-Output $accountId > $accountIdFile Write-Output "Account id has been written to $accountIdFile file..." diff --git a/OAuth/jwt.ps1 b/OAuth/jwt.ps1 index 79de04e..7f6f885 100644 --- a/OAuth/jwt.ps1 +++ b/OAuth/jwt.ps1 @@ -2,7 +2,10 @@ param( [Parameter(Mandatory = $true)] [string]$clientId, [Parameter(Mandatory = $true)] - [string]$apiVersion) + [string]$apiVersion, + [Parameter(Mandatory = $true)] + [string]$targetAccountId + ) # Reference dependencies . ([System.IO.Path]::Combine($PSScriptRoot, "..\Install-NugetPackage.ps1")) @@ -13,6 +16,12 @@ Install-NugetPackage PemUtils '3.0.0.82' New-Item "config\ds_access_token.txt" -Force + +if (!(test-path ".\config\private.key")){ + Write-Error "`n Error: First create an RSA keypair on your integration key and copy the private_key into the file `config/private.key` and save it" -ErrorAction Stop + exit 1 +} + $privateKeyPath = [System.IO.Path]::Combine($PSScriptRoot, "..\config\private.key") | Resolve-Path $outputFile = [System.IO.Path]::Combine($PSScriptRoot, "..\config\ds_access_token.txt") | Resolve-Path $accountIdFile = [System.IO.Path]::Combine($PSScriptRoot, "..\config\API_ACCOUNT_ID") @@ -21,17 +30,41 @@ $accountIdFile = [System.IO.Path]::Combine($PSScriptRoot, "..\config\API_ACCOUNT $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json $userId = $variables.IMPERSONATION_USER_GUID $INTEGRATION_KEY_JWT = $variables.INTEGRATION_KEY_JWT -$timestamp = [int][double]::Parse((Get-Date -UFormat %s)) +$timestamp = [int][double]::Parse((Get-Date (Get-Date).ToUniversalTime() -UFormat %s)) if ($apiVersion -eq "rooms") { - $scopes = "signature%20impersonation%20dtr.rooms.read%20dtr.rooms.write%20dtr.documents.read%20dtr.documents.write%20dtr.profile.read%20dtr.profile.write%20dtr.company.read%20dtr.company.write%20room_forms" - } elseif ($apiVersion -eq "eSignature") { - $scopes = "signature%20impersonation" - } elseif ($apiVersion -eq "click") { - $scopes = "click.manage" + $scopes = "signature%20impersonation%20dtr.rooms.read%20dtr.rooms.write%20dtr.documents.read%20dtr.documents.write%20dtr.profile.read%20dtr.profile.write%20dtr.company.read%20dtr.company.write%20room_forms" +} elseif (($apiVersion -eq "eSignature") -or ($apiVersion -eq "idEvidence")) { + $scopes = "signature%20impersonation" +} elseif ($apiVersion -eq "click") { + $scopes = "click.manage%20click.send%20signature%20impersonation" +} +elseif ($apiVersion -eq "monitor") { + $scopes = "signature%20impersonation" +} +elseif ($apiVersion -eq "admin") { + $scopes = "signature%20impersonation%20organization_read%20group_read%20permission_read%20user_read%20user_write%20account_read%20domain_read%20identity_provider_read%20user_data_redact%20asset_group_account_read%20asset_group_account_clone_write%20asset_group_account_clone_read%20organization_sub_account_write%20organization_sub_account_read" +} +elseif ($apiVersion -eq "notary") { + $scopes = "signature%20organization_read%20notary_read%20notary_write" +} +elseif ($apiVersion -eq "maestro") { + $scopes = "signature%20aow_manage%20impersonation" +} +elseif ($apiVersion -eq "webForms") { + $scopes = "signature%20webforms_read%20webforms_instance_read%20webforms_instance_write" +} +elseif ($apiVersion -eq "navigator") { + $scopes = "signature%20adm_store_unified_repo_read" +} +elseif ($apiVersion -eq "connectedFields") { + $scopes = "signature%20adm_store_unified_repo_read%20impersonation" +} +elseif ($apiVersion -eq "workspaces") { + $scopes = "signature%20impersonation%20dtr.company.read%20dtr.rooms.read%20dtr.rooms.write%20dtr.documents.write" } -# Step 1. Request application consent +# Request application consent $PORT = '8080' $IP = 'localhost' $state = [Convert]::ToString($(Get-Random -Maximum 1000000000), 16) @@ -43,57 +76,7 @@ $authorizationURL = "${authorizationEndpoint}auth?scope=$scopes&redirect_uri=$re Write-Output "The authorization URL is: $authorizationURL" Write-Output "" -# Request the authorization code -# Use Http Server -$http = New-Object System.Net.HttpListener - -# Hostname and port to listen on -$http.Prefixes.Add($redirectURI + "/") - -# Start the Http Server -$http.Start() - -if ($http.IsListening) { - Write-Output "Open the following URL in a browser to continue:" $authorizationURL - Start-Process $authorizationURL -} - -while ($http.IsListening) { - $context = $http.GetContext() - - if ($context.Request.HttpMethod -eq 'GET' -and $context.Request.Url.LocalPath -match '/authorization-code/callback') { - # write-host "Check context" - # write-host "$($context.Request.UserHostAddress) => $($context.Request.Url)" -f 'mag' - [string]$html = ' - - - - - - - Ok. You may close this tab and return to the shell. This window closes automatically in five seconds. - - - - ' - # Respond to the request - $buffer = [System.Text.Encoding]::UTF8.GetBytes($html) # Convert HTML to bytes - $context.Response.ContentLength64 = $buffer.Length - $context.Response.OutputStream.Write($buffer, 0, $buffer.Length) # Stream HTML to browser - $context.Response.OutputStream.Close() # Close the response - - Start-Sleep 10 - $http.Stop() - } -} - -# Step 2. Create a JWT +# Create a JWT $decJwtHeader = [ordered]@{ 'typ' = 'JWT'; 'alg' = 'RS256' @@ -135,11 +118,12 @@ $signedBase64Token = [System.Convert]::ToBase64String($signedToken) -replace '\+ $jwtToken = "$encJwtHeader.$encJwtPayLoad.$signedBase64Token" -# Step 3. Obtain the access token +# Obtain the access token try { $authorizationEndpoint = "https://account-d.docusign.com/oauth/" $tokenResponse = Invoke-WebRequest ` -Uri "$authorizationEndpoint/token" ` + -UseBasicParsing ` -Method "POST" ` -Body "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$jwtToken" $accessToken = ($tokenResponse | ConvertFrom-Json).access_token @@ -149,13 +133,113 @@ try { Write-Output "Getting an account id..." $userInfoResponse = Invoke-RestMethod ` -Uri "$authorizationEndpoint/userinfo" ` + -UseBasicParsing ` -Method "GET" ` -Headers @{ "Authorization" = "Bearer $accessToken" } - $accountId = $userInfoResponse.accounts[0].account_id + + if ($targetAccountId -ne "TARGET_ACCOUNT_ID" -and $targetAccountId -ne "{TARGET_ACCOUNT_ID}") { + $targetAccountFound = "false"; + foreach ($account_info in $userInfoResponse.accounts) { + if ($account_info.account_id -eq $targetAccountId) { + $accountId = $account_info.account_id; + $targetAccountFound = "true"; + break; + } + } + + if ($targetAccountFound -eq "false") { + Write-Error "Targeted Account with Id $targetAccountId not found." -ErrorAction Stop; + } + } else { + foreach ($account_info in $userInfoResponse.accounts) { + if ($account_info.is_default -eq "true") { + $accountId = $account_info.account_id; + break; + } + } + } Write-Output "Account id: $accountId" Write-Output $accountId > $accountIdFile Write-Output "Account id has been written to $accountIdFile file..." } catch { + if (($_.ErrorDetails.Message | ConvertFrom-Json | Select-Object -Expand error) -eq "consent_required") { + # Use Http Server + $http = New-Object System.Net.HttpListener + + # Hostname and port to listen on + $http.Prefixes.Add($redirectURI + "/") + + try { + # Start the Http Server + $http.Start() + + } + catch { + Write-Error "OAuth listener failed. Is port 8080 in use by another program?" -ErrorAction Stop + } + + if ($http.IsListening) { + Write-Output "Open the following URL in a browser to continue:" $authorizationURL + Start-Process $authorizationURL + } + + while ($http.IsListening) { + $context = $http.GetContext() + + if ($context.Request.HttpMethod -eq 'GET' -and $context.Request.Url.LocalPath -match '/authorization-code/callback') { + # write-host "Check context" + # write-host "$($context.Request.UserHostAddress) => $($context.Request.Url)" -f 'mag' + [string]$html = ' + + + + + + + Ok. You may close this tab and return to the shell. This window closes automatically in five seconds. + + + + ' + # Respond to the request + $buffer = [System.Text.Encoding]::UTF8.GetBytes($html) # Convert HTML to bytes + $context.Response.ContentLength64 = $buffer.Length + $context.Response.OutputStream.Write($buffer, 0, $buffer.Length) # Stream HTML to browser + $context.Response.OutputStream.Close() # Close the response + + Start-Sleep 4 + $http.Stop() + } + } + + $authorizationEndpoint = "https://account-d.docusign.com/oauth/" + $tokenResponse = Invoke-WebRequest ` + -Uri "$authorizationEndpoint/token" ` + -UseBasicParsing ` + -Method "POST" ` + -Body "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$jwtToken" + $accessToken = ($tokenResponse | ConvertFrom-Json).access_token + Write-Output $accessToken > $outputFile + Write-Output "Access token has been written to $outputFile file..." + + Write-Output "Getting an account id..." + $userInfoResponse = Invoke-RestMethod ` + -Uri "$authorizationEndpoint/userinfo" ` + -UseBasicParsing ` + -Method "GET" ` + -Headers @{ "Authorization" = "Bearer $accessToken" } + $accountId = $userInfoResponse.accounts[0].account_id + Write-Output "Account id: $accountId" + Write-Output $accountId > $accountIdFile + Write-Output "Account id has been written to $accountIdFile file..." + } else { Write-Error $_ -} + } +} \ No newline at end of file diff --git a/PAYMENTS_INSTALLATION.md b/PAYMENTS_INSTALLATION.md index 425bf7f..c3fb4b9 100644 --- a/PAYMENTS_INSTALLATION.md +++ b/PAYMENTS_INSTALLATION.md @@ -1,24 +1,21 @@ -# Configuring a DocuSign payments gateway +# Configure a payment gateway -DocuSign offers built-in connections to multiple payment -gateways. The payments example uses a demo account via the Stripe -gateway service. +Docusign offers built-in connections to multiple payment gateways. The payments code example uses a developer account via the Stripe gateway service. -## Creating the payments gateway account -1. Login to demo.docusign.net and go to the Admin Tool. -1. On the Integrations / Payments screen, click Stripe. -1. For development, you can skip the Stripe account application - by using the `Skip this account form` link: +## Create a Stripe payment gateway + +1. Select the Stripe button on the [**Payments**](https://admindemo.docusign.com/authenticate?goTo=payments) page in your developer account. + +1. For development, you can skip the Stripe account application by using the **Skip this account form** link at the top of the page.
+ + ![Skipping the Stripe account form](docs/stripe_skip_account_form_link.png) + + An enabled Stripe payment gateway is now associated with your Docusign developer account and is shown under **Payment Gateway**. + +1. Save the **Gateway Account ID** GUID to the code example launcher configuration file. - ![Skipping the Stripe account form](documentation/stripe_skip_account_form_link.png) -1. Next, the Admin Tool will show that an enabled Stripe - payment gateway account has been associated with your - DocuSign Developer account. -1. Configure the example launcher with the gateway account id shown in the Admin tool. ## Additional documentation -See the -[Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways) -documentation. - +* [Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways) +* [How to send a request for payment](https://developers.docusign.com/docs/esign-rest-api/how-to/request-a-payment) diff --git a/Quick_ACG/launcherACG.ps1 b/Quick_ACG/launcherACG.ps1 new file mode 100644 index 0000000..08154d1 --- /dev/null +++ b/Quick_ACG/launcherACG.ps1 @@ -0,0 +1,61 @@ +. "../utils/invokeScript.ps1" + +$ErrorActionPreference = "Stop" # force stop on failure + +$configFile = "..\config\settings.json" + +if ((Test-Path $configFile) -eq $False) { + Write-Output "Error: " + Write-Output "First copy the file '..\config\settings.example.json' to '$configFile'." + Write-Output "Next, fill in your API credentials, Signer name and email to continue." +} + +# Get required environment variables from ..\config\settings.json file +$config = Get-Content $configFile -Raw | ConvertFrom-Json + +function startQuickACG { + Write-Output '' + Write-Output "Authentication in progress, please wait" + Write-Output '' + Invoke-Script -Command "`"..\OAuth\code_grant.ps1`" -clientId `"$($config.INTEGRATION_KEY_AUTH_CODE)`" -clientSecret `"$($config.SECRET_KEY)`" -apiVersion $("eSignature") -targetAccountId `"$($config.TARGET_ACCOUNT_ID)`"" + Write-Output '' + + if ((Test-Path "../config/ds_access_token.txt") -eq $true) { + Invoke-Script -Command "`"..\eg001EmbeddedSigning.ps1`"" + Write-Output '' + startSignature + } + else { + Write-Error "Failed to retrieve OAuth Access token, check your settings.json and that port 8080 is not in use" -ErrorAction Stop + } +} + +function startSignature { + do { + # Preparing a list of menu options + Enum MenuOptions { + Embedded_Signing = 1; + Exit = 2; + } + + $MenuOptionsView = $null; + do { + Write-Output "" + Write-Output 'Pick the next action: ' + Write-Output "$([int][MenuOptions]::Embedded_Signing)) Rerun the embedded signing code example" + Write-Output "$([int][MenuOptions]::Exit)) Exit" + [int]$MenuOptionsView = Read-Host "Pick the next action" + } while (-not [MenuOptions]::IsDefined([MenuOptions], $MenuOptionsView)); + + if ($MenuOptionsView -eq [MenuOptions]::Embedded_Signing) { + Invoke-Script -Command "`"..\eg001EmbeddedSigning.ps1`"" + } + elseif ($MenuOptionsView -eq [MenuOptions]::Exit) { + exit 1 + } + } until ($MenuOptionsView -eq [MenuOptions]::Exit) + exit 1 +} + +Write-Output "Welcome to the Docusign PowerShell Quick Authorization Code Grant Launcher" +startQuickACG diff --git a/README.md b/README.md index 3a2746e..1a33db9 100644 --- a/README.md +++ b/README.md @@ -1,241 +1,184 @@ -# DocuSign PowerShell Code Examples +# PowerShell Launcher Code Examples + +> +>### PLEASE! Share your feedback in a [two-question survey](https://docs.google.com/forms/d/e/1FAIpQLScPa74hwhJwi7XWDDj4-XZVOQTF9jJWgbIFEpulXokCqYWT4A/viewform?usp=pp_url&entry.680551577=PowerShell). +> +> +### GitHub repo: [code-examples-powershell](./README.md) + +This GitHub repo includes code examples for the [Web Forms API](https://developers.docusign.com/docs/web-forms-api/), [Maestro API](https://developers.docusign.com/docs/maestro-api/), [Docusign Admin API](https://developers.docusign.com/docs/admin-api/), [Click API](https://developers.docusign.com/docs/click-api/), [eSignature REST API](https://developers.docusign.com/docs/esign-rest-api/), [Monitor API](https://developers.docusign.com/docs/monitor-api/), and [Rooms API](https://developers.docusign.com/docs/rooms-api/). + ## Introduction -This repo includes a powershell command-line application to demonstrate: - -## eSignature API - -For more information about the scopes used for obtaining authorization to use the eSignature API, see the [Required Scopes section](https://developers.docusign.com/docs/esign-rest-api/esign101/auth) - -1. **Use embedded signing.** - [Source.](./eg001EmbeddedSigning.ps1) - This example sends an envelope, and then uses embedded signing for the first signer. - With embedded signing, the DocuSign signing is initiated from your website. -1. **Request a signature by email (Remote Signing).** - [Source.](./examples/eSignature/eg002SigningViaEmail.ps1) - The envelope includes a pdf, Word, and HTML document. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **List envelopes in the user's account.** - [Source.](./examples/eSignature/eg003ListEnvelopes.ps1) -1. **Get an envelope's basic information.** - [Source.](./examples/eSignature/eg004EnvelopeInfo.ps1) - The example lists the basic information about an envelope, including its overall status. -1. **List an envelope's recipients** - [Source.](./examples/eSignature/eg005EnvelopeRecipients.ps1) - Includes current recipient status. -1. **List an envelope's documents.** - [Source.](./examples/eSignature/eg006EnvelopeDocs.ps1) -1. **Download an envelope's documents.** - [Source.](./examples/eSignature/eg007EnvelopeGetDoc.ps1) - The example can download individual - documents, the documents concatenated together, or a zip file of the documents. -1. **Programmatically create a template.** - [Source.](./examples/eSignature/eg008CreateTemplate.ps1) -1. **Request a signature by email using a template.** - [Source.](./examples/eSignature/eg009UseTemplate.ps1) -1. **Send an envelope and upload its documents with multipart binary transfer.** - [Source.](./examples/eSignature/eg010SendBinaryDocs.ps1) - Binary transfer is 33% more efficient than using Base64 encoding. -1. **Use embedded sending.** - [Source.](./examples/eSignature/eg011EmbeddedSending.ps1) - Embeds the DocuSign web tool (NDSE) in your web app to finalize or update - the envelope and documents before they are sent. -1. **Embedded DocuSign web tool (NDSE).** - [Source.](./examples/eSignature/eg012EmbeddedConsole.ps1) -1. **Use embedded signing from a template with an added document.** - [Source.](./examples/eSignature/eg013AddDocToTemplate.ps1) - This example sends an envelope based on a template. - In addition to the template's document(s), the example adds an - additional document to the envelope by using the - [Composite Templates](https://developers.docusign.com/esign-rest-api/guides/features/templates#composite-templates) - feature. -1. **Payments Example.** - [Source.](./examples/eSignature/eg014CollectPayment.ps1) - An order form, with online payment by credit card. -1. **Get the envelope tab data.** - Retrieve the tab (field) values for all of the envelope's recipients. - [Source.](./examples/eSignature/eg015EnvelopeTabData.ps1) -1. **Set envelope tab values.** - The example creates an envelope and sets the initial values for its tabs (fields). Some of the tabs - are set to be read-only, others can be updated by the recipient. The example also stores - metadata with the envelope. - [Source.](./examples/eSignature/eg016SetTabValues.ps1) -1. **Set template tab values.** - The example creates an envelope using a template and sets the initial values for its tabs (fields). - The example also stores metadata with the envelope. - [Source.](./examples/eSignature/eg017SetTemplateTabValues.ps1) -1. **Get the envelope custom field data (metadata).** - The example retrieves the custom metadata (custom data fields) stored with the envelope. - [Source.](./examples/eSignature/eg018EnvelopeCustomFieldData.ps1) -1. **Requiring an Access Code for a Recipient** - [Source.](./examples/eSignature/eg019SigningViaEmailWithAccessCode.ps1) - This example sends an envelope using remote (email) signing requiring the recipient to enter an access code. -1. **Send an envelope with a remote (email) signer using SMS authentication.** - [Source.](./examples/eSignature/eg020SigningViaEmailWithSmsAuthentication.ps1) - This example sends an envelope using remote (email) signing requiring the recipient to supply a verification code sent to them via SMS. -1. **Send an envelope with a remote (email) signer using Phone authentication.** - [Source.](./examples/eSignature/eg021SigningViaEmailWithPhoneAuthentication.ps1) - This example sends an envelope using remote (email) signing requiring the recipient to supply a verification code sent to them via a phone call. -1. **Send an envelope with a remote (email) signer using Knowledge-Based authentication.** - [Source.](./examples/eSignature/eg022SigningViaEmailWithKnowledgeBasedAuthentication.ps1) - This example sends an envelope using remote (email) signing requiring the recipient to validate their identity via Knowledge-Based authentication. -1. **Send an envelope with a remote (email) signer using Identity Verification.** - [Source.](./examples/eSignature/eg023SigningViaEmailWithIDVAuthentication.ps1) - This example sends an envelope using remote (email) signing requiring the recipient to validate their identity via a government issued ID. -1. **Creating a permission profile** - [Source.](./examples/eSignature/eg024CreatingPermissionProfiles.ps1) - This code example demonstrates how to create a permission profile using the [Create Permission Profile](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/create) method. -1. **Setting a permission profile** - [Source.](./examples/eSignature/eg025SettingPermissionProfiles.ps1) - This code example demonstrates how to set a user group’s permission profile using the [Update Group](https://developers.docusign.com/esign-rest-api/reference/UserGroups/Groups/update) method. - You must have already created the permissions profile and the group of users. -1. **Updating individual permission settings** - [Source.](./examples/eSignature/eg026UpdatingIndividualPermission.ps1) - This code example demonstrates how to edit individual permission settings on a permissions profile using the [Update Permission Profile](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/update) method. -1. **Deleting a permission profile** - [Source.](./examples/eSignature/eg027DeletingPermissions.ps1) - This code example demonstrates how to delete a permission profile using the [Delete Permission Profile](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/create) method. -1. **Creating a brand** - [Source.](./examples/eSignature/eg028CreatingABrand.ps1) - This example creates brand profile for an account using the [Create Brand](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountBrands/create) method. -1. **Applying a brand to an envelope** - [Source.](./examples/eSignature/eg029ApplyingBrandEnvelope.ps1) - This code example demonstrates how to apply a brand you've created to an envelope using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. - First, creates the envelope and then applies the brand to it. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **Applying a brand to a template** - [Source.](./examples/eSignature/eg030ApplyingBrandTemplate.ps1) - This code example demonstrates how to apply a brand you've created to a template using using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. - You must have already created the template and the brand. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **Bulk sending envelopes to multiple recipients** - [Source.](./examples/eSignature/eg031BulkSending.ps1) - This code example demonstrates how to send envelopes in bulk to multiple recipients using these methods: - [Create Bulk Send List](https://developers.docusign.com/esign-rest-api/reference/BulkEnvelopes/BulkSend/createBulkSendList), - [Create Bulk Send Request](https://developers.docusign.com/esign-rest-api/reference/BulkEnvelopes/BulkSend/createBulkSendRequest). - Firstly, creates a bulk send recipients list, and then creates an envelope. - After that, initiates bulk envelope sending. -1. **Pausing a signature workflow Source.** - [Source.](./examples/eSignature/eg032PauseSignatureWorkflow.ps1) - This code example demonstrates how to create an envelope where the workflow is paused before the envelope is sent to a second recipient. -1. **Unpausing a signature workflow** - [Source.](./examples/eSignature/eg033UnpauseSignatureWorkflow.ps1) - This code example demonstrates how to resume an envelope workflow that has been paused -1. **Using conditional recipients** - [Source.](./examples/eSignature/eg034UseConditionalRecipients.ps1) - This code example demonstrates how to create an envelope where the workflow is routed to different recipients based on the value of a transaction. -1. **Request a signature by SMS** - [Source.](./app/eSignature/examples/eg035SMSDelivery.ps1) - This code example demonstrates how to send a signature request for a signer to read and sign via an SMS message. - + +This repo includes a PowerShell command-line application that supports the following authentication workflows: + +* Authentication with Docusign via [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode). +When the token expires, the user is asked to re-authenticate. The refresh token is not used. + +* Authentication with Docusign via [JSON Web Token (JWT) Grant](https://developers.docusign.com/platform/auth/jwt/). +When the token expires, it updates automatically. + + +## eSignature REST API + +For more information about the scopes used for obtaining authorization to use the eSignature REST API, see [Required scopes](https://developers.docusign.com/docs/esign-rest-api/esign101/auth#required-scopes). + +For a list of code examples that use the eSignature REST API, see the [How-to guides overview](https://developers.docusign.com/docs/esign-rest-api/how-to/) on the Docusign Developer Center. + + +## Admin API + +**Note:** To use the Admin API, you must [create an organization](https://support.docusign.com/en/guides/org-admin-guide-create-org) in your Docusign developer account. Also, to run the Docusign CLM code example, [CLM must be enabled for your organization](https://support.docusign.com/en/articles/Docusign-and-SpringCM). + +For information about the scopes used for obtaining authorization to use the Admin API, see the [scopes section](https://developers.docusign.com/docs/admin-api/admin101/auth/). + +For a list of code examples that use the Admin API, see the [How-to guides overview](https://developers.docusign.com/docs/admin-api/how-to/) on the Docusign Developer Center. + + +## Click API +For more information about the scopes used for obtaining authorization to use the Click API, see [Required scopes](https://developers.docusign.com/docs/click-api/click101/auth/#required-scopes) + +For a list of code examples that use the Click API, see the [How-to guides overview](https://developers.docusign.com/docs/click-api/how-to/) on the Docusign Developer Center. + + +## ID Evidence API + +**Note:** To run the ID Evidence code examples, you must first complete the [Prerequisites](https://developers.docusign.com/docs/idevidence-api/how-to/retrieve-idevidence-events/). + +For more information about the scopes used for obtaining authorization to use the ID Evidence API, see [Required scopes](https://developers.docusign.com/docs/esign-rest-api/esign101/auth#required-scopes). + +For a list of code examples that use the ID Evidence API, see the [How-to guides overview](https://developers.docusign.com/docs/idevidence-api/how-to/) on the Docusign Developer Center. + +## Maestro API (beta) + +**Note:** Maestro API is currently only avaiable for developers that participate in the [beta program](https://developers.docusign.com/docs/maestro-api/get-access/). + +For information about the scopes used for obtaining authorization to use the Maestro API, see the [scopes section](https://developers.docusign.com/docs/maestro-api/auth/). + +For a list of code examples that use the Maestro API, see the [How-to guides overview](https://developers.docusign.com/docs/maestro-api/how-to/) on the Docusign Developer Center. + +## Monitor API + +**Note:** To use the Monitor API, you must also [enable Docusign Monitor for your organization](https://developers.docusign.com/docs/monitor-api/how-to/enable-monitor/). + +For information about the scopes used for obtaining authorization to use the Monitor API, see the [scopes section](https://developers.docusign.com/docs/monitor-api/monitor101/auth/). + +For a list of code examples that use the Monitor API, see the [How-to guides overview](https://developers.docusign.com/docs/monitor-api/how-to/) on the Docusign Developer Center. + + +## Notary API + +Example 1 requires a Sender Docusign developer account API account ID that has been provisioned for the [Notary API base URI](https://developers.docusign.com/docs/notary-api/go-live/). + +Example 2 requires that you [create an organization](https://support.docusign.com/en/guides/org-admin-guide-create-org) in your Sender Docusign developer account. + +For information about the scopes used for obtaining authorization to use the Notary API, see the [scopes section](https://developers.docusign.com/docs/notary-api/notary101/auth/). + +For a list of code examples that use the Notary API, see the [How-to guides overview](https://developers.docusign.com/docs/notary-api/how-to/) on the Docusign Developer Center. + + ## Rooms API -For more information about the scopes used for obtaining authorization to use the Rooms API, see the [Required Scopes section](https://developers.docusign.com/docs/rooms-api/rooms101/auth/) - -**Note:** to use the Rooms API you must also [create your DocuSign Developer Account for Rooms](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). - -1. **Create room with Data.** - [Source.](./examples/Rooms/eg001CreateRoomWithDataController.ps1) - This example creates a new room in your DocuSign Rooms account to be used for a transaction. -1. **Create a room from a template.** - [Source.](./examples/Rooms/eg002CreateRoomWithTemplateController.ps1) - This example creates a new room using a template. -1. **Create room with Data.** - [Source.](./examples/Rooms/eg003ExportDataFromRoomController.ps1)) - This example exports all the avialalble data from a specific room in your DocuSign Rooms account. -1. **Add forms to a room.** - [Source.](./examples/Rooms/eg004AddFormsToRoomController.ps1) - This example adds a standard real estate related form to a specific room in your DocuSign Rooms account. -1. **How to search for rooms with filters.** - [Source.](./examples/Rooms/eg005GetRoomsWithFiltersController.ps1) - This example searches for rooms in your DocuSign Rooms account using a specific filter. -1. **Create an external form fillable session.** - [Source.](./examples/Rooms/eg006CreateAnExternalFormFillSessionController.ps1) - This example create an external form that can be filled using DocuSign for a specific room in your DocuSign Rooms account. - -## Click API -**Note:** To use the Click API include the click_manage scope. Review the [Click API 101 Auth Guide](https://developers.docusign.com/docs/click-api/click101/auth) for more details. - - -1. **Create clickwraps.** - [Source.](./examples/Click/eg001CreateClickwrap.ps1) - Creates a clickwrap that you can embed in your website or app. -1. **Activate clickwrap.** - [Source.](./examples/Click/eg002ActivateClickwrap.ps1) - Activates a new clickwrap. By default, new clickwraps are inactive. You must activate your clickwrap before you can use it. -1. **Clickwrap Versioning.** - [Source.](./examples/Click/eg003CreateNewClickwrapVersion.ps1) - Demonstrates how to use the Click API to create a new version of a clickwrap. -1. **Retrieve clickwraps.** - [Source.](./examples/Click/eg004GetListOfClickwraps.ps1) - Demonstrates how to get a list of clickwraps associated with a specific DocuSign user. -1. **Get clickwrap Responses.** - [Source.](./examples/Click/eg005GetClickwrapResponses.ps1) - Demonstrates how to get user responses to your clickwrap agreements. +**Note:** To use the Rooms API, you must also [create your Rooms developer account](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). -## Installation -**Note: If you downloaded this code using Quickstart from the DocuSign Developer Center, skip to Running the examples, the next step has been automatically performed for you.** -Download or clone this repository to your workstation. Open a PowerShell terminal window and navigate to this repo's folder. +Examples 4 and 6 require that you have the Docusign Forms feature enabled in your Rooms for Real Estate account. +For more information about the scopes used for obtaining authorization to use the Rooms API, see [Required scopes](https://developers.docusign.com/docs/rooms-api/rooms101/auth/). + +For a list of code examples that use the Rooms API, see the [How-to guides overview](https://developers.docusign.com/docs/rooms-api/how-to/) on the Docusign Developer Center. -## Collect your Integration information -* Create a [DocuSign developer account](https://account-d.docusign.com/#/username) if you have not yet done so. -* Once you have a DocuSign account created, make a new [**integration key**](https://admindemo.docusign.com/api-integrator-key). -* Add in the following **redirect uri** `http://localhost:8080/authorization-code/callback` -* Find your **API Account Id:** on the same page you used to setup your [**integration key**](https://admindemo.docusign.com/api-integrator-key). -* Update `config/settings.json` with the credentials from DocuSign developer account: - * `IMPERSONATION_USER_GUID` = API Account ID - * `INTEGRATION_KEY_JWT` = Integration Key - * `INTEGRATION_KEY_AUTH_CODE` = Integration Key - * `SECRET_KEY` = Secret Key - * `GATEWAY_ACCOUNT_ID` = Account ID -* **Signer name and email:** Remember to try the DocuSign signing using both a mobile phone and a regular - email client. +## Web Forms API + +The Web Forms API is available in all developer accounts, but only in certain production account plans. Contact [Docusign Support](https://support.docusign.com/) or your account manager to find out whether the Web Forms API is available for your production account plan. + +For more information about the scopes used for obtaining authorization to use the Rooms API, see [Required scopes](https://developers.docusign.com/docs/web-forms-api/plan-integration/authentication/). + +For a list of code examples that use the Web Forms API, see the [How-to guides overview](https://developers.docusign.com/docs/web-forms-api/how-to/) on the Docusign Developer Center. + +## Installation +### Prerequisites +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip items 1 and 2 as they were automatically performed for you. -## JWT Authentication +1. A free [Docusign developer account](https://go.docusign.com/o/sandbox/); create one if you don't already have one. +1. A Docusign app and integration key that is configured for authentication to use either [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/) or [JWT Grant](https://developers.docusign.com/platform/auth/jwt/). -* create an RSA KeyPair on your **integration key** and copy the **private_key** into the file `config/private.key` and save it. Use JWT authentication if you intend to run a system account integration or to impersonate a different user. -* OPTIONAL: If you intend to use JWT grant authentication, set **Impersonation_user_guid** by using your own **user_account_id** found on the same page used to set your [**integration key**](https://admindemo.docusign.com/api-integrator-key). + This [video](https://www.youtube.com/watch?v=eiRI4fe5HgM) demonstrates how to obtain an integration key. -**Note:** Before you can make any API calls using JWT Grant, you must get your user’s consent for your app to impersonate them. To do this, the `impersonation` scope is added when requesting a JSON Web Token. + To use [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/), you will need an integration key and a secret key. See [Installation steps](#installation-steps) for details. -## OAuth Details + To use [JWT Grant](https://developers.docusign.com/platform/auth/jwt/), you will need an integration key, an RSA key pair, and the User ID GUID of the impersonated user. See [Installation steps for JWT Grant authentication](#installation-steps-for-jwt-grant-authentication) for details. -This launcher is a collection of powershell scripts with an included http listener script. The listener script works on **port 8080** in order to receive the redirect callback from successful authorization with DocuSign servers that include the Authorization code or an access token in the response payload. Confirm that port 8080 is not in use by other applications so that the OAuth mechanism functions properly. + For both authentication flows: + + If you use this launcher on your own workstation, the integration key must include a redirect URI of -These OAuth scripts are integrated into the launcher and hardcode the location for the RSA private key in the case of the JWT php scripts. + http://localhost:8080/authorization-code/callback -Do not delete or change the name of the private.key file located in the config directory as this will cause problems with jwt authentication. + If you host this launcher on a remote web server, set your redirect URI as + + {base_url}/authorization-code/callback + + where {base_url} is the URL for the web app. + +1. PowerShell 5 or later +1. PowerShell script files (PS1) downloaded from the internet that are not digitally signed are considered a security risk. By default, PowerShell blocks execution of such scripts. To bypass this limitation, you will need to run the following command at the Windows PowerShell command line: `Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass` + After executing this command, you can run the downloaded PowerShell script. + -## Running the examples -You can see each of the various examples in action by running `powershell launcher.ps1` and pressing the number six to get to the option to edit your form data. To use the Rooms API, select Rooms API at the selection prompt just after running `powershell launcher.ps1`. +### Installation steps +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip step 3 as it was automatically performed for you. -Log in to your DocuSign account using either Authorization Code Grant or using JWT to gain an OAuth token. From there, you can pick the number that corresponds to a setting or feature you wish to try out. +1. Extract the Quickstart ZIP file or download or clone the code-examples-powershell repository. +1. In File Explorer, open your Quickstart folder or your code-examples-powershell folder. +1. To configure the launcher for [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/) authentication, create a copy of the file config/settings.example.json and save the copy as config/settings.json. + 1. Add your integration key. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **Apps and Integration Keys**, choose the app to use, then select **Actions** > **Edit**. Under **General Info**, copy the **Integration Key** GUID and save it in settings.json as your `INTEGRATION_KEY_AUTH_CODE`. + 1. Generate a secret key, if you don’t already have one. Under **Authentication**, select **+ ADD SECRET KEY**. Copy the secret key and save it in settings.json as your `SECRET_KEY`. + 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:8080/authorization-code/callback. Select **SAVE**. + 1. Set a name and email address for the signer. In settings.json, save an email address as `SIGNER_EMAIL` and a name as `SIGNER_NAME`. +**Note:** Protect your personal information. Please make sure that settings.json will not be stored in your source code repository. -If you make a mistake, simply run the settings option again. Each code example is a standalone file, but can be reached using the launcher.ps1 file. +#### Installation steps for Multiple code examples, Authorization Code Grant, and JWT Grant and JWT grant remote signing example +1. Run the launcher. In the root folder, right-click the **launcher** file and select **Run with PowerShell** > **Open**. +1. Select an API when prompted in Windows PowerShell. +1. Select **Authorization Code Grant** when authenticating your account. +1. Select your desired code example. -Use the powershell terminal to run the examples. +#### Installation steps for Authorization Code Grant embedded signing example +1. In File Explorer, open your Quickstart folder, then open the Quick_ACG folder within it. +1. Right-click the launcherACG.ps1 file and select Run with PowerShell > Open. -The examples have been tested on Windows but can conceivably be used with MacOS and Linux systems. -The source files for each example are located in the `/examples` directory. +### Installation steps for JWT Grant authentication +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip step 3 as it was automatically performed for you. Also, in order to select JSON Web Token authentication in the launcher, in config/settings.json, change the `quickstart` setting to `"false"`. +1. Extract the Quickstart ZIP file or download or clone the code-examples-powershell repository. +1. In File Explorer, open your Quickstart folder or your code-examples-powershell folder. +1. To configure the launcher for [JWT Grant](https://developers.docusign.com/platform/auth/jwt/) authentication, create a copy of the file config/settings.example.json and save the copy as config/settings.json. + 1. Add your User ID. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **My Account Information**, copy the **User ID** GUID and save it in settings.json as your `IMPERSONATION_USER_GUID`. + 1. Add your integration key. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **Apps and Integration Keys**, choose the app to use, then select **Actions** > **Edit**. Under **General Info**, copy the **Integration Key** GUID and save it in settings.json as your `INTEGRATION_KEY_JWT`. + 1. Generate an RSA key pair, if you don’t already have one. Under **Authentication**, select **+ GENERATE RSA**. Copy the private key and save it in a new file named config/private.key. + 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:8080/authorization-code/callback. Select **SAVE**. + 1. Set a name and email address for the signer. In settings.json, save an email address as `SIGNER_EMAIL` and a name as `SIGNER_NAME`. +**Note:** Protect your personal information. Please make sure that your settings.json and private.key files will not be stored in your source code repository. +1. Run the launcher. In the root folder, right-click the **launcher** file and select **Run with PowerShell** > **Open** +1. Select an API when prompted in Windows PowerShell. +1. Select **JSON Web Token** when authenticating your account. +1. When prompted, log in to your Docusign developer account. If this is your first time using the app, select **ACCEPT** at the consent window. +1. Select your desired code example. -### Payments code example -To use the payments code example, first create a test payments gateway in your account. -Follow the instructions in the -[PAYMENTS_INSTALLATION.md](https://github.com/docusign/code-examples-powershell/blob/master/PAYMENTS_INSTALLATION.md) -file. -Then add the payment gateway id to the code example file. +## Payments code example +To use the payments code example, create a test payment gateway on the [**Payments**](https://admindemo.docusign.com/authenticate?goTo=payments) page in your developer account. See [Configure a payment gateway](./PAYMENTS_INSTALLATION.md) for details. +Once you've created a payment gateway, save the **Gateway Account ID** GUID to settings.json. ## License and additional information ### License -This repository uses the MIT License. See the LICENSE file for more information. +This repository uses the MIT License. See [LICENSE](./LICENSE) for details. ### Pull Requests Pull requests are welcomed. Pull requests will only be considered if their content diff --git a/bulkexport.csv b/bulkexport.csv new file mode 100644 index 0000000..be09de1 Binary files /dev/null and b/bulkexport.csv differ diff --git a/config/settings.example.json b/config/settings.example.json index 1a51074..5baef34 100644 --- a/config/settings.example.json +++ b/config/settings.example.json @@ -6,9 +6,12 @@ "SIGNER_NOT_CHECKED_EMAIL": "{SIGNER_NOT_CHECKED_EMAIL}", "CC_EMAIL": "{CC_EMAIL}", "CC_NAME": "{CC_NAME}", - "IMPERSONATION_USER_GUID": "{IMPERSONATED_USER_GUID}", + "IMPERSONATION_USER_GUID": "{IMPERSONATED_USER_ID}", "INTEGRATION_KEY_JWT": "{INTEGRATION_KEY_JWT}", "INTEGRATION_KEY_AUTH_CODE": "{INTEGRATION_KEY_AUTH_CODE}", "SECRET_KEY": "{SECRET_KEY}", - "GATEWAY_ACCOUNT_ID": "{DS_PAYMENT_GATEWAY_ID}" -} \ No newline at end of file + "GATEWAY_ACCOUNT_ID": "{DS_PAYMENT_GATEWAY_ID}", + "ORGANIZATION_ID": "{ORGANIZATION_ID}", + "ACCOUNT_ID": "{ACCOUNT_ID}", + "TARGET_ACCOUNT_ID": "{TARGET_ACCOUNT_ID}" +} diff --git a/demo_documents/Offer_Letter_Dynamic_Table.docx b/demo_documents/Offer_Letter_Dynamic_Table.docx new file mode 100644 index 0000000..8c109be Binary files /dev/null and b/demo_documents/Offer_Letter_Dynamic_Table.docx differ diff --git a/demo_documents/World_Wide_Corp_Web_Form.pdf b/demo_documents/World_Wide_Corp_Web_Form.pdf new file mode 100644 index 0000000..b6af1fa Binary files /dev/null and b/demo_documents/World_Wide_Corp_Web_Form.pdf differ diff --git a/demo_documents/doc_1.html b/demo_documents/doc_1.html index be6ec51..f11b1d6 100644 --- a/demo_documents/doc_1.html +++ b/demo_documents/doc_1.html @@ -11,6 +11,7 @@ color: darkblue;">Order Processing Division

Ordered by {USER_FULLNAME}

Email: {USER_EMAIL}

+

Copy to: {CC_NAME}, {CC_EMAIL}

Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.

diff --git a/demo_documents/order_form.html b/demo_documents/order_form.html index ae79998..5df418d 100644 --- a/demo_documents/order_form.html +++ b/demo_documents/order_form.html @@ -26,7 +26,7 @@ Harmonica - /l1q/ + /l1q/ $5 @@ -36,7 +36,7 @@ Xylophone - /l2q/ + /l2q/ $150 diff --git a/demo_documents/web-form-config.json b/demo_documents/web-form-config.json new file mode 100644 index 0000000..047e90e --- /dev/null +++ b/demo_documents/web-form-config.json @@ -0,0 +1,2 @@ +{ "id": "608a6c8a-16b2-4419-92f4-d06bc3af6e53", "accountId": "85443307-xxxx-xxxx-xxxx-87671e153075", "isPublished": true, "isEnabled": true, "hasDraftChanges": false, "formState": "active", "formProperties": { "name": "Web Form Example Template", "isPrivateAccess": false, "allowSending": true }, "formMetadata": { "source": "templates", "createdDateTime": "2025-08-19T21:15:03.323Z", "publishedSlug": "d12c1e15b310d55b4e530edfd64c1b63", "owner": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastModifiedDateTime": "2025-08-19T21:18:40.409Z", "lastModifiedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "publishedComponentNames": { "signer_name": "TextBox", "signer_email": "Email", "FullName": "TextBox", "PhoneNumber": "TextBox", "Yes": "CheckboxGroup", "Company": "TextBox", "JobTitle": "TextBox", "$recipients": { "41f3109b-ff3a-4c48-a40c-eac994ad0988": { "components": { "signer_name": { "type": "TextBox" }, "signer_email": { "type": "Email" }, "FullName": { "type": "TextBox" }, "PhoneNumber": { "type": "TextBox" }, "Yes": { "type": "CheckboxGroup" }, "Company": { "type": "TextBox" }, "JobTitle": { "type": "TextBox" } } } } }, "admModelNamespace": "docusign.forms._85443307_664c_4c85_882f_87671e153075._608a6c8a_16b2_4419_92f4_d06bc3af6e53", "formContentModifiedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "formContentModifiedDateTime": "2025-08-19T21:18:09.337Z", "admModelVersion": "1.0.0", "type": "hasEsignTemplate", "formPropertiesModifiedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "formPropertiesModifiedDateTime": "2025-08-19T21:15:39.578Z", "sender": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastSenderConsentDateTime": "2025-08-19T21:18:34.468Z", "lastPublishedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastPublishedDateTime": "2025-08-19T21:18:40.409Z", "lastEnabledBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastEnabledDateTime": "2025-08-19T21:18:40.409Z" }, "formContent": { "components": { "Root_Of_Journey": { "children": [ "View_41f3109b-ff3a-4c48-a40c-eac994ad0988" ], "componentKey": "Root_Of_Journey", "componentName": "Root_Of_Journey", "componentRules": {}, "componentType": "Root", "schemaVersion": 2, "text": "" }, "View_41f3109b-ff3a-4c48-a40c-eac994ad0988": { "children": [ "Welcome_qXWJJEvM", "Step_69uweXrH", "Summary_2KLFQRxQ", "ESignAction_7XmnXEcI", "Thankyou_feHjbCWS" ], "componentKey": "View_41f3109b-ff3a-4c48-a40c-eac994ad0988", "componentType": "View", "isEnabled": true, "isSender": false, "roleName": "signer" }, "Welcome_qXWJJEvM": { "startButtonText": "Start", "subText": "Part-Time Work Application", "text": "Welcome", "componentKey": "Welcome_qXWJJEvM", "componentType": "Welcome" }, "Step_69uweXrH": { "children": [ "TextBox_OWBHFHgL", "Email_cJoe6lNy", "TextBox_FPT2Gs29", "TextBox_BOTyRGju", "CheckboxGroup_MyyHfWle", "TextBox_mwg3Cl1O", "TextBox_AHodbvx_" ], "componentKey": "Step_69uweXrH", "componentName": "Step_69uweXrH", "componentType": "Step", "text": "Part-Time Work Application" }, "Summary_2KLFQRxQ": { "subText": "Please review the information you have entered:", "text": "Summary", "componentKey": "Summary_2KLFQRxQ", "componentType": "Summary" }, "ESignAction_7XmnXEcI": { "componentKey": "ESignAction_7XmnXEcI", "componentType": "ESignAction", "documentInfoMap": { "Document_fRTvNpa8": { "documentId": "1", "name": "World_Wide_Web_Form", "order": "1" } }, "enableDocumentFieldEditing": true, "primaryRecipientId": "1", "recipientInfoMap": { "1": { "emailComponentKey": "Email_cJoe6lNy", "emailFromTemplate": "", "nameComponentKey": "TextBox_OWBHFHgL", "nameFromTemplate": "", "recipientId": "1", "recipientType": "signer", "roleName": "signer", "routingOrder": "1", "recipientViewId": "41f3109b-ff3a-4c48-a40c-eac994ad0988" } }, "requireRemoteSigning": false, "tabInfoMap": { "c995df10-9141-4686-b9bf-f1dfc4121d33": { "componentKey": "TextBox_FPT2Gs29", "locked": "false", "recipientId": "1", "tabId": "c995df10-9141-4686-b9bf-f1dfc4121d33", "tabLabel": "FullName", "tabType": "text" }, "71ccfefa-5469-493e-b0c7-886c65b84742": { "componentKey": "TextBox_BOTyRGju", "locked": "false", "recipientId": "1", "tabId": "71ccfefa-5469-493e-b0c7-886c65b84742", "tabLabel": "PhoneNumber", "tabType": "text" }, "184f8a65-f2be-48b9-a30e-15592c59b335": { "componentKey": "TextBox_mwg3Cl1O", "locked": "false", "recipientId": "1", "tabId": "184f8a65-f2be-48b9-a30e-15592c59b335", "tabLabel": "Company", "tabType": "text" }, "e7d30733-ce70-4fee-93a0-ea10ecc014b1": { "componentKey": "TextBox_AHodbvx_", "locked": "false", "recipientId": "1", "tabId": "e7d30733-ce70-4fee-93a0-ea10ecc014b1", "tabLabel": "JobTitle", "tabType": "text" }, "0f1e242d-550d-4729-a141-de0dce1d1b6c": { "componentKey": "CheckboxGroup_MyyHfWle", "locked": "false", "name": "Yes", "recipientId": "1", "selected": "false", "tabId": "0f1e242d-550d-4729-a141-de0dce1d1b6c", "tabLabel": "Yes", "tabType": "checkbox" } }, "templateInfoMap": { "2230c545-1680-47aa-8581-7d2284093f46": { "lastModified": "2025-08-19T21:15:03.9670000Z", "name": "Web Form Copy - Web Form Example Template", "owner": { "userName": "Raileen Del Rosario", "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "email": "example@email.com" }, "templateId": "e8af8a0d-f7ab-4cd2-b6f5-c84d5ecc1bf8" } } }, "Thankyou_feHjbCWS": { "confirmationButtonText": "Done", "confirmationButtonUrl": "", "showConfirmationButton": false, "subText": "We've received your form.", "text": "Thank you", "componentKey": "Thankyou_feHjbCWS", "componentType": "Thankyou" }, "TextBox_OWBHFHgL": { "componentKey": "TextBox_OWBHFHgL", "componentName": "signer_name", "componentType": "TextBox", "description": "", "label": "Signer Name", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "Email_cJoe6lNy": { "componentKey": "Email_cJoe6lNy", "componentName": "signer_email", "componentType": "Email", "description": "", "label": "Signer Email", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "TextBox_FPT2Gs29": { "componentKey": "TextBox_FPT2Gs29", "componentName": "FullName", "componentType": "TextBox", "description": "", "label": "Full Name", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "TextBox_BOTyRGju": { "componentKey": "TextBox_BOTyRGju", "componentName": "PhoneNumber", "componentType": "TextBox", "description": "", "label": "Phone Number", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "CheckboxGroup_MyyHfWle": { "componentKey": "CheckboxGroup_MyyHfWle", "componentName": "Yes", "componentType": "CheckboxGroup", "description": "", "label": "I prefer to be contacted by text.", "options": [ { "label": "Yes", "optionKey": "i_BVfm2G", "selected": false, "value": "Yes" } ] }, "TextBox_mwg3Cl1O": { "componentKey": "TextBox_mwg3Cl1O", "componentName": "Company", "componentType": "TextBox", "description": "", "label": "Company", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "TextBox_AHodbvx_": { "componentKey": "TextBox_AHodbvx_", "componentName": "JobTitle", "componentType": "TextBox", "description": "", "label": "Job Title", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true } }, "isStandalone": false, "templates": [ { "originalTemplateId": "e8af8a0d-f7ab-4cd2-b6f5-c84d5ecc1bf8", "clonedTemplateId": "e8af8a0d-f7ab-4cd2-b6f5-c84d5ecc1bf8", "importedDateTime": "2025-08-19T21:15:05.277Z", "recipientIds": [ "1" ] } ] }, "formLocale": "en", "versionId": 1, "eSignTemplates": [ { "templateId": "e8af8a0d-f7ab-4cd2-b6f5-c84d5ecc1bf8", "uri": "/templates/e8af8a0d-f7ab-4cd2-b6f5-c84d5ecc1bf8", "name": "Web Form Copy - Web Form Example Template", "shared": "false", "passwordProtected": "false", "description": "Example template created via the API", "created": "2025-08-19T21:15:03.7170000Z", "lastModified": "2025-08-19T21:18:40.2070000Z", "lastModifiedBy": { "userName": "Raileen Del Rosario", "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "email": "example@email.com", "uri": "/users/53d98270-xxxx-xxxx-xxxx-6d080391b538" }, "lastUsed": "2025-08-19T21:15:03.9830000Z", "owner": { "userName": "Raileen Del Rosario", "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "email": "example@email.com" }, "pageCount": "1", "folderId": "cf5522c4-cac6-4f81-9a57-d5fe179cae1c", "folderName": "Deleted Items", "folderIds": [ "cf5522c4-cac6-4f81-9a57-d5fe179cae1c" ], "autoMatch": "true", "autoMatchSpecifiedByUser": "false", "documents": [ { "documentId": "1", "uri": "/envelopes/2230c545-1680-47aa-8581-7d2284093f46/documents/1", "name": "World_Wide_Web_Form", "order": "1", "pages": "1", "documentBase64": "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MDc6MDA8L3htcDpDcmVhdGVEYXRlPg0KICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMjUtMDgtMTlUMTQ6MTg6NDAtMDc6MDA8L3htcDpNb2RpZnlEYXRlPg0KICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAyNS0wOC0xOVQxNDoxODo0MC0wNzowMDwveG1wOk1ldGFkYXRhRGF0ZT4NCiAgICA8L3JkZjpEZXNjcmlwdGlvbj4NCiAgICA8cmRmOkRlc2NyaXB0aW9uIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgcmRmOmFib3V0PSIiPg0KICAgICAgPGRjOmNyZWF0b3I+DQogICAgICAgIDxyZGY6U2VxIC8+DQogICAgICA8L2RjOmNyZWF0b3I+DQogICAgICA8ZGM6dGl0bGU+DQogICAgICAgIDxyZGY6QWx0Pg0KICAgICAgICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+V29ybGRfV2lkZV9Db3JwX1dlYl9Gb3JtPC9yZGY6bGk+DQogICAgICAgIDwvcmRmOkFsdD4NCiAgICAgIDwvZGM6dGl0bGU+DQogICAgICA8ZGM6Zm9ybWF0PmFwcGxpY2F0aW9uL3BkZjwvZGM6Zm9ybWF0Pg0KICAgIDwvcmRmOkRlc2NyaXB0aW9uPg0KICA8L3JkZjpSREY+DQo8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVuZD0idyI/PgplbmRzdHJlYW0KZW5kb2JqCjM2IDAgb2JqCjw8L0ZpbHRlci9GbGF0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", "display": "inline", "includeInDownload": "true", "signerMustAcknowledge": "no_interaction", "templateLocked": "false", "templateRequired": "false" } ], "emailSubject": "Please sign this document", "emailBlurb": "", "signingLocation": "Online", "authoritativeCopy": "false", "enforceSignerVisibility": "false", "enableWetSign": "true", "allowMarkup": "false", "allowReassign": "true", "customFields": { "textCustomFields": [ { "fieldId": "11230853228", "name": "ModelNamespace", "show": "false", "required": "false", "value": "docusign.forms._85443307_664c_4c85_882f_87671e153075._608a6c8a_16b2_4419_92f4_d06bc3af6e53" }, { "fieldId": "11230853229", "name": "ModelVersion", "show": "false", "required": "false", "value": "1" }, { "fieldId": "11230853230", "name": "ModelAccount", "show": "false", "required": "false", "value": "85443307-664c-4c85-882f-87671e153075" } ], "listCustomFields": [] }, "recipients": { "signers": [ { "defaultRecipient": "false", "tabs": { "signHereTabs": [ { "stampType": "signature", "name": "SignHere", "tabLabel": "Signature", "scaleValue": "1", "optional": "false", "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "126", "yPosition": "374", "anchorString": "/SignHere/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "be01d0ca-1057-455f-9af6-95f6074b5cf6", "tabType": "signhere" } ], "dateSignedTabs": [ { "name": "DateSigned", "value": "", "tabLabel": "DateSigned", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "409", "yPosition": "396", "anchorString": "/Date/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "a0276278-95a3-4de2-a374-15e92510112e", "tabType": "datesigned" } ], "textTabs": [ { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "FullName", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "145", "yPosition": "234", "width": "0", "height": "0", "anchorString": "/FullName/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "c995df10-9141-4686-b9bf-f1dfc4121d33", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.FullName\"}}", "tabType": "text" }, { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "PhoneNumber", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "167", "yPosition": "261", "width": "0", "height": "0", "anchorString": "/PhoneNumber/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "71ccfefa-5469-493e-b0c7-886c65b84742", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.PhoneNumber\"}}", "tabType": "text" }, { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "Company", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "182", "yPosition": "315", "width": "0", "height": "0", "anchorString": "/Company/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "184f8a65-f2be-48b9-a30e-15592c59b335", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.Company\"}}", "tabType": "text" }, { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "JobTitle", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "137", "yPosition": "342", "width": "0", "height": "0", "anchorString": "/Title/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "e7d30733-ce70-4fee-93a0-ea10ecc014b1", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.JobTitle\"}}", "tabType": "text" } ], "checkboxTabs": [ { "name": "Yes", "tabLabel": "Yes", "selected": "false", "selectedOriginal": "false", "requireInitialOnSharedChange": "false", "bold": "false", "italic": "false", "underline": "false", "required": "true", "locked": "false", "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "237", "yPosition": "288", "width": "0", "height": "0", "anchorString": "/SMS/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "0f1e242d-550d-4729-a141-de0dce1d1b6c", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.Yes.Yes\"}}", "tabType": "checkbox" } ] }, "signInEachLocation": "false", "agentCanEditEmail": "false", "agentCanEditName": "false", "requireUploadSignature": "false", "name": "", "email": "", "recipientId": "1", "recipientIdGuid": "00000000-0000-0000-0000-000000000000", "accessCode": "", "requireIdLookup": "false", "routingOrder": "1", "note": "", "roleName": "signer", "completedCount": "0", "deliveryMethod": "email", "templateLocked": "false", "templateRequired": "false", "inheritEmailNotificationConfiguration": "false", "recipientType": "signer" } ], "agents": [], "editors": [], "intermediaries": [], "carbonCopies": [], "certifiedDeliveries": [], "inPersonSigners": [], "seals": [], "witnesses": [], "notaries": [], "recipientCount": "1" }, "envelopeIdStamping": "true", "autoNavigation": "true", "uSigState": "esign", "allowComments": "true", "disableResponsiveDocument": "true", "anySigner": null, "envelopeLocation": "current_site" } ] } + diff --git a/docs/stripe_skip_account_form_link.png b/docs/stripe_skip_account_form_link.png new file mode 100644 index 0000000..766a12a Binary files /dev/null and b/docs/stripe_skip_account_form_link.png differ diff --git a/eg001EmbeddedSigning.ps1 b/eg001EmbeddedSigning.ps1 index 01597ca..1db4868 100644 --- a/eg001EmbeddedSigning.ps1 +++ b/eg001EmbeddedSigning.ps1 @@ -1,19 +1,31 @@ $apiUri = "https://demo.docusign.net/restapi" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" + +# Check the folder structure to switch paths for Quick ACG +if ((Test-Path $configPath) -eq $false) { + $configPath = "..\config\settings.json" +} +if ((Test-Path $tokenPath) -eq $false) { + $tokenPath = "..\config\ds_access_token.txt" +} +if ((Test-Path $accountIdPath) -eq $false) { + $accountIdPath = "..\config\API_ACCOUNT_ID" +} # Use embedded signing # Get required variables from .\config\settings.json file -$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json - - +$variables = Get-Content $configPath -Raw | ConvertFrom-Json # 1. Obtain your OAuth token -$accessToken = Get-Content .\config\ds_access_token.txt +$accessToken = Get-Content $tokenPath # Obtain your accountId from demo.docusign.net -- the account id is shown in # the drop down on the upper right corner of the screen by your picture or # the default picture. -$accountID = Get-Content .\config\API_ACCOUNT_ID +$accountID = Get-Content $accountIdPath # Step 2. Create the envelope definition. # The signer recipient includes a clientUserId setting @@ -28,12 +40,20 @@ $requestData = New-TemporaryFile $response = New-TemporaryFile $doc1Base64 = New-TemporaryFile +$docPath = ".\demo_documents\World_Wide_Corp_lorem.pdf" + +# Check the folder structure to switch paths for Quick ACG +if ((Test-Path $docPath) -eq $false) { + $docPath = "..\demo_documents\World_Wide_Corp_lorem.pdf" +} + # Fetch doc and encode -[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $doc1Base64 +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path $docPath))) > $doc1Base64 -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." # Concatenate the different parts of the request +#ds-snippet-start:eSign1Step2 @{ emailSubject = "Please sign this document set"; documents = @( @@ -67,8 +87,10 @@ Write-Output "Sending the envelope request to DocuSign..." }; status = "sent"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign1Step2 -# Step 3. Call DocuSign to create the envelope +# Step 3. Call Docusign to create the envelope +#ds-snippet-start:eSign1Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` @@ -78,6 +100,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign1Step3 Write-Output "Response: $(Get-Content -Raw $response)" @@ -88,11 +111,12 @@ Write-Output "EnvelopeId: $envelopeId" # Step 4. Create a recipient view definition # The signer will directly open this link from the browser to sign. # -# The returnUrl is normally your own web app. DocuSign will redirect +# The returnUrl is normally your own web app. Docusign will redirect # the signer to returnUrl when the signing completes. # For this example, we'll use http://httpbin.org/get to show the -# query parameters passed back from DocuSign +# query parameters passed back from Docusign +#ds-snippet-start:eSign1Step4 Write-Output "Requesting the url for the embedded signing..." $json = [ordered]@{ @@ -102,9 +126,10 @@ $json = [ordered]@{ 'userName' = $variables.SIGNER_NAME; 'clientUserId' = 1000 } | ConvertTo-Json -Compress +#ds-snippet-end:eSign1Step4 - -# Step 5. Create the recipient view and begin the DocuSign signing +# Step 5. Create the recipient view and begin the Docusign signing +#ds-snippet-start:eSign1Step5 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient" ` -Method 'POST' ` @@ -117,6 +142,7 @@ Invoke-RestMethod ` Write-Output "Response: $(Get-Content -Raw $response)" $signingUrl = $(Get-Content $response | ConvertFrom-Json).url +#ds-snippet-end:eSign1Step5 # ***DS.snippet.0.end Write-Output "The embedded signing URL is $signingUrl" @@ -129,4 +155,4 @@ Remove-Item $requestData Remove-Item $response Remove-Item $doc1Base64 -Write-Output "Done." +Write-Output "Done." \ No newline at end of file diff --git a/examples/Admin/eg001CreateNewUserWithActiveStatus.ps1 b/examples/Admin/eg001CreateNewUserWithActiveStatus.ps1 new file mode 100644 index 0000000..4fabd0b --- /dev/null +++ b/examples/Admin/eg001CreateNewUserWithActiveStatus.ps1 @@ -0,0 +1,98 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# Construct your API headers +#ds-snippet-start:Admin1Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin1Step2 + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Check that we have an organization id in the settings.json config file +if (!$variables.ORGANIZATION_ID) { + Write-Output "Organization ID is needed. Please add the ORGANIZATION_ID variable to the settings.json" + exit -1 +} + +$base_path = "https://api-d.docusign.net/management" +$organizationId = $variables.ORGANIZATION_ID + +# Get groups and permission profile IDs +#ds-snippet-start:Admin1Step3 +$uri = "${base_path}/v2/organizations/${organizationId}/accounts/${accountId}/permissions" +$result = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + +$permissionsObj = $($result.Content | ConvertFrom-Json).permissions +#ds-snippet-end:Admin1Step3 + +# Setup a temporary menu option to pick a permission profile +$menu = @{} + for ($i=1;$i -le $permissionsObj.count; $i++) + { Write-Output "$i. $($permissionsObj[$i-1].name)" + $menu.Add($i,($permissionsObj[$i-1].id))} + do { + [int]$selection = Read-Host 'Select an eSignature permission profile to assign to the new user' + } while ($selection -gt $permissionsObj.count -or $selection -lt 1); + $permissionsId = $menu.Item($selection) + +#ds-snippet-start:Admin1Step4 +$uri = "${base_path}/v2/organizations/${organizationId}/accounts/${accountId}/groups" +$result = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + +$groupsObj = $($result.Content | ConvertFrom-Json).groups +#ds-snippet-end:Admin1Step4 + +# Setup a temporary menu option to pick a group +$menu = @{} + for ($i=1;$i -le $groupsObj.count; $i++) + { Write-Output "$i. $($groupsObj[$i-1].name)" + $menu.Add($i,($groupsObj[$i-1].id))} + do { + [int]$selection = Read-Host 'Select an eSignature group Id to assign to the new user' + } while ($selection -gt $groupsObj.count -or $selection -lt 1); + $groupId = $menu.Item($selection) + + + +$userName = Read-Host "Enter a username for the new user" +$firstName = Read-Host "Enter the first name of the new user" +$lastName = Read-Host "Enter the last name of the new user" +$userEmail = Read-Host "Enter an email for the new user" + +# Construct the request body for the new user +#ds-snippet-start:Admin1Step5 +$body = @" +{ + "user_name": "$userName", + "first_name": "$firstName", + "last_name": "$lastName", + "email": "$userEmail", + "auto_activate_memberships": true, + "accounts": [ + { + "id": "${accountId}", + "permission_profile": { + "id": $permissionsId, + }, + "groups": [ + { + "id": $groupId, + } + ] + } + ] +} +"@ +#ds-snippet-end:Admin1Step5 + +$result = "" +# Call the Docusign Admin API +#ds-snippet-start:Admin1Step6 +$uri = "${base_path}/v2/organizations/${organizationId}/users" +$result = Invoke-WebRequest -headers $headers -Uri $uri -body $body -Method POST +$result.Content +#ds-snippet-end:Admin1Step6 diff --git a/examples/Admin/eg002CreateActiveCLMEsignUser.ps1 b/examples/Admin/eg002CreateActiveCLMEsignUser.ps1 new file mode 100644 index 0000000..19fc491 --- /dev/null +++ b/examples/Admin/eg002CreateActiveCLMEsignUser.ps1 @@ -0,0 +1,183 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Get required variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +$base_path = "https://api-d.docusign.net/management" +$organizationId=$variables.ORGANIZATION_ID + +# Construct your API headers +#ds-snippet-start:Admin2Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin2Step2 + + +try { + # Display the JSON response + # Write-Output "Response:" + #ds-snippet-start:Admin2Step3 + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/products/permission_profiles" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + + $productProfiles = $($response.Content | ConvertFrom-Json).product_permission_profiles + #ds-snippet-end:Admin2Step3 +} +catch { + Write-Output "Error:" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + + +$esignPermissionProfiles = $null +$clmPermissionProfiles = $null +foreach ($productProfile in $productProfiles) { + if ($productProfile.product_name -eq "ESign") { + $esignPermissionProfiles = $productProfile.permission_profiles + $esignProductId = $productProfile.product_id + } else { + $clmPermissionProfiles = $productProfile.permission_profiles + $clmProductId = $productProfile.product_id + } +} + +if($null -eq $esignPermissionProfiles){ + Write-Output "You must create an eSignature permission profile before running this code example" + exit 1 +} elseif ($esignPermissionProfiles.count -eq 1){ + $esignPermissionProfileId = $esignPermissionProfiles[0].permission_profile_id +} else { + $menu = @{} + for ($i=1;$i -le $esignPermissionProfiles.count; $i++) + { Write-Output "$i. $($esignPermissionProfiles[$i-1].permission_profile_name)" + $menu.Add($i,($esignPermissionProfiles[$i-1].permission_profile_id))} + do { + [int]$selection = Read-Host 'Select an eSignature permission profile to assign to the new user' + } while ($selection -gt $esignPermissionProfiles.count -or $selection -lt 1); + $esignPermissionProfileId = $menu.Item($selection) +} + +if($null -eq $clmPermissionProfiles){ + Write-Output "You must create a CLM permission profile before running this code example" + exit 1 +} elseif ($clmPermissionProfiles.count -eq 1){ + $clmPermissionProfileId = $clmPermissionProfiles[0].permission_profile_id +} else { + $menu = @{} + for ($i=1;$i -le $clmPermissionProfiles.count; $i++) + { Write-Output "$i. $($clmPermissionProfiles[$i-1].permission_profile_name)" + $menu.Add($i,($clmPermissionProfiles[$i-1].permission_profile_id))} + do { + [int]$selection = Read-Host 'Select a CLM permission profile to assign to the new user' + } while ($selection -gt $clmPermissionProfiles.count -or $selection -lt 1); + $clmPermissionProfileId = $menu.Item($selection) +} + + +try { + # Display the JSON response + Write-Output "Response:" + #ds-snippet-start:Admin2Step4 + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/dsgroups" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + $dsGroups = $($response.Content | ConvertFrom-Json).ds_groups + #ds-snippet-end:Admin2Step4 +} +catch { + Write-Output "Error:" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + + +if($dsGroups.count -eq 0){ + Write-Output "You must create a DS Group before running this code example" + exit 1 +} elseif ($dsGroups.count -eq 1){ + $dsGroupId = $dsGroups[0].ds_group_id +} else { + $menu = @{} + for ($i=1;$i -le $dsGroups.count; $i++) + { Write-Output "$i. $($dsGroups[$i-1].group_name)" + $menu.Add($i,($dsGroups[$i-1].ds_group_id))} + do { + [int]$selection = Read-Host 'Select a DS Group to assign the new user to' + } while ($selection -gt $dsGroups.count -or $selection -lt 1); + $dsGroupId = $menu.Item($selection) +} + +$userName = Read-Host "Enter a username for the new user" +$firstName = Read-Host "Enter the first name of the new user" +$lastName = Read-Host "Enter the last name of the new user" +$userEmail = Read-Host "Enter an email for the new user" + +# Construct the request body for the new user +#ds-snippet-start:Admin2Step5 +$body = @" +{ + "user_name": "$userName", + "first_name": "$firstName", + "last_name": "$lastName", + "email": "$userEmail", + "auto_activate_memberships": true, + "product_permission_profiles": [ + { + "permission_profile_id": "$esignPermissionProfileId", + "product_id": "$esignProductId" + }, + { + "permission_profile_id": "$clmPermissionProfileId", + "product_id": "$clmProductId" + } + ], + "ds_groups": [ + { + "ds_group_id": "$dsGroupId" + } + ] +} +"@ +#ds-snippet-end:Admin2Step5 + + +try { + # Display the JSON response + Write-Output "Response:" + #ds-snippet-start:Admin2Step6 + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/users/" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -body $body -method POST + $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 5 + #ds-snippet-end:Admin2Step6 + + # Store user email to the file for future reference + $($response.Content | ConvertFrom-Json).email > .\config\ESIGN_CLM_USER_EMAIL + Write-Output "Done" +} +catch { + Write-Output "Unable to create a new user." + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} diff --git a/examples/Admin/eg003BulkExportUserData.ps1 b/examples/Admin/eg003BulkExportUserData.ps1 new file mode 100644 index 0000000..1261c5d --- /dev/null +++ b/examples/Admin/eg003BulkExportUserData.ps1 @@ -0,0 +1,86 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt + +# Construct your API headers +#ds-snippet-start:Admin3Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin3Step2 + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Check that we have an organization id in the settings.json config file +if (!$variables.ORGANIZATION_ID) { + Write-Output "Organization ID is needed. Please add the ORGANIZATION_ID variable to the settings.json" + exit -1 +} + +$base_path = "https://api-d.docusign.net/management" +$organizationId = $variables.ORGANIZATION_ID + +# Create the bulk export request +$uri2 = "${base_path}/v2/organizations/$organizationId/exports/user_list" + +$body = @" +{ + "type": "organization_memberships_export" +} +"@ + +$result = Invoke-WebRequest -headers $headers -Uri $uri2 -body $body -Method POST + +# Get request Id +$requestId = $($result.Content | ConvertFrom-Json).id +$requestId + +#ds-snippet-start:Admin3Step3 +Write-Output "Checking Bulk Action Status" +$uri2 = "${base_path}/v2/organizations/$organizationId/exports/user_list/$requestId" +$result2 = Invoke-WebRequest -headers $headers -Uri $uri2 -Method GET +$result2.Content +$results = $result2 | ConvertFrom-Json +$retrycount = 0 + +Do { + Write-Output "The Bulk Action has not been completed. Retrying in 5 seconds. To abort, Press Control+C" + Start-Sleep 5 + $result2 = Invoke-WebRequest -headers $headers -Uri $uri2 -Method GET + $resultStatus = $($result2 | ConvertFrom-Json).status + $retrycount++ + if ($retrycount -eq 5) { + exit 1 + } +} While ($resultStatus -ne "completed") + +if ($resultStatus -eq "completed") { + Write-Output $($result2 | ConvertFrom-Json).results.id + $resultId = $($result2 | ConvertFrom-Json).results.id +} +else { + Write-Output "An error has occurred, the Bulk Action has not been completed." + exit 1 +} +#ds-snippet-end:Admin3Step3 + +# Check the request status +#ds-snippet-start:Admin3Step4 +$uri2 = "${base_path}/v2/organizations/$organizationId/exports/user_list/$requestId" +$result2 = Invoke-WebRequest -headers $headers -Uri $uri2 -Method GET +$result2.Content +$results = $result2 | ConvertFrom-Json +#ds-snippet-end:Admin3Step4 +$results + +# Get result Id +$resultId = $($result2 | ConvertFrom-Json).results.id + +# Download the exported user data +#ds-snippet-start:Admin3Step5 +$uri3 = "https://demo.docusign.net/restapi/v2/organization_exports/$organizationId/user_list/$resultId" +$result3 = Invoke-WebRequest -headers $headers -Uri $uri3 -Method GET +$result3.Content +#ds-snippet-end:Admin3Step5 +Write-Output "Export data to file bulkexport.csv ..." +$result3.Content > bulkexport.csv diff --git a/examples/Admin/eg004AddUsersViaBulkImport.ps1 b/examples/Admin/eg004AddUsersViaBulkImport.ps1 new file mode 100644 index 0000000..ab78979 --- /dev/null +++ b/examples/Admin/eg004AddUsersViaBulkImport.ps1 @@ -0,0 +1,51 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# Construct your API headers +#ds-snippet-start:Admin4Step2 +$headers1 = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers1.add("Authorization","Bearer ${accessToken}") +$headers1.add("Content-Disposition", "filename=bulkimport.csv") +$headers1.add("Content-Type","text/csv") + +$headers2 = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers2.add("Authorization","Bearer ${accessToken}") +#ds-snippet-end:Admin4Step2 + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Check that we have an organization id in the settings.json config file +if (!$variables.ORGANIZATION_ID) { + Write-Output "Organization ID is needed. Please add the ORGANIZATION_ID variable to the settings.json" + exit -1 +} + +$base_path = "https://api-d.docusign.net/management" +$organizationId = $variables.ORGANIZATION_ID + +# Create the bulk import request +#ds-snippet-start:Admin4Step3 +$body = @" +AccountID,AccountName,FirstName,LastName,UserEmail,eSignPermissionProfile,Group,Language,UserTitle,CompanyName,AddressLine1,AddressLine2,City,StateRegionProvince,PostalCode,Phone,LoginPolicy,AutoActivate +$accountId,Sample Account,John,Markson,user1email@example.com,Account Administrator,Everyone,en,Mr.,Some Division,123 4th St,Suite C1,Seattle,WA,8178,2065559999,fedAuthRequired,true +$accountId,Sample Account,Jill,Smith,user2email@example.com,Account Administrator,Everyone,en,Mr.,Some Division,123 4th St,Suite C1,Seattle,WA,8178,2065559999,fedAuthRequired,true +$accountId,Sample Account,James,Grayson,user3emailt@example.com,Account Administrator,Everyone,en,Mr.,Some Division,123 4th St,Suite C1,Seattle,WA,8178,2065559999,fedAuthRequired,true +"@ + +$uri1 = "${base_path}/v2/organizations/$organizationId/imports/bulk_users/add" +$result1 = Invoke-WebRequest -headers $headers1 -Uri $uri1 -body $body -Method POST +$result1.Content +$results = $result1 | ConvertFrom-Json +$importId = $results.id +#ds-snippet-end:Admin4Step3 + +# Check the request status +Write-Output "Sleep 20 seconds..." +Start-Sleep 20 +#ds-snippet-start:Admin4Step4 +$uri2 = "${base_path}/v2/organizations/$organizationId/imports/bulk_users/$importId" +$result2 = Invoke-WebRequest -headers $headers2 -Uri $uri2 -Method GET +$result2.Content +#ds-snippet-end:Admin4Step4 diff --git a/examples/Admin/eg005AuditUsers.ps1 b/examples/Admin/eg005AuditUsers.ps1 new file mode 100644 index 0000000..c0bb422 --- /dev/null +++ b/examples/Admin/eg005AuditUsers.ps1 @@ -0,0 +1,75 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Get required variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +$base_path = "https://api-d.docusign.net/management" +$organizationId=$variables.ORGANIZATION_ID + +# Check that organizationId has been set +if($null -eq $organizationId) +{ + Write-Output "PROBLEM: Please add your ORGANIZATION_ID to settings.json." + Exit +} + +# Construct your API headers +#ds-snippet-start:Admin5Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin5Step2 + +#ds-snippet-start:Admin5Step3 +$modifiedSince = (Get-Date (Get-Date).AddDays(-10) -Format "yyyy-MM-dd") + +try { + # Display the JSON response + Write-Output "Response:" + $uri = "${base_path}/management/v2/organizations/${organizationId}/users?account_id=${APIAccountId}&last_modified_since=${modifiedSince}" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 4 + $modifiedUsers = $($response.Content | ConvertFrom-Json).users +} +catch { + Write-Output "Error:" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} +#ds-snippet-end:Admin5Step3 + +#ds-snippet-start:Admin5Step4 +$userEmails = $modifiedUsers.email +#ds-snippet-end:Admin5Step4 + +#ds-snippet-start:Admin5Step5 +foreach ($emailAddress in $userEmails){ + try { + # Display the JSON response + Write-Output "Response:" + $uri = "${base_path}/management/v2/organizations/${organizationId}/users/profile?email=${emailAddress}" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 4 + } + catch { + Write-Output "Error:" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 + } +} +#ds-snippet-end:Admin5Step5 \ No newline at end of file diff --git a/examples/Admin/eg006GetUserProfileByEmail.ps1 b/examples/Admin/eg006GetUserProfileByEmail.ps1 new file mode 100644 index 0000000..94b88af --- /dev/null +++ b/examples/Admin/eg006GetUserProfileByEmail.ps1 @@ -0,0 +1,31 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt + +# Construct your API headers +#ds-snippet-start:Admin6Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin6Step2 + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Check that we have an organization id in the settings.json config file +if (!$variables.ORGANIZATION_ID) { + Write-Output "Organization ID is needed. Please add the ORGANIZATION_ID variable to the settings.json" + exit -1 +} + +$base_path = "https://api-d.docusign.net/management" +$organizationId = $variables.ORGANIZATION_ID + +$email = Read-Host "Enter the user's email address" + +$result = "" +# Call the Docusign Admin API +#ds-snippet-start:Admin6Step3 +$uri = "${base_path}/v2.1/organizations/${organizationId}/users/dsprofile?email=${email}" +$result = Invoke-WebRequest -headers $headers -Uri $uri -body $body -Method GET +$result.Content +#ds-snippet-end:Admin6Step3 diff --git a/examples/Admin/eg007GetUserProfileByUserId.ps1 b/examples/Admin/eg007GetUserProfileByUserId.ps1 new file mode 100644 index 0000000..dbfcd0a --- /dev/null +++ b/examples/Admin/eg007GetUserProfileByUserId.ps1 @@ -0,0 +1,31 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt + +# Construct your API headers +#ds-snippet-start:Admin7Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin7Step2 + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Check that we have an organization id in the settings.json config file +if (!$variables.ORGANIZATION_ID) { + Write-Output "Organization ID is needed. Please add the ORGANIZATION_ID variable to the settings.json" + exit -1 +} + +$base_path = "https://api-d.docusign.net/management" +$organizationId = $variables.ORGANIZATION_ID + +$userId = Read-Host "Enter the user's User ID" + +$result = "" +# Call the Docusign Admin API +#ds-snippet-start:Admin7Step3 +$uri = "${base_path}/v2.1/organizations/${organizationId}/users/${userId}/dsprofile" +$result = Invoke-WebRequest -headers $headers -Uri $uri -body $body -Method GET +$result.Content +#ds-snippet-end:Admin7Step3 diff --git a/examples/Admin/eg008UpdateUserProductPermissionProfile.ps1 b/examples/Admin/eg008UpdateUserProductPermissionProfile.ps1 new file mode 100644 index 0000000..9a74af3 --- /dev/null +++ b/examples/Admin/eg008UpdateUserProductPermissionProfile.ps1 @@ -0,0 +1,170 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID +$emailAddressFile = ".\config\ESIGN_CLM_USER_EMAIL" + +# Get required variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json +$base_path = "https://api-d.docusign.net/management" +$organizationId=$variables.ORGANIZATION_ID + +# Check that we have an email address of created user +if (Test-Path $emailAddressFile) { + $emailAddress = Get-Content $emailAddressFile + + try { + $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" + $headers.add("Authorization", "Bearer $accessToken") + $headers.add("Content-Type", "application/json") + + $uri = "${base_path}/v2.1/organizations/${organizationId}/users/dsprofile?email=${emailAddress}" + $response = Invoke-WebRequest -headers $headers -Uri $uri -body $body -Method GET + } catch { + Write-Output "The user with stored email address is not present in the account." + Write-Output "Please run example 2: 'Create a new active CLM and eSignature user' before running this code example" + exit 1 + } +} else { + Write-Output "Please run example 2: 'Create a new active CLM and eSignature user' before running this code example" + exit 1 +} + +# Construct your API headers +#ds-snippet-start:Admin8Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin8Step2 + +try { + # Get all permission profiles + Write-Output "Getting permission profiles..." + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/products/permission_profiles" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + $productProfiles = $($response.Content | ConvertFrom-Json).product_permission_profiles + + # Get and showcase permission profiles that are currently added to the user + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/products/permission_profiles/users?email=${emailAddress}" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + + Write-Output "Response:" + Write-Output "" + Write-Output $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 5 + Write-Output "" +} +catch { + Write-Output "Error:" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +$esignPermissionProfiles = $null +$clmPermissionProfiles = $null + +foreach ($productProfile in $productProfiles) { + if ($productProfile.product_name -eq "ESign") { + $esignPermissionProfiles = $productProfile.permission_profiles + $esignProductId = $productProfile.product_id + } else { + $clmPermissionProfiles = $productProfile.permission_profiles + $clmProductId = $productProfile.product_id + } +} + +Write-Output "" +Write-Output "Update user product permission profile for the following email: $emailAddress" +Write-Output "" + +Enum listProductChoices { + CLM = 1; + eSignature = 2; +} +$listProductChoicesView = $null; +do { + Write-Output "$([int][listProductChoices]::CLM)) CLM" + Write-Output "$([int][listProductChoices]::eSignature)) eSignature" + [int]$listProductChoicesView = Read-Host "Would you like to update the permission profile for the eSignature or CLM product?" +} while (-not [listProductChoices]::IsDefined([listProductChoices], $listProductChoicesView)); + +if ($listProductChoicesView -eq [listProductChoices]::CLM) { + $productId = $clmProductId + Write-Output "" + + $menu = @{} + for ($i=1;$i -le $clmPermissionProfiles.count; $i++) { + Write-Output "$i. $($clmPermissionProfiles[$i-1].permission_profile_name)" + $menu.Add($i,($clmPermissionProfiles[$i-1].permission_profile_id)) + } + + do { + [int]$selection = Read-Host 'Select a CLM permission profile to update' + } while ($selection -gt $clmPermissionProfiles.count -or $selection -lt 1); + + $permissionProfileId = $menu.Item($selection) +} else { + $productId = $esignProductId + Write-Output "" + + $menu = @{} + for ($i=1;$i -le $esignPermissionProfiles.count; $i++) { + Write-Output "$i. $($esignPermissionProfiles[$i-1].permission_profile_name)" + $menu.Add($i,($esignPermissionProfiles[$i-1].permission_profile_id)) + } + + do { + [int]$selection = Read-Host 'Select an eSignature permission profile to update' + } while ($selection -gt $esignPermissionProfiles.count -or $selection -lt 1); + + $permissionProfileId = $menu.Item($selection) +} + +# Construct the request body +#ds-snippet-start:Admin8Step3 +$body = @" +{ + "email": "$emailAddress", + "product_permission_profiles": [ + { + "product_id": '$productId', + "permission_profile_id": '$permissionProfileId', + } + ] +} +"@ +#ds-snippet-end:Admin8Step3 + +try { + # Display the JSON response + Write-Output "Response:" +#ds-snippet-start:Admin8Step4 + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/products/permission_profiles/users" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -body $body -method POST + $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 4 +#ds-snippet-end:Admin8Step4 + + Write-Output "Done" +} +catch { + Write-Output "Unable to update the permission profiles." + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} diff --git a/examples/Admin/eg009DeleteUserProductPermissionProfile.ps1 b/examples/Admin/eg009DeleteUserProductPermissionProfile.ps1 new file mode 100644 index 0000000..ef9ac2f --- /dev/null +++ b/examples/Admin/eg009DeleteUserProductPermissionProfile.ps1 @@ -0,0 +1,147 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID +$emailAddressFile = ".\config\ESIGN_CLM_USER_EMAIL" + +# Get required variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json +$base_path = "https://api-d.docusign.net/management" +$organizationId=$variables.ORGANIZATION_ID + +# Check that we have an email address of created user +if (Test-Path $emailAddressFile) { + $emailAddress = Get-Content $emailAddressFile + + try { + $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" + $headers.add("Authorization", "Bearer $accessToken") + $headers.add("Content-Type", "application/json") + + $uri = "${base_path}/v2.1/organizations/${organizationId}/users/dsprofile?email=${emailAddress}" + $response = Invoke-WebRequest -headers $headers -Uri $uri -body $body -Method GET + } catch { + Write-Output "The user with stored email address is not present in the account." + Write-Output "Please run example 2: 'Create a new active CLM and eSignature user' before running this code example" + exit 1 + } +} else { + Write-Output "Please run example 2: 'Create a new active CLM and eSignature user' before running this code example" + exit 1 +} + +# Construct your API headers +#ds-snippet-start:Admin9Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin9Step2 + +try { + # Display the JSON response + Write-Output "Getting permission profiles by email address..." + #ds-snippet-start:Admin9Step3 + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/products/permission_profiles/users?email=${emailAddress}" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + $productProfiles = $($response.Content | ConvertFrom-Json).product_permission_profiles + #ds-snippet-end:Admin9Step3 + + Write-Output "Response:" + Write-Output "" + Write-Output $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 5 + Write-Output "" +} +catch +{ + Write-Output "Error:" + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +$clmProductId = "37f013eb-7012-4588-8028-357b39fdbd00" +$esignProductId = "f6406c68-225c-4e9b-9894-64152a26fa83" + +Write-Output "" +Write-Output "Delete user product permission profile for the following email: " $emailAddress +Write-Output "" + +Enum listProductChoices { + CLM = 1; + eSignature = 2; +} + +$listProductChoicesView = $null; +do { + Write-Output "$([int][listProductChoices]::CLM)) CLM" + Write-Output "$([int][listProductChoices]::eSignature)) eSignature" + [int]$listProductChoicesView = Read-Host "Which product permission profile would you like to delete?" +} while (-not [listProductChoices]::IsDefined([listProductChoices], $listProductChoicesView)); + +if ($listProductChoicesView -eq [listProductChoices]::CLM) { + $productId = $clmProductId +} else { + $productId = $esignProductId +} + + +foreach ($productProfile in $productProfiles) { + if ($productProfile.product_id -eq $productId) { + $userHasProductPermissions = "true" + } +} + +if ($null -eq $userHasProductPermissions) { + Write-Output "" + Write-Output "This user was already removed from this product." + Write-Output "Please, try another product or run example 2: 'Create a new active CLM and eSignature user' to create a user with both product accesses." + Write-Output "" +} else { + # Construct the request body +#ds-snippet-start:Admin9Step4 + $body = @" + { + "user_email": "$emailAddress", + "product_ids": [ + "$productId", + ] + } +"@ +#ds-snippet-end:Admin9Step4 + + try { + # Display the JSON response + Write-Output "Response:" +#ds-snippet-start:Admin9Step5 + $uri = "${base_path}/v2.1/organizations/${organizationId}/accounts/${APIAccountId}/products/users" + Invoke-WebRequest -uri $uri -headers $headers -body $body -method DELETE +#ds-snippet-end:Admin9Step5 + + Write-Output "Product permission profile has been deleted." + Write-Output "" + Write-Output "Done" + } catch { + Write-Output "Unable to delete the permission profile." + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 + } +} diff --git a/examples/Admin/eg010DeleteUserDataFromOrganization.ps1 b/examples/Admin/eg010DeleteUserDataFromOrganization.ps1 new file mode 100644 index 0000000..4113d9b --- /dev/null +++ b/examples/Admin/eg010DeleteUserDataFromOrganization.ps1 @@ -0,0 +1,62 @@ +$accessToken = Get-Content .\config\ds_access_token.txt + +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json +$basePath = "https://api-d.docusign.net/management" +$organizationId=$variables.ORGANIZATION_ID + +#ds-snippet-start:Admin10Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Admin10Step2 + +# Get user information +$emailAddress = Read-Host "Please input the email address of the user whose data will be deleted. Note that this email address should be associated with a user that has been closed for at least 24 hours." + +$uri = "${basePath}/v2.1/organizations/${organizationId}/users/dsprofile?email=${emailAddress}" +$response = Invoke-WebRequest -headers $headers -Uri $uri -body $body -Method GET + +$userId = $($response.Content | ConvertFrom-Json).users.id +$accountId = $($response.Content | ConvertFrom-Json).users.memberships.account_id + +# Construct the request body +#ds-snippet-start:Admin10Step3 +$body = @" + { + "user_id": "$userId", + "memberships": [{ + "account_id": "$accountId", + }] + } +"@ +#ds-snippet-end:Admin10Step3 + +try { + # Display the JSON response + Write-Output "" + Write-Output "Response:" + #ds-snippet-start:Admin10Step4 + $uri = "${basePath}/v2/data_redaction/organizations/${organizationId}/user" + + $result = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST + $result.content + #ds-snippet-end:Admin10Step4 +} catch { + Write-Output "Unable to delete the user." + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +Write-Output "" +Write-Output "Done" diff --git a/examples/Admin/eg011DeleteUserDataFromAccount.ps1 b/examples/Admin/eg011DeleteUserDataFromAccount.ps1 new file mode 100644 index 0000000..b3efeaf --- /dev/null +++ b/examples/Admin/eg011DeleteUserDataFromAccount.ps1 @@ -0,0 +1,52 @@ +$accessToken = Get-Content .\config\ds_access_token.txt + +$basePath = "https://api-d.docusign.net/management" +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +#ds-snippet-start:Admin11Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Admin11Step2 + +# Get user information +$userId = Read-Host "Please enter the user ID of the user whose data will be deleted. Note that this user ID should be associated with a user that has been closed for at least 24 hours." + +# Construct the request body +#ds-snippet-start:Admin11Step3 +$body = @" + { + "user_id": "$userId", + } +"@ +#ds-snippet-end:Admin11Step3 + +try { + # Display the JSON response + Write-Output "" + Write-Output "Response:" + #ds-snippet-start:Admin11Step4 + $uri = "${basePath}/v2/data_redaction/accounts/${APIAccountId}/user" + + $result = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST + $result.content + #ds-snippet-end:Admin11Step4 +} catch { + Write-Output "Unable to delete the user." + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +Write-Output "" +Write-Output "Done" diff --git a/examples/Admin/eg012CloneAccount.ps1 b/examples/Admin/eg012CloneAccount.ps1 new file mode 100644 index 0000000..83588cc --- /dev/null +++ b/examples/Admin/eg012CloneAccount.ps1 @@ -0,0 +1,112 @@ +$accessToken = Get-Content .\config\ds_access_token.txt + +# Get required variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +$basePath = "https://api-d.docusign.net/management" +$organizationId=$variables.ORGANIZATION_ID + +# Construct your API headers +#ds-snippet-start:Admin12Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Admin12Step2 + +$response = $null + +try { + # Retrieve asset group accounts + Write-Output "" + Write-Output "Accounts to clone:" + + #ds-snippet-start:Admin12Step3 + $uri = "${basePath}/v1/organizations/${organizationId}/assetGroups/accounts?compliant=true" + $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + #ds-snippet-end:Admin12Step3 +} catch { + Write-Output "Unable retrieve asset group accounts." + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +$sourceAccountId = $null +$accounts = $($response.Content | ConvertFrom-Json).assetGroupAccounts +if ($accounts.count -eq 1){ + $sourceAccountId = $accounts[0].accountId +} else { + $menu = @{} + for ($i=1;$i -le $accounts.count; $i++) + { Write-Output "$i. $($accounts[$i-1].accountName)" + $menu.Add($i,($accounts[$i-1].accountId))} + do { + [int]$selection = Read-Host 'Select an account to clone' + } while ($selection -gt $accounts.count -or $selection -lt 1); + $sourceAccountId = $menu.Item($selection) +} + +$targetAccountName = Read-Host "Please enter the name of the new account" +$targetAccountFirstName = Read-Host "Please enter the first name of the new account admin" +$targetAccountLastName = Read-Host "Please enter the last name of the new account admin" +$targetAccountEmail = Read-Host "Please enter the email address of the new account admin" + +#ds-snippet-start:Admin12Step4 +# The country code value is set to "US" for the developer environment +# In production, set the value to the code for the country of the target account +$body = @" +{ + "sourceAccount": { + "id": "$sourceAccountId" + }, + "targetAccount": { + "name": "$targetAccountName", + "admin": { + "firstName": "$targetAccountFirstName", + "lastName": "$targetAccountLastName", + "email": "$targetAccountEmail" + }, + "countryCode": "US" + } +} +"@ +#ds-snippet-end:Admin12Step4 + +try { + # Clone source account into new account + Write-Output "" + Write-Output "Response:" + + #ds-snippet-start:Admin12Step5 + $uri = "${basePath}/v1/organizations/${organizationId}/assetGroups/accountClone" + $response = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST + $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 5 + #ds-snippet-end:Admin12Step5 +} catch { + Write-Output "Failed to clone an account." + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +Write-Output "" +Write-Output "Done" diff --git a/examples/Admin/eg013CreateAccount.ps1 b/examples/Admin/eg013CreateAccount.ps1 new file mode 100644 index 0000000..ae23e92 --- /dev/null +++ b/examples/Admin/eg013CreateAccount.ps1 @@ -0,0 +1,97 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Get required variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json +$base_path = "https://api-d.docusign.net/management" +$organizationId=$variables.ORGANIZATION_ID + +# Construct your API headers +#ds-snippet-start:Admin13Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Admin13Step2 + +try { + #ds-snippet-start:Admin13Step3 + $uri = "${base_path}/v2/organizations/${organizationId}/planItems" + $response = Invoke-WebRequest -headers $headers -Uri $uri -Method GET + + Write-Output "Results from the GET request:" + $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 5 + #ds-snippet-end:Admin13Step3 +} catch { + Write-Output "Error:" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +$responseJson = $response.Content | ConvertFrom-Json +$planId = $responseJson.plan_id +$subscriptionId = $responseJson.subscription_id + +$emailAddress = Read-Host "Please enter the email address for the new account" +$firstName = Read-Host "Please enter the first name for the new account" +$lastName = Read-Host "Please enter the last name for the new account" + +#ds-snippet-start:Admin13Step4 +# The country code value is set to "US" for the developer environment +# In production, set the value to the code for the country of the target account +$body = @" +{ + "subscriptionDetails": { + "id": "$subscriptionId", + "planId": "$planId", + "modules": [] + }, + "targetAccount": { + "name": "CreatedThroughAPI", + "countryCode": "US", + "admin": { + "email": "$emailAddress", + "firstName": "$firstName", + "lastName": "$lastName", + "locale": "en" + } + } +} +"@ +#ds-snippet-end:Admin13Step4 + +try { + #ds-snippet-start:Admin13Step5 + $uri = "${base_path}/v2/organizations/${organizationId}/assetGroups/accountCreate" + $response = Invoke-WebRequest -headers $headers -Uri $uri -body $body -Method POST + + Write-Output "Results from the create account method:" + $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 5 + #ds-snippet-end:Admin13Step5 +} +catch +{ + Write-Output "Error:" + + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line + exit 1 +} + +Write-Output "" +Write-Output "Done" diff --git a/examples/Click/eg001CreateClickwrap.ps1 b/examples/Click/eg001CreateClickwrap.ps1 index 6c60997..1ed129b 100644 --- a/examples/Click/eg001CreateClickwrap.ps1 +++ b/examples/Click/eg001CreateClickwrap.ps1 @@ -2,13 +2,18 @@ $accessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID +$clickwrapName = Read-Host "Please input a name for the clickwrap: " + # Step 2. Construct your API headers +#ds-snippet-start:Click1Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Click1Step2 # Step 3. Construct the request body +#ds-snippet-start:Click1Step3 $body = @" { "displaySettings": { @@ -18,7 +23,6 @@ $body = @" "format": "modal", "hasAccept": true, "mustRead": true, - "mustView": true, "requireAccept": true, "size": "medium", "documentDisplay": "document" @@ -31,17 +35,21 @@ $body = @" "order": 0 } ], - "name": "Terms of Service", + "name": "$clickwrapName", "requireReacceptance": true } "@ +#ds-snippet-end:Click1Step3 # Step 4. Call the Click API # a) Make a POST call to the clickwraps endpoint to create a clickwrap for an account # b) Display the JSON structure of the created clickwrap +#ds-snippet-start:Click1Step4 $uri = "https://demo.docusign.net/clickapi/v1/accounts/$APIAccountId/clickwraps" $result = Invoke-WebRequest -headers $headers -Uri $uri -UseBasicParsing -Method POST -Body $body +Write-Output "Response: " $result.Content +#ds-snippet-end:Click1Step4 # Store clickwrapId to the file for future reference $($result.Content | ConvertFrom-Json).clickwrapId > .\config\CLICKWRAP_ID \ No newline at end of file diff --git a/examples/Click/eg002ActivateClickwrap.ps1 b/examples/Click/eg002ActivateClickwrap.ps1 index 11750f0..9b06052 100644 --- a/examples/Click/eg002ActivateClickwrap.ps1 +++ b/examples/Click/eg002ActivateClickwrap.ps1 @@ -12,23 +12,30 @@ if (Test-Path .\config\CLICKWRAP_ID) { } # Step 2. Construct your API headers +#ds-snippet-start:Click2Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Click2Step2 # Note: These values are not valid, but are shown for example purposes only! $VersionNumber = "1" # Construct your clickwrap JSON body +#ds-snippet-start:Click2Step3 $body = @" { "status" : "active" } "@ +#ds-snippet-end:Click2Step3 # a) Make a POST call to updateClickwrapVersionByNumber # b) Display the JSON structure of the created clickwrap +#ds-snippet-start:Click2Step4 $uri = "https://demo.docusign.net/clickapi/v1/accounts/$APIAccountId/clickwraps/$ClickWrapId/versions/$VersionNumber" $result = Invoke-WebRequest -headers $headers -Uri $uri -UseBasicParsing -Method PUT -Body $body +Write-Output "Response: " $result.Content +#ds-snippet-end:Click2Step4 diff --git a/examples/Click/eg003CreateNewClickwrapVersion.ps1 b/examples/Click/eg003CreateNewClickwrapVersion.ps1 index e75926d..c3f39b7 100644 --- a/examples/Click/eg003CreateNewClickwrapVersion.ps1 +++ b/examples/Click/eg003CreateNewClickwrapVersion.ps1 @@ -12,12 +12,15 @@ if (Test-Path .\config\CLICKWRAP_ID) { } # Step 2. Construct your API headers +#ds-snippet-start:Click3Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Click3Step2 # Construct your clickwrap JSON body +#ds-snippet-start:Click3Step3 $body = @" { "name": "Terms of Service", @@ -25,7 +28,6 @@ $body = @" "displaySettings": { "displayName": "Terms of Service v2", "mustRead": true, - "mustView": false, "requireAccept": false, "downloadable": false, "sendToEmail": false, @@ -47,10 +49,14 @@ $body = @" "requireReacceptance": true } "@ +#ds-snippet-end:Click3Step3 # Step 4. Call the Click API # a) Make a POST call to the versions endpoint to update a specific clickwrap (create a version) for an account # b) Display the returned JSON structure of the versioned clickwrap +#ds-snippet-start:Click3Step4 $uri = "https://demo.docusign.net/clickapi/v1/accounts/$APIAccountId/clickwraps/$ClickWrapId/versions" $result = Invoke-WebRequest -headers $headers -Uri $uri -UseBasicParsing -body $body -Method POST +Write-Output "Response: " $result.Content +#ds-snippet-end:Click3Step4 diff --git a/examples/Click/eg004GetListOfClickwraps.ps1 b/examples/Click/eg004GetListOfClickwraps.ps1 index 988bf76..2842f8d 100644 --- a/examples/Click/eg004GetListOfClickwraps.ps1 +++ b/examples/Click/eg004GetListOfClickwraps.ps1 @@ -3,14 +3,19 @@ $accessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Step 2. Construct your API headers +#ds-snippet-start:Click4Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Click4Step2 # Step 3. Call the Click API # a) Make a GET call to the clickwraps endpoint to retrieve all clickwraps for an account # b) Display the JSON structure of the returned clickwraps +#ds-snippet-start:Click4Step3 $uri = "https://demo.docusign.net/clickapi/v1/accounts/$APIAccountId/clickwraps" $result = Invoke-WebRequest -headers $headers -Uri $uri -UseBasicParsing -Method GET +Write-Output "Response: " $result.Content +#ds-snippet-end:Click4Step3 diff --git a/examples/Click/eg005GetClickwrapResponses.ps1 b/examples/Click/eg005GetClickwrapResponses.ps1 index 5ff769d..80559f2 100644 --- a/examples/Click/eg005GetClickwrapResponses.ps1 +++ b/examples/Click/eg005GetClickwrapResponses.ps1 @@ -12,14 +12,19 @@ if (Test-Path .\config\CLICKWRAP_ID) { } # Step 2. Construct your API headers +#ds-snippet-start:Click5Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Click5Step2 # Step 3. Call the Click API # a) Make a GET call to the users endpoint to retrieve responses (acceptance) of a specific clickwrap for an account # b) Display the returned JSON structure of the responses +#ds-snippet-start:Click5Step3 $uri = "https://demo.docusign.net/clickapi/v1/accounts/$APIAccountId/clickwraps/$ClickWrapId/users" $result = Invoke-WebRequest -headers $headers -Uri $uri -UseBasicParsing -Method GET -$result.Content \ No newline at end of file +Write-Output "Response: " +$result.Content +#ds-snippet-end:Click5Step3 \ No newline at end of file diff --git a/examples/Click/eg006EmbedClickwrap.ps1 b/examples/Click/eg006EmbedClickwrap.ps1 new file mode 100644 index 0000000..5a30aab --- /dev/null +++ b/examples/Click/eg006EmbedClickwrap.ps1 @@ -0,0 +1,87 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Check that we have a clickwrap ID +if (Test-Path .\config\CLICKWRAP_ID) { + $ClickWrapId = Get-Content .\config\CLICKWRAP_ID +} +else { + Write-Output "Clickwrap ID required. Please run code example 1 - Create Clickwrap" + exit 1 +} + +# Construct your API headers +#ds-snippet-start:Click6Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Click6Step2 + +$client_user_id = Read-Host "Please input a Client User Id (your own unique identifier) for the clickwrap" +$full_name = Read-Host "Please input a full name" +$email_address = Read-Host "Please input an email address" +$company_name = Read-Host "Please input a company name" +$title = Read-Host "Please input a job title" + +# Construct the request body +#ds-snippet-start:Click6Step3 +$requestData = New-TemporaryFile +$response = New-TemporaryFile + +@{ + clientUserId = $client_user_id; + documentData = + @{ + fullName = $full_name; + email = $email_address; + company = $company_name; + title = $title; + date = $((Get-Date).ToString("yyyy-MM-ddThh:mm:ssK")) + }; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:Click6Step3 + +# Call the Click API +# a) Make a GET call to the users endpoint to retrieve responses (acceptance) of a specific clickwrap for an account +# b) Display the returned JSON structure of the responses +try { + + + #ds-snippet-start:Click6Step4 + Invoke-RestMethod ` + -Uri "https://demo.docusign.net/clickapi/v1/accounts/$APIAccountId/clickwraps/$ClickWrapId/agreements" ` + -UseBasicParsing ` + -Method "POST" ` + -Headers @{ + "Authorization" = "Bearer $accessToken" + "Content-Type" = "application/json" + "Accept" = "application/json" +} ` +-InFile (Resolve-Path $requestData).Path ` +-OutFile $response +#ds-snippet-end:Click6Step4 + +Write-Output "Response: $(Get-Content -Raw $response)" + + +# cleanup +Remove-Item $requestData +Remove-Item $response +Write-Output "Done." + +} +catch { + $errorMessage = $_.ErrorDetails.Message + Write-Host "" + if($errorMessage){ + if ( $errorMessage.Contains("There are no active versions for clickwrapId") ) { + Write-Host "Clickwrap must be activated. Please run code example 2 - Activate Clickwrap" + } + elseif ( $errorMessage.Contains("Unable to find Clickwrap with id") ) { + Write-Host "Clickwrap ID required. Please run code example 1 - Create Clickwrap" + } + +} +} diff --git a/examples/ConnectedFields/eg001SetConnectedFields.ps1 b/examples/ConnectedFields/eg001SetConnectedFields.ps1 new file mode 100644 index 0000000..e55c31c --- /dev/null +++ b/examples/ConnectedFields/eg001SetConnectedFields.ps1 @@ -0,0 +1,301 @@ +$apiUri1 = "https://api-d.docusign.com" +$apiUri2 = "https://demo.docusign.net/restapi" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" + +# Get required variables from .\config\settings.json file +$variables = Get-Content $configPath -Raw | ConvertFrom-Json + +# 1. Obtain your OAuth token +$accessToken = Get-Content $tokenPath + +# Obtain your accountId from demo.docusign.net -- the account id is shown in +# the drop down on the upper right corner of the screen by your picture or +# the default picture. +$accountID = Get-Content $accountIdPath + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$docBase64 = New-TemporaryFile + +#ds-snippet-start:ConnectedFields1Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:ConnectedFields1Step2 + +#ds-snippet-start:ConnectedFields1Step3 +Invoke-RestMethod ` + -Uri "${apiUri1}/v1/accounts/${accountId}/connected-fields/tab-groups" ` + -Method 'GET' ` + -Headers $headers ` + -OutFile $response +#ds-snippet-end:ConnectedFields1Step3 + +#ds-snippet-start:ConnectedFields1Step4 +function Extract-VerifyInfo { + param ( + [string]$responseFile + ) + + # Read the response file content + $content = Get-Content $responseFile -Raw + + # Extract JSON + $jsonStart = $content.IndexOf('[') + if ($jsonStart -ge 0) { + $json = $content.Substring($jsonStart) | ConvertFrom-Json + } else { + Write-Error "No JSON array found in the response." + return + } + + # Filter the JSON to keep only verification extension apps + $filtered = $json | Where-Object { + $_.tabs -and ($_.tabs | Where-Object { + ($_.extensionData.actionContract -like "*Verify*") -or + ($_.PSObject.Properties['tabLabel'] -and $_.tabLabel -like "*connecteddata*") + }) + } + + return $filtered | ConvertTo-Json -Depth 10 +} + +function Prompt-UserChoice { + param ( + [string]$jsonData + ) + + if (-not $jsonData -or $jsonData -eq "[]") { + Write-Host "No data verification were found in the account. Please install a data verification app." + Write-Host "You can install a phone number verification extension app by copying the following link to your browser: " + Write-Host "https://apps.docusign.com/app-center/app/d16f398f-8b9a-4f94-b37c-af6f9c910c04" + exit 1 + } + + $data = $jsonData | ConvertFrom-Json + + # Extract unique app IDs and application names + $uniqueApps = @{} + foreach ($item in $data) { + if ($item.appId -and $item.tabs[0].extensionData.applicationName) { + $uniqueApps[$item.appId] = $item.tabs[0].extensionData.applicationName + } + } + + # If no unique apps are found + if ($uniqueApps.Count -eq 0) { + Write-Host "No valid apps found in the JSON data." + exit 1 + } + + # Display available apps + Write-Host "Please select an app by entering a number:" + $appList = $uniqueApps.Keys | Sort-Object + for ($i = 0; $i -lt $appList.Count; $i++) { + Write-Host "$($i + 1). $($uniqueApps[$appList[$i]])" + } + + # Get user choice + $choice = Read-Host "Enter choice (1-$($appList.Count))" + if ($choice -match "^\d+$" -and [int]$choice -ge 1 -and [int]$choice -le $appList.Count) { + $chosenAppId = $appList[[int]$choice - 1] + + # Filter JSON data for the selected app ID + $selectedData = $data | Where-Object { $_.appId -eq $chosenAppId } + return $selectedData + } else { + Write-Host "Invalid choice. Exiting." + exit 1 + } +} + +function Parse-VerificationData { + param ( + $selectedAppId, + $tab + ) + + $connectionKeyData = '' + $connectionValueData = '' + if ($tab.extensionData.connectionInstances) { + $connectionKeyData = $tab.extensionData.connectionInstances[0].connectionKey + $connectionValueData = $tab.extensionData.connectionInstances[0].connectionValue + } + + # Extract required fields from the first element + $extractedData = @{ + appId = $selectedAppId + extensionGroupId = $tab.extensionData.extensionGroupId + publisherName = $tab.extensionData.publisherName + applicationName = $tab.extensionData.applicationName + actionName = $tab.extensionData.actionName + actionInputKey = $tab.extensionData.actionInputKey + actionContract = $tab.extensionData.actionContract + extensionName = $tab.extensionData.extensionName + extensionContract = $tab.extensionData.extensionContract + requiredForExtension = $tab.extensionData.requiredForExtension + tabLabel = $tab.tabLabel + connectionKey = $connectionKeyData + connectionValue = $connectionValueData + } + + return $extractedData +} + +$filteredData = Extract-VerifyInfo -responseFile $response + +$selectedApp = Prompt-UserChoice -jsonData $filteredData +#ds-snippet-end:ConnectedFields1Step4 + +Write-Output "Sending the envelope request to Docusign..." + +# Fetch doc and encode +$docPath = ".\demo_documents\World_Wide_Corp_Lorem.pdf" +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path $docPath))) > $docBase64 + +# Construct the request body +#ds-snippet-start:ConnectedFields1Step5 +function Get-Extension-Data { + param ( + $verificationData + ) + + return @{ + extensionGroupId = $verificationData.extensionGroupId; + publisherName = $verificationData.publisherName; + applicationId = $verificationData.appId; + applicationName = $verificationData.applicationName; + actionName = $verificationData.actionName; + actionContract = $verificationData.actionContract; + extensionName = $verificationData.extensionName; + extensionContract = $verificationData.extensionContract; + requiredForExtension = $verificationData.requiredForExtension; + actionInputKey = $verificationData.actionInputKey; + extensionPolicy = "MustVerifyToSign"; + connectionInstances = @( + @{ + connectionKey = $verificationData.connectionKey; + connectionValue = $verificationData.connectionValue; + }; + ); + }; +} + +function Make-Text-Tab { + param ( + $verificationData, + $extensionData, + $textTabCount + ) + + return @{ + requireInitialOnSharedChange = "false"; + requireAll = "false"; + name = $verificationData.applicationName; + required = "true"; + locked = "false"; + disableAutoSize = "false"; + maxLength = "4000"; + tabLabel = $verificationData.tabLabel; + font = "lucidaconsole"; + fontColor = "black"; + fontSize = "size9"; + documentId = "1"; + recipientId = "1"; + pageNumber = "1"; + xPosition = [string](70 + 100 * [math]::Floor($textTabCount / 10)); + yPosition = [string](560 + 20 * ($textTabCount % 10)); + width = "84"; + height = "22"; + templateRequired = "false"; + tabType = "text"; + tooltip = $verificationData.actionInputKey; + extensionData = $extensionData + }; +} + +function Make-Text-Tab-List { + param ( + $app + ) + + $text_tabs = @() + foreach ($tab in $app.tabs | Where-Object { $_.tabLabel -notlike '*SuggestionInput*' }) { + $verificationData = Parse-VerificationData -selectedAppId $app.appId -tab $tab + $extensionData = Get-Extension-Data -verificationData $verificationData + + $text_tab = Make-Text-Tab -verificationData $verificationData -extensionData $extensionData -textTabCount $text_tabs.Count + $text_tabs += $text_tab + } + + return $text_tabs +} + +$textTabs = Make-Text-Tab-List -app $selectedApp + +@{ + emailSubject = "Please sign this document"; + documents = @( + @{ + documentBase64 = "$(Get-Content $docBase64)"; + name = "Lorem Ipsum"; + fileExtension = "pdf"; + documentId = "1"; + }; + ); + status = "sent"; + recipients = @{ + signers = @( + @{ + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "1"; + routingOrder = "1"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + textTabs = @($textTabs); + }; + }; + ); + }; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:ConnectedFields1Step5 + +#ds-snippet-start:ConnectedFields1Step6 +Invoke-RestMethod ` + -Uri "${apiUri2}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:ConnectedFields1Step6 + +Write-Output "Response: $(Get-Content -Raw $response)" + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId + +# Save the envelope id for use by other scripts +Write-Output "EnvelopeId: $envelopeId" +Write-Output $envelopeId > .\config\ENVELOPE_ID + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $docBase64 + +Write-Output "Done. When signing the envelope, ensure the connection to your data verification extension app is active." diff --git a/examples/ID_Evidence/retrieveEvents.ps1 b/examples/ID_Evidence/retrieveEvents.ps1 new file mode 100644 index 0000000..4b6517a --- /dev/null +++ b/examples/ID_Evidence/retrieveEvents.ps1 @@ -0,0 +1,127 @@ +# Required values: $oAuthAccessToken, $APIaccountId, $envelopeId +# Returns: $recipientIdGuid, $resourceToken, $copy_of_id_front + +$oAuthAccessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Check that we have an IDV Envelope ID +if (-not (Test-Path .\config\IDV_ENVELOPE_ID)) { + Write-Output "An IDV Envelope ID is needed. Run eSignature example 23 'Signing via Email with IDV Authentication' and complete IDV before running this code example." + exit 0 +} + +$envelopeId = Get-Content .\config\IDV_ENVELOPE_ID + +# Construct your API headers +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization","Bearer ${oAuthAccessToken}") +$headers.add("Accept","application/json, text/plain, */*") +$headers.add("Content-Type","application/json;charset=UTF-8") + +# Retrieve recipient data +#ds-snippet-start:IDEvidence1Step2 +$uri = "https://demo.docusign.net/restapi/v2.1/accounts/${APIaccountId}/envelopes/${envelopeId}/recipients" + +write-host "Retrieving recipient data" + +try{ + write-host "Response:" + $result = Invoke-RestMethod -uri $uri -headers $headers -method GET + $result.content + #Obtain the recipient ID GUID from the API response + $recipientIdGuid = $result.signers.recipientIdGuid + } +#ds-snippet-end +catch{ + $int = 0 + foreach($header in $_.Exception.Response.Headers){ + #On error, display the error, the line that triggered the error, and the TraceToken + if($header -eq "X-DocuSign-TraceToken"){ write-host "TraceToken : " $_.Exception.Response.Headers[$int]} + $int++ + } + write-host "Error : "$_.ErrorDetails.Message + write-host "Command : "$_.InvocationInfo.Line +} + +write-host "recipientIdGuid: " $recipientIdGuid +# Save the Recipient ID Guid for use by other scripts +Write-Output $recipientIdGuid > .\config\RECIPIENT_ID_GUID + + +# Construct your API headers +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization","Bearer ${oAuthAccessToken}") +$headers.add("Accept","application/json, text/plain, */*") +$headers.add("Content-Type","application/json;charset=UTF-8") + +# Obtain identity proof token (resource token) +#ds-snippet-start:IDEvidence1Step3 +$uri = "https://demo.docusign.net/restapi/v2.1/accounts/${APIaccountId}/envelopes/${envelopeId}/recipients/${recipientIdGuid}/identity_proof_token" + +write-host "Attempting to retrieve your identity proof token" + +try{ + write-host "Response:" + $result = Invoke-RestMethod -uri $uri -headers $headers -method POST + $result.content + #Obtain the resourceToken from the API response + $resourceToken = $result.resourceToken + } +#ds-snippet-end +catch{ + $int = 0 + foreach($header in $_.Exception.Response.Headers){ + #On error, display the error, the line that triggered the error, and the TraceToken + if($header -eq "X-DocuSign-TraceToken"){ write-host "TraceToken : " $_.Exception.Response.Headers[$int]} + $int++ + } + write-host "Error : "$_.ErrorDetails.Message + write-host "Command : "$_.InvocationInfo.Line +} + +write-host "resourceToken: " $resourceToken +# Save the Resource Token for use by other scripts +Write-Output $resourceToken > .\config\RESOURCE_TOKEN.txt + +#ds-snippet-start:IDEvidence1Step4 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization","Bearer $resourceToken") +$headers.add("Accept","application/json, text/plain, */*") +$headers.add("Content-Type","application/json;charset=UTF-8") +#ds-snippet-end + +# Retrieve recipient data +#ds-snippet-start:IDEvidence1Step5 +$uri = "https://proof-d.docusign.net/api/v1/events/person/$recipientIdGuid.json" + +write-host "Retrieving recipient data" + +try{ + write-host "Response:" + $result = Invoke-RestMethod -uri $uri -headers $headers -UseBasicParsing -method GET + #Obtain the Event List from the API response + $EventList = $result.events | ConvertTo-Json + + write-host $EventList + } +#ds-snippet-end + +catch{ + $int = 0 + foreach($header in $_.Exception.Response.Headers){ + #On error, display the error, the line that triggered the error, and the TraceToken + if($header -eq "X-DocuSign-TraceToken"){ write-host "TraceToken : " $_.Exception.Response.Headers[$int]} + $int++ + } + write-host "Error : "$_.ErrorDetails.Message + write-host "Command : "$_.InvocationInfo.Line +} + +$copy_of_id_front = $result.events.data.copy_of_id_front | ConvertTo-Json +write-host "copy_of_id_front:"$copy_of_id_front +# Save the copy_of_id_front URL for use by other scripts +Write-Output $copy_of_id_front > .\config\COPY_OF_ID_FRONT_URL.txt + +# cleanup +Remove-Item $result +Write-Output "Done." diff --git a/examples/ID_Evidence/retrieveMedia.ps1 b/examples/ID_Evidence/retrieveMedia.ps1 new file mode 100644 index 0000000..49cb86f --- /dev/null +++ b/examples/ID_Evidence/retrieveMedia.ps1 @@ -0,0 +1,166 @@ +# Required values: $oAuthAccessToken, $APIaccountId, $envelopeId +# Returns: $recipientIdGuid, $resourceToken, $copy_of_id_front + +$oAuthAccessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Check that we have an IDV Envelope ID +if (-not (Test-Path .\config\IDV_ENVELOPE_ID)) { + Write-Output "An IDV Envelope ID is needed. Run eSignature example 23 'Signing via Email with IDV Authentication' and complete IDV before running this code example." + exit 0 +} + +# Check that we have Copy of ID front URL and Resource Token in config file +if ((-not (Test-Path .\config\COPY_OF_ID_FRONT_URL.txt)) -or (-not (Test-Path .\config\RESOURCE_TOKEN.txt))) { + Write-Output "Copy of ID Front URL and Resource Token are needed. Run ID Evidence example 1 'Retrieve events' before running this code example." + exit 0 +} + +$envelopeId = Get-Content .\config\IDV_ENVELOPE_ID + +# Construct your API headers +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization","Bearer ${oAuthAccessToken}") +$headers.add("Accept","application/json, text/plain, */*") +$headers.add("Content-Type","application/json;charset=UTF-8") + +# Retrieve recipient data +#ds-snippet-start:IDEvidence2Step2 +$uri = "https://demo.docusign.net/restapi/v2.1/accounts/${APIaccountId}/envelopes/${envelopeId}/recipients" + +write-host "Retrieving recipient data" + +try{ + write-host "Response:" + $result = Invoke-RestMethod -uri $uri -headers $headers -method GET + $result.content + #Obtain the recipient ID GUID from the API response + $recipientIdGuid = $result.signers.recipientIdGuid + } +#ds-snippet-end +catch{ + $int = 0 + foreach($header in $_.Exception.Response.Headers){ + #On error, display the error, the line that triggered the error, and the TraceToken + if($header -eq "X-DocuSign-TraceToken"){ write-host "TraceToken : " $_.Exception.Response.Headers[$int]} + $int++ + } + write-host "Error : "$_.ErrorDetails.Message + write-host "Command : "$_.InvocationInfo.Line +} + +write-host "recipientIdGuid: " $recipientIdGuid +# Save the Recipient ID Guid for use by other scripts +Write-Output $recipientIdGuid > .\config\RECIPIENT_ID_GUID + +# Construct your API headers +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization","Bearer ${oAuthAccessToken}") +$headers.add("Accept","application/json, text/plain, */*") +$headers.add("Content-Type","application/json;charset=UTF-8") + +# Obtain identity proof token (resource token) +#ds-snippet-start:IDEvidence2Step3 +$uri = "https://demo.docusign.net/restapi/v2.1/accounts/${APIaccountId}/envelopes/${envelopeId}/recipients/${recipientIdGuid}/identity_proof_token" + +write-host "Attempting to retrieve your identity proof token" + +try{ + write-host "Response:" + $result = Invoke-RestMethod -uri $uri -headers $headers -method POST + $result.content + #Obtain the resourceToken from the API response + $resourceToken = $result.resourceToken + } +#ds-snippet-end +catch{ + $int = 0 + foreach($header in $_.Exception.Response.Headers){ + #On error, display the error, the line that triggered the error, and the TraceToken + if($header -eq "X-DocuSign-TraceToken"){ write-host "TraceToken : " $_.Exception.Response.Headers[$int]} + $int++ + } + write-host "Error : "$_.ErrorDetails.Message + write-host "Command : "$_.InvocationInfo.Line +} + +write-host "resourceToken: " $resourceToken + +# Save the Resource Token for use by other scripts +Write-Output $resourceToken > .\config\RESOURCE_TOKEN.txt + +#ds-snippet-start:IDEvidence2Step4 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization","Bearer $resourceToken") +$headers.add("Accept","application/json, text/plain, */*") +$headers.add("Content-Type","application/json;charset=UTF-8") +#ds-snippet-end + +# Retrieve recipient data +#ds-snippet-start:IDEvidence2Step5 +$uri = "https://proof-d.docusign.net/api/v1/events/person/$recipientIdGuid.json" + +write-host "Retrieving recipient data" + +try{ + write-host "Response:" + $result = Invoke-RestMethod -uri $uri -headers $headers -UseBasicParsing -method GET + #Obtain the Event List from the API response + $EventList = $result.events | ConvertTo-Json + write-host $EventList +#ds-snippet-end +}catch{ + $int = 0 + foreach($header in $_.Exception.Response.Headers){ + #On error, display the error, the line that triggered the error, and the TraceToken + if($header -eq "X-DocuSign-TraceToken"){ write-host "TraceToken : " $_.Exception.Response.Headers[$int]} + $int++ + } + write-host "Error : "$_.ErrorDetails.Message + write-host "Command : "$_.InvocationInfo.Line +} + +$copy_of_id_front = $result.events.data.copy_of_id_front | ConvertTo-Json +write-host "copy_of_id_front:"$copy_of_id_front +# Save the copy_of_id_front URL for use by other scripts +Write-Output $copy_of_id_front > .\config\COPY_OF_ID_FRONT_URL.txt + + +# Required values: $resourceToken, $copy_of_id_front +$copy_of_id_front = Get-Content .\config\COPY_OF_ID_FRONT_URL.txt +$recipientIdGuid = Get-Content .\config\RECIPIENT_ID_GUID +$resourceToken = Get-Content .\config\RESOURCE_TOKEN.txt + + +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization","Bearer $resourceToken") +$headers.add("Accept", "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*") +$headers.add("Content-Type","image/jpg") + +# return a base-64 image of the front of the photo ID +#ds-snippet-start:IDEvidence2Step6 +$uri = $copy_of_id_front -replace '"' + +write-host "Retrieving recipient data" + +try{ + #Obtain the base-64 image + $result = Invoke-RestMethod -uri $uri -headers $headers -method GET + $result | Out-File -FilePath .\id_front_base64_image.txt + write-host "Response: Saved to .\id_front_base64_image.txt" + } +#ds-snippet-end +catch{ + $int = 0 + foreach($header in $_.Exception.Response.Headers){ + #On error, display the error, the line that triggered the error, and the TraceToken + if($header -eq "X-DocuSign-TraceToken"){ write-host "TraceToken : " $_.Exception.Response.Headers[$int]} + $int++ + } + write-host "Error : "$_.ErrorDetails.Message + write-host "Command : "$_.InvocationInfo.Line +} + +# cleanup +Remove-Item $result +Write-Output "Done." diff --git a/examples/Maestro/createWorkflowUtils.ps1 b/examples/Maestro/createWorkflowUtils.ps1 new file mode 100644 index 0000000..4b18b05 --- /dev/null +++ b/examples/Maestro/createWorkflowUtils.ps1 @@ -0,0 +1,594 @@ +. "utils/invokeScript.ps1" + +# Check that we have a template id +if (-not (Test-Path .\config\TEMPLATE_ID)) { + Write-Output "Creating template" + Invoke-Script -Command "`".\examples\eSignature\eg008CreateTemplate.ps1`"" +} + +$templateId = Get-Content "config/TEMPLATE_ID" + +Write-Host "Creating a new workflow" + +$signerId = [guid]::NewGuid().ToString().ToLower() +$ccId = [guid]::NewGuid().ToString().ToLower() +$triggerId = "wfTrigger" + +$accessToken = Get-Content "config/ds_access_token.txt" +$accountId = Get-Content "config/API_ACCOUNT_ID" + +$base_path = "https://demo.services.docusign.net/aow-manage/v1.0" + +$headers = @{ + 'Authorization' = "Bearer $accessToken" + 'Accept' = 'application/json' + 'Content-Type' = 'application/json' +} + +$body = @" +{ + "workflowDefinition": { + "workflowName": "Example workflow - send invite to signer", + "workflowDescription": "", + "accountId": "$accountId", + "documentVersion": "1.0.0", + "schemaVersion": "1.0.0", + "participants": { + "$signerId": { + "participantRole": "Signer" + }, + "$ccId": { + "participantRole": "CC" + } + }, + "trigger": { + "name": "Get_URL", + "type": "Http", + "httpType": "Get", + "id": "$triggerId", + "input": { + "metadata": { + "customAttributes": {} + }, + "payload": { + "dacId_$triggerId": { + "source": "step", + "propertyName": "dacId", + "stepId": "$triggerId" + }, + "id_$triggerId": { + "source": "step", + "propertyName": "id", + "stepId": "$triggerId" + }, + "signerName_$triggerId": { + "source": "step", + "propertyName": "signerName", + "stepId": "$triggerId" + }, + "signerEmail_$triggerId": { + "source": "step", + "propertyName": "signerEmail", + "stepId": "$triggerId" + }, + "ccName_$triggerId": { + "source": "step", + "propertyName": "ccName", + "stepId": "$triggerId" + }, + "ccEmail_$triggerId": { + "source": "step", + "propertyName": "ccEmail", + "stepId": "$triggerId" + } + }, + "participants": {} + }, + "output": { + "dacId_$triggerId": { + "source": "step", + "propertyName": "dacId", + "stepId": "$triggerId" + } + }, + "type": "API" + }, + "variables": { + "dacId_$triggerId": { + "source": "step", + "propertyName": "dacId", + "stepId": "$triggerId" + }, + "id_$triggerId": { + "source": "step", + "propertyName": "id", + "stepId": "$triggerId" + }, + "signerName_$triggerId": { + "source": "step", + "propertyName": "signerName", + "stepId": "$triggerId" + }, + "signerEmail_$triggerId": { + "source": "step", + "propertyName": "signerEmail", + "stepId": "$triggerId" + }, + "ccName_$triggerId": { + "source": "step", + "propertyName": "ccName", + "stepId": "$triggerId" + }, + "ccEmail_$triggerId": { + "source": "step", + "propertyName": "ccEmail", + "stepId": "$triggerId" + }, + "envelopeId_step2": { + "source": "step", + "propertyName": "envelopeId", + "stepId": "step2", + "type": "String" + }, + "combinedDocumentsBase64_step2": { + "source": "step", + "propertyName": "combinedDocumentsBase64", + "stepId": "step2", + "type": "File" + }, + "fields.signer.text.value_step2": { + "source": "step", + "propertyName": "fields.signer.text.value", + "stepId": "step2", + "type": "String" + } + }, + "steps": [ + { + "id": "step2", + "name": "Get Signatures", + "moduleName": "ESign", + "configurationProgress": "Completed", + "type": "DS-Sign", + "config": { + "participantId": "$signerId" + }, + "input": { + "isEmbeddedSign": true, + "documents": [ + { + "type": "FromDSTemplate", + "eSignTemplateId": "$templateId" + } + ], + "emailSubject": "Please sign this document", + "emailBlurb": "", + "recipients": { + "signers": [ + { + "defaultRecipient": "false", + "tabs": { + "signHereTabs": [ + { + "stampType": "signature", + "name": "SignHere", + "tabLabel": "Sign Here", + "scaleValue": "1", + "optional": "false", + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "191", + "yPosition": "148", + "tabId": "1", + "tabType": "signhere" + } + ], + "textTabs": [ + { + "requireAll": "false", + "value": "", + "required": "false", + "locked": "false", + "concealValueOnDocument": "false", + "disableAutoSize": "false", + "tabLabel": "text", + "font": "helvetica", + "fontSize": "size14", + "localePolicy": {}, + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "153", + "yPosition": "230", + "width": "84", + "height": "23", + "tabId": "2", + "tabType": "text" + } + ], + "checkboxTabs": [ + { + "name": "", + "tabLabel": "ckAuthorization", + "selected": "false", + "selectedOriginal": "false", + "requireInitialOnSharedChange": "false", + "required": "true", + "locked": "false", + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "75", + "yPosition": "417", + "width": "0", + "height": "0", + "tabId": "3", + "tabType": "checkbox" + }, + { + "name": "", + "tabLabel": "ckAuthentication", + "selected": "false", + "selectedOriginal": "false", + "requireInitialOnSharedChange": "false", + "required": "true", + "locked": "false", + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "75", + "yPosition": "447", + "width": "0", + "height": "0", + "tabId": "4", + "tabType": "checkbox" + }, + { + "name": "", + "tabLabel": "ckAgreement", + "selected": "false", + "selectedOriginal": "false", + "requireInitialOnSharedChange": "false", + "required": "true", + "locked": "false", + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "75", + "yPosition": "478", + "width": "0", + "height": "0", + "tabId": "5", + "tabType": "checkbox" + }, + { + "name": "", + "tabLabel": "ckAcknowledgement", + "selected": "false", + "selectedOriginal": "false", + "requireInitialOnSharedChange": "false", + "required": "true", + "locked": "false", + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "75", + "yPosition": "508", + "width": "0", + "height": "0", + "tabId": "6", + "tabType": "checkbox" + } + ], + "radioGroupTabs": [ + { + "documentId": "1", + "recipientId": "1", + "groupName": "radio1", + "radios": [ + { + "pageNumber": "1", + "xPosition": "142", + "yPosition": "384", + "value": "white", + "selected": "false", + "tabId": "7", + "required": "false", + "locked": "false", + "bold": "false", + "italic": "false", + "underline": "false", + "fontColor": "black", + "fontSize": "size7" + }, + { + "pageNumber": "1", + "xPosition": "74", + "yPosition": "384", + "value": "red", + "selected": "false", + "tabId": "8", + "required": "false", + "locked": "false", + "bold": "false", + "italic": "false", + "underline": "false", + "fontColor": "black", + "fontSize": "size7" + }, + { + "pageNumber": "1", + "xPosition": "220", + "yPosition": "384", + "value": "blue", + "selected": "false", + "tabId": "9", + "required": "false", + "locked": "false", + "bold": "false", + "italic": "false", + "underline": "false", + "fontColor": "black", + "fontSize": "size7" + } + ], + "shared": "false", + "requireInitialOnSharedChange": "false", + "requireAll": "false", + "tabType": "radiogroup", + "value": "", + "originalValue": "" + } + ], + "listTabs": [ + { + "listItems": [ + { + "text": "Red", + "value": "red", + "selected": "false" + }, + { + "text": "Orange", + "value": "orange", + "selected": "false" + }, + { + "text": "Yellow", + "value": "yellow", + "selected": "false" + }, + { + "text": "Green", + "value": "green", + "selected": "false" + }, + { + "text": "Blue", + "value": "blue", + "selected": "false" + }, + { + "text": "Indigo", + "value": "indigo", + "selected": "false" + }, + { + "text": "Violet", + "value": "violet", + "selected": "false" + } + ], + "value": "", + "originalValue": "", + "required": "false", + "locked": "false", + "requireAll": "false", + "tabLabel": "list", + "font": "helvetica", + "fontSize": "size14", + "localePolicy": {}, + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "142", + "yPosition": "291", + "width": "78", + "height": "0", + "tabId": "10", + "tabType": "list" + } + ], + "numericalTabs": [ + { + "validationType": "currency", + "value": "", + "required": "false", + "locked": "false", + "concealValueOnDocument": "false", + "disableAutoSize": "false", + "tabLabel": "numericalCurrency", + "font": "helvetica", + "fontSize": "size14", + "localePolicy": { + "cultureName": "en-US", + "currencyPositiveFormat": "csym_1_comma_234_comma_567_period_89", + "currencyNegativeFormat": "opar_csym_1_comma_234_comma_567_period_89_cpar", + "currencyCode": "usd" + }, + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": "163", + "yPosition": "260", + "width": "84", + "height": "0", + "tabId": "11", + "tabType": "numerical" + } + ] + }, + "signInEachLocation": "false", + "agentCanEditEmail": "false", + "agentCanEditName": "false", + "requireUploadSignature": "false", + "name": { + "source": "step", + "propertyName": "signerName", + "stepId": "$triggerId" + }, + "email": { + "source": "step", + "propertyName": "signerEmail", + "stepId": "$triggerId" + }, + "recipientId": "1", + "recipientIdGuid": "00000000-0000-0000-0000-000000000000", + "accessCode": "", + "requireIdLookup": "false", + "routingOrder": "1", + "note": "", + "roleName": "signer", + "completedCount": "0", + "deliveryMethod": "email", + "templateLocked": "false", + "templateRequired": "false", + "inheritEmailNotificationConfiguration": "false", + "recipientType": "signer" + } + ], + "carbonCopies": [ + { + "agentCanEditEmail": "false", + "agentCanEditName": "false", + "name": { + "source": "step", + "propertyName": "ccName", + "stepId": "$triggerId" + }, + "email": { + "source": "step", + "propertyName": "ccEmail", + "stepId": "$triggerId" + }, + "recipientId": "2", + "recipientIdGuid": "00000000-0000-0000-0000-000000000000", + "accessCode": "", + "requireIdLookup": "false", + "routingOrder": "2", + "note": "", + "roleName": "cc", + "completedCount": "0", + "deliveryMethod": "email", + "templateLocked": "false", + "templateRequired": "false", + "inheritEmailNotificationConfiguration": "false", + "recipientType": "carboncopy" + } + ], + "certifiedDeliveries": [] + } + }, + "output": { + "envelopeId_step2": { + "source": "step", + "propertyName": "envelopeId", + "stepId": "step2", + "type": "String" + }, + "combinedDocumentsBase64_step2": { + "source": "step", + "propertyName": "combinedDocumentsBase64", + "stepId": "step2", + "type": "File" + }, + "fields.signer.text.value_step2": { + "source": "step", + "propertyName": "fields.signer.text.value", + "stepId": "step2", + "type": "String" + } + } + }, + { + "id": "step3", + "name": "Show a Confirmation Screen", + "moduleName": "ShowConfirmationScreen", + "configurationProgress": "Completed", + "type": "DS-ShowScreenStep", + "config": { + "participantId": "$signerId" + }, + "input": { + "httpType": "Post", + "payload": { + "participantId": "$signerId", + "confirmationMessage": { + "title": "Tasks complete", + "description": "You have completed all your workflow tasks." + } + } + }, + "output": {}, + "triggerType": "API" + } + ] + } +} +"@ + +# Create temporary file for response +$response = New-TemporaryFile + +# Send request to create new workflow +try { + $workflowDefinition = Invoke-RestMethod -Uri "$base_path/management/accounts/$accountId/workflowDefinitions" -Method POST -Headers $headers -body $body + + $workflow_id = $workflowDefinition.workflowDefinitionId + Write-Host "Workflow ID: $workflow_id" +} +catch { + Write-Host "" + Write-Host "Unable to create a new workflow" + Write-Host "" + Get-Content $workflowDefinition.FullName + Exit 0 +} + +# Define redirect_url +$redirect_url = "http://localhost:8080" + +if (-not (Test-Path -Path "config/WORKFLOW_ID")) { + New-Item -ItemType File -Path "config/WORKFLOW_ID" +} + +# Publish workflow +$published = $false +while (-not $published) { + try { + Invoke-RestMethod -Uri "$base_path/management/accounts/$accountId/workflowDefinitions/$workflow_id/publish?isPreRunCheck=true" -Method POST -Headers $Headers + + $published = $true + $workflow_id | Out-File -FilePath "config/WORKFLOW_ID" + Write-Host "Successfully created and published workflow $workflow_id, ID saved to config/WORKFLOW_ID" + } + catch { + $message = $($_ | ConvertFrom-Json).message + Write-Host $message + if ($message -eq "Consent required") { + $consentUrl = $($_ | ConvertFrom-Json).consentUrl + Write-Host "" + Write-Host "Please grant consent at the following URL to publish this workflow: $consentUrl&host=$redirect_url" + + # Wait for user to press Enter + Read-Host "Press Enter to continue" + } else { + Write-Host $message + Exit 0 + } + } +} + +# Remove the temporary files +Remove-Item $response \ No newline at end of file diff --git a/examples/Maestro/eg001TriggerWorkflow.ps1 b/examples/Maestro/eg001TriggerWorkflow.ps1 new file mode 100644 index 0000000..bb479ad --- /dev/null +++ b/examples/Maestro/eg001TriggerWorkflow.ps1 @@ -0,0 +1,107 @@ +. "utils/invokeScript.ps1" + +# Trigger a workflow +if (-not (Test-Path .\config\WORKFLOW_ID)) { + # create workflow + Invoke-Script -Command "`".\examples\Maestro\createWorkflowUtils.ps1`"" +} + +$workflowId = Get-Content .\config\WORKFLOW_ID + +# check that create workflow script ran successfully +if (Test-Path .\config\WORKFLOW_ID) { + $workflowId = Get-Content .\config\WORKFLOW_ID +} else { + Write-Output "Please create a workflow before running this example" + exit 1 +} + +$base_path = "https://demo.services.docusign.net/v1" + +# Step 1: Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# temp files: +$response = New-TemporaryFile + +# Construct your API headers +#ds-snippet-start:Maestro1Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Maestro1Step2 + +Write-Output "Attempting to retrieve the workflow definition..." + +#ds-snippet-start:Maestro1Step3 +Invoke-RestMethod ` + -Uri "${base_path}/accounts/${accountId}/workflows/${workflowId}/trigger-requirements" ` + -Method 'GET' ` + -Headers $headers ` + -OutFile $response + +$jsonContent = Get-Content -Path $response -Raw | ConvertFrom-Json +$triggerUrl = $jsonContent.trigger_http_config.url +$triggerUrl = $triggerUrl -replace "\\u0026", "&" +#ds-snippet-end:Maestro1Step3 + +$instance_name = Read-Host "Please input a name for the workflow instance" +$signerName = Read-Host "Please input the full name for the signer participant" +$signerEmail = Read-Host "Please input an email for the signer participant" +$ccName = Read-Host "Please input the full name for the cc participant" +$ccEmail = Read-Host "Please input an email for the cc participant" + +#ds-snippet-start:Maestro1Step4 +$body = @" +{ + "instance_name": "$instance_name", + "trigger_inputs": { + "signerEmail": "$signerEmail", + "signerName": "$signerName", + "ccEmail": "$ccEmail", + "ccName": "$ccName" + } +} +"@ +#ds-snippet-end:Maestro1Step4 + +if (-not ([string]::IsNullOrEmpty($triggerUrl))) { + #ds-snippet-start:Maestro1Step5 + $triggerResult = Invoke-WebRequest -uri $triggerUrl -headers $headers -body $body -method POST -UseBasicParsing + #ds-snippet-end:Maestro1Step5 + Write-Host $triggerResult + Write-Host "" + + $workflowInstanceId = $($triggerResult | ConvertFrom-Json).instance_id + $workflowInstanceId | Out-File -FilePath "config/INSTANCE_ID" -Encoding utf8 -Force + Write-Host "Successfully created and published workflow $workflowInstanceId, ID saved to config/INSTANCE_ID" + + + $instanceUrl = $($triggerResult | ConvertFrom-Json).instance_url + # Decode escaped characters + $instanceUrl = $instanceUrl -replace "\\u0026", "&" + Write-Host "Use this URL to complete the workflow steps:" + Write-Host $instanceUrl + + + Write-Host "" + Write-Host "Opening a browser with the embedded workflow..." + + # Wait a bit to let the server start + Start-Sleep -Seconds 2 + + # Start script for the embedded workflow +& "./examples/Maestro/startServerForEmbeddingWorkflow.ps1" -instanceUrl $instanceUrl + + # Open the browser + Start-Process "http://localhost:8080" +} else { + Write-Host "" + Write-Host "The WORKFLOW_ID file contains the ID of the unpublished maestro workflow. Please, delete this file and try running the example again." +} \ No newline at end of file diff --git a/examples/Maestro/eg002PauseWorkflow.ps1 b/examples/Maestro/eg002PauseWorkflow.ps1 new file mode 100644 index 0000000..6e37578 --- /dev/null +++ b/examples/Maestro/eg002PauseWorkflow.ps1 @@ -0,0 +1,63 @@ +# Pause a running workflow instance + +# Check for workflow_id file existence and content +if (Test-Path "config/WORKFLOW_ID") { + $workflowId = Get-Content "config/WORKFLOW_ID" + if ([string]::IsNullOrWhiteSpace($workflowId)) { + Write-Host "Workflow ID file is empty. Please run example 1 to create a workflow before running this example." + exit 0 + } +} else { + Write-Host "Workflow ID file does not exist. Please run example 1 to create a workflow before running this example." + exit 1 +} +# Step 1: Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +$basePath = "https://api-d.docusign.com/v1" + +# Construct your API headers +#ds-snippet-start:Maestro2Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Maestro2Step2 + +Write-Host "" +Write-Host "Attempting to pause the Workflow.." +Write-Host "" + +# Send the POST request + +$response = New-TemporaryFile +try { + #ds-snippet-start:Maestro2Step3 + Invoke-RestMethod ` + -Uri "${basePath}/accounts/${accountId}/workflows/${workflowId}/actions/pause" ` + -Method 'POST' ` + -Headers $headers ` + -OutFile $response + #ds-snippet-end:Maestro2Step3 + + Write-Host "" + Write-Host "Workflow has been paused." + Write-Host "" + Write-Output "Response: $(Get-Content -Raw $response)" +} catch { + Write-Output "Unable to pause creation of workflow instances." + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} +Write-Host "" + diff --git a/examples/Maestro/eg003ResumeWorkflow.ps1 b/examples/Maestro/eg003ResumeWorkflow.ps1 new file mode 100644 index 0000000..e513d82 --- /dev/null +++ b/examples/Maestro/eg003ResumeWorkflow.ps1 @@ -0,0 +1,62 @@ +# Resume creation of a workflow instance + +# Check for workflow_id file existence and content +if (Test-Path "config/WORKFLOW_ID") { + $workflowId = Get-Content "config/WORKFLOW_ID" + if ([string]::IsNullOrWhiteSpace($workflowId)) { + Write-Host "Workflow ID file is empty. Please run example 1 to create a workflow before running this example." + exit 0 + } +} else { + Write-Host "Workflow ID file does not exist. Please run example 1 to create a workflow before running this example." + exit 1 +} + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID +$basePath = "https://api-d.docusign.com/v1" + +# Construct your API headers +#ds-snippet-start:Maestro3Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Maestro3Step2 + +Write-Host "" +Write-Host "Attempting to resume the Workflow..." +Write-Host "" + +# Make the API call to resume +$response = New-TemporaryFile +try { +#ds-snippet-start:Maestro3Step3 + Invoke-RestMethod ` + -Uri "${basePath}/accounts/${accountId}/workflows/${workflowId}/actions/resume" ` + -Method 'POST' ` + -Headers $headers ` + -OutFile $response +#ds-snippet-end:Maestro3Step3 + + Write-Host "" + Write-Host "Workflow has been resumed." + Write-Host "" + Write-Output "Response: $(Get-Content -Raw $response)" +} catch { + Write-Output "Unable to resume creation of workflow instances." + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + Write-Output "Error : " $_.ErrorDetails.Message + Write-Output "Command : " $_.InvocationInfo.Line +} +Write-Host "" diff --git a/examples/Maestro/eg004CancelWorkflow.ps1 b/examples/Maestro/eg004CancelWorkflow.ps1 new file mode 100644 index 0000000..b8a40c3 --- /dev/null +++ b/examples/Maestro/eg004CancelWorkflow.ps1 @@ -0,0 +1,56 @@ +# Cancel a workflow instance + +# Check that there is a workflow +if (Test-Path .\config\WORKFLOW_ID) { + $workflowId = Get-Content .\config\WORKFLOW_ID +} else { + Write-Output "Please run example 1 to create and trigger a workflow before running this example," + exit 0 +} + +# Check that there is a running workflow instance to cancel +if (Test-Path .\config\INSTANCE_ID) { + $workflowInstanceId = Get-Content .\config\INSTANCE_ID +} else { + Write-Output "Please run example 1 to trigger a workflow before running this example." + exit 0 +} + +$base_path = "https://api-d.docusign.com/v1" + +# Step 1: Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$apiAccountId = Get-Content .\config\API_ACCOUNT_ID + +# temp file: +$response = New-TemporaryFile + +# Construct your API headers +#ds-snippet-start:Maestro4Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Maestro4Step2 + +Write-Output "Attempting to cancel the Workflow instance..." + +#ds-snippet-start:Maestro4Step3 +Invoke-RestMethod ` + -Uri "${base_path}/accounts/${apiAccountId}/workflows/${workflowId}/instances/${workflowInstanceId}/actions/cancel" ` + -Method 'POST' ` + -Headers $headers ` + -OutFile $response + +#Write-Output "Workflow instance $workflowInstanceId has been canceled." +Write-Output "Response: $(Get-Content -Raw $response)" +#ds-snippet-end:Maestro4Step3 + +# cleanup +Remove-Item $response + +Write-Output "Done." \ No newline at end of file diff --git a/examples/Maestro/startServerForEmbeddingWorkflow.ps1 b/examples/Maestro/startServerForEmbeddingWorkflow.ps1 new file mode 100644 index 0000000..92aeb6f --- /dev/null +++ b/examples/Maestro/startServerForEmbeddingWorkflow.ps1 @@ -0,0 +1,68 @@ +param ( + [string]$instanceUrl +) + +$PORT = 8080 +$IP = "localhost" +$listener = New-Object System.Net.HttpListener +$prefix = "http://$IP`:$PORT/" +$listener.Prefixes.Add($prefix) +$listener.Start() +Write-Host "Listening on $prefix" + +# Correct HTML without raw HTTP headers +$responseHtml = @" +
+

The document has been embedded using Maestro Embedded Workflow.

+
+ + + + + + Example Workflow + + + + +
+ +
+ + + + +

Continue

+"@ + +try { + while ($true) { + $context = $listener.GetContext() + $request = $context.Request + $response = $context.Response + + if ($request.HttpMethod -eq "GET") { + $response.ContentType = "text/html" + $buffer = [System.Text.Encoding]::UTF8.GetBytes($responseHtml) + $response.ContentLength64 = $buffer.Length + $response.OutputStream.Write($buffer, 0, $buffer.Length) + $response.OutputStream.Close() + break + } + } +} catch { + Write-Error "Server error: $_" +} finally { + $listener.Stop() + Write-Host "Server stopped." +} \ No newline at end of file diff --git a/examples/Monitor/eg001GetMonitoringData.ps1 b/examples/Monitor/eg001GetMonitoringData.ps1 new file mode 100644 index 0000000..7aacf53 --- /dev/null +++ b/examples/Monitor/eg001GetMonitoringData.ps1 @@ -0,0 +1,90 @@ +# Temp files: +$response = New-TemporaryFile + +# Step 1. Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt + +# Step 2. Construct your API headers +#ds-snippet-start:Monitor1Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Monitor1Step2 + +# Declare variables +$complete=$false +$cursorValue = (Get-Date (Get-Date).AddDays(-10) -Format "yyyy-MM-dd") +$iterations=0 +# You must provide an access token that impersonates a user with permissions to access the Monitor API endpoint +if (($accessToken -eq "") -or ($null -eq $accessToken)) { + Write-Output "You must provide an access token" + $complete = $true +} + +# Step 3: Get monitoring data +# First call the endpoint with no cursor to get the first records. +# After each call, save the cursor and use it to make the next +# call from the point where the previous one left off when iterating through +# the monitoring records +#ds-snippet-start:Monitor1Step3 +DO { + $iterations++ + Write-Output "" + try { + Invoke-RestMethod ` + -Uri "https://lens-d.docusign.net/api/v2.0/datasets/monitor/stream?cursor=${cursorValue}&limit=2000" ` + -Method 'GET' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -OutFile $response + # Display the data + Write-Output "Iteration:" + Write-Output $iterations + Write-Output "Results:" + Get-Content $response + # Get the endCursor value from the response. This lets you resume + # getting records from the spot where this call left off + $endCursorValue = (Get-Content $response | ConvertFrom-Json).endCursor + Write-Output "endCursorValue is:" + Write-Output $endCursorValue + Write-Output "cursorValue is:" + Write-Output $cursorValue + + # If the endCursor from the response is the same as the one that you already have, + # it means that you have reached the + # end of the records + if ($endCursorValue -eq $cursorValue) { + Write-Output 'After getting records, the cursor values are the same. This indicates that you have reached the end of your available records.' + $complete=$true + } + else { + Write-Output "Updating the cursor value of $cursorValue to the new value of $endCursorValue" + $cursorValue=$endCursorValue + Start-Sleep -Second 5 + } + } + catch { + $int = 0 + foreach($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { + Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] + } + $int++ + } + Write-Output "You do not have Monitor enabled for your account, follow https://developers.docusign.com/docs/monitor-api/how-to/enable-monitor/ to get it enabled." + Write-Output "Command : "$_.InvocationInfo.Line + $complete = $true + } + +} While ($complete -eq $false ) +#ds-snippet-end:Monitor1Step3 + +Remove-Item $response + +Write-Output "" +Write-Output "" +Write-Output "Done." +Write-Output "" diff --git a/examples/Navigator/eg001ListAgreements.ps1 b/examples/Navigator/eg001ListAgreements.ps1 new file mode 100644 index 0000000..bdd22f6 --- /dev/null +++ b/examples/Navigator/eg001ListAgreements.ps1 @@ -0,0 +1,49 @@ +$apiUri = "https://api-d.docusign.com/v1" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" +$agreementsPath = ".\config\AGREEMENTS.txt" + +# Get required variables from .\config\settings.json file +$config = Get-Content $configPath -Raw | ConvertFrom-Json + +$accessToken = Get-Content $tokenPath +$accountId = Get-Content $accountIdPath + +#ds-snippet-start:Navigator1Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Navigator1Step2 + +# List agreements +#ds-snippet-start:Navigator1Step3 +$response = New-TemporaryFile +Invoke-RestMethod ` + -Uri "${apiUri}/accounts/${accountId}/agreements" ` + -Method 'GET' ` + -Headers $headers ` + -OutFile $response + +$responseContent = $(Get-Content $response | ConvertFrom-Json) +#ds-snippet-end:Navigator1Step3 + +Write-Host "" +Write-Output "Response: $(Get-Content -Raw $response)" + +# Clear the output file at the beginning +Clear-Content -Path $agreementsPath -ErrorAction SilentlyContinue + +# Loop through each item in the 'data' array +foreach ($item in $responseContent.data) { + # Extract id and file_name + $id = $item.id + $fileName = $item.file_name + + # Write the id and file_name to the output file + "$id $fileName" | Out-File -FilePath $agreementsPath -Append +} + +Write-Output "" +Write-Output "Done." diff --git a/examples/Navigator/eg002GetSingleAgreement.ps1 b/examples/Navigator/eg002GetSingleAgreement.ps1 new file mode 100644 index 0000000..dc76b0c --- /dev/null +++ b/examples/Navigator/eg002GetSingleAgreement.ps1 @@ -0,0 +1,78 @@ +$apiUri = "https://api-d.docusign.com/v1" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" +$agreementsPath = ".\config\AGREEMENTS.txt" + +# Check if the agreements file exists and has content +if (-not (Test-Path $agreementsPath) -or -not (Get-Content $agreementsPath -ErrorAction SilentlyContinue)) { + Write-Output "No agreements found in $agreementsPath." + Write-Output "Please run Navigator example 1: List_Agreements first to get a list of agreements." + exit 0 +} + +# Load the file into an array, separating each line into id and file name +$agreements = Get-Content -Path $agreementsPath | ForEach-Object { + $parts = $_ -split '\s+' + [PSCustomObject]@{ + Id = $parts[0] + FileName = $parts[1] + } +} + +# Display the file names for selection +Write-Output "Please select an agreement:" +for ($i = 0; $i -lt $agreements.Count; $i++) { + Write-Output "$($i + 1). $($agreements[$i].FileName)" +} + +# Prompt user selection +do { + $selection = Read-Host "Enter the number corresponding to your choice" + + if ($selection -match '^\d+$' -and $selection -gt 0 -and $selection -le $agreements.Count) { + $chosenAgreement = $agreements[$selection - 1] + $AGREEMENT_ID = $chosenAgreement.Id + + Write-Output "You selected: $($chosenAgreement.FileName)" + Write-Output "AGREEMENT_ID: $AGREEMENT_ID" + break + } + else { + Write-Output "Invalid selection. Please try again." + } +} while ($true) + +# Get required variables from .\config\settings.json file +$config = Get-Content $configPath -Raw | ConvertFrom-Json + +$accessToken = Get-Content $tokenPath +$accountId = Get-Content $accountIdPath + +#ds-snippet-start:Navigator2Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Navigator2Step2 + +# List agreements +#ds-snippet-start:Navigator2Step3 +$response = New-TemporaryFile +Invoke-RestMethod ` + -Uri "${apiUri}/accounts/${accountId}/agreements/${AGREEMENT_ID}" ` + -Method 'GET' ` + -Headers $headers ` + -OutFile $response + +$responseContent = $(Get-Content $response | ConvertFrom-Json) +#ds-snippet-end:Navigator2Step3 + +Write-Host "" +Write-Output "Response: $(Get-Content -Raw $response)" + + + + +Write-Output "" +Write-Output "Done." diff --git a/examples/Notary/Jurisdictions.ps1 b/examples/Notary/Jurisdictions.ps1 new file mode 100644 index 0000000..ab1b537 --- /dev/null +++ b/examples/Notary/Jurisdictions.ps1 @@ -0,0 +1,36 @@ +# Returns the status of whether or not jurisdictions are disabled + +$apiUri = "https://notary-d.docusign.net/restapi" + +# Obtain your Oauth access token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountID = Get-Content .\config\API_ACCOUNT_ID + +# Make a GET request to the jurisdictions endpoint + +$response = New-TemporaryFile + +Write-Output "Sending the jurisdiction status request to Docusign..." +Write-Output "" +Write-Output "Results:" +#ds-snippet-start:Notary3Step2 +Invoke-RestMethod ` + -UseBasicParsing ` + -Uri "${apiUri}/v1.0/accounts/${accountID}/jurisdictions" ` + -Method 'GET' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -OutFile $response +#ds-snippet-end +Write-Output "Response: $(Get-Content -Raw $response)" + +# cleanup +Remove-Item $response +Write-Output "" +Write-Output "Done." diff --git a/examples/Notary/inviteNotaryToPool.ps1 b/examples/Notary/inviteNotaryToPool.ps1 new file mode 100644 index 0000000..edd830f --- /dev/null +++ b/examples/Notary/inviteNotaryToPool.ps1 @@ -0,0 +1,80 @@ +# Invite a notary to join your pool + +$apiUri = "https://notary-d.docusign.net/restapi" + + +# Step 1. Obtain your Oauth access token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt +$accountID = Get-Content .\config\API_ACCOUNT_ID +$response = New-TemporaryFile + +# Construct your API headers + +#ds-snippet-start:Notary2Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Check that we have an organization id in the settings.json config file +if (!$variables.ORGANIZATION_ID) { + Write-Output "Organization ID is needed. Please add the ORGANIZATION_ID variable to the settings.json" + exit -1 +} + +$organizationId = $variables.ORGANIZATION_ID + + +Write-Output "" +Write-Output "Retrieving your notary pool:" +Write-Output "" + +#ds-snippet-start:Notary2Step3 +Invoke-RestMethod ` + -UseBasicParsing ` + -Uri "${apiUri}/v1.0/organizations/${organizationId}/pools/" ` + -Headers $headers ` + -Method 'GET' ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" +$POOL_ID = $(Get-Content $response | ConvertFrom-Json).pools[0].poolId +#ds-snippet-end + +#ds-snippet-start:Notary2Step4 +write-Output "" +$NOTARY_NAME = Read-Host "Enter a name for the notary" +$NOTARY_EMAIL = Read-Host "Enter an email address for the notary" + +$body = @" +{ + "email" : "${NOTARY_EMAIL}", + "name" : "${NOTARY_NAME}", +} +"@ +#ds-snippet-end + +write-Output "" +write-Output "Inviting ${NOTARY_NAME} to your organization's notary pool" +write-Output "" +write-Output "Pool id is ${POOL_ID}" +write-Output "" +#ds-snippet-start:Notary2Step5 +Invoke-RestMethod ` + -Uri "${apiUri}/v1.0/organizations/${organizationId}/pools/${POOL_ID}/invites" ` + -Headers $headers ` + -Method 'POST' ` + -Body $body ` + -Outfile $response + +Write-Output "Response: $(Get-Content -Raw $response)" +#ds-snippet-end + +# cleanup +Remove-Item $response +Write-Output "" +Write-Output "Done." diff --git a/examples/Notary/sendWithThirdPartyNotary.ps1 b/examples/Notary/sendWithThirdPartyNotary.ps1 new file mode 100644 index 0000000..f50432d --- /dev/null +++ b/examples/Notary/sendWithThirdPartyNotary.ps1 @@ -0,0 +1,159 @@ +$apiUri = "https://demo.docusign.net/restapi" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" + +# Check the folder structure to switch paths for Quick ACG +if ((Test-Path $configPath) -eq $false) { + $configPath = "..\config\settings.json" +} +if ((Test-Path $tokenPath) -eq $false) { + $tokenPath = "..\config\ds_access_token.txt" +} +if ((Test-Path $accountIdPath) -eq $false) { + $accountIdPath = "..\config\API_ACCOUNT_ID" +} + +# Send an envelope with a third-party notary service + +# Get required variables from .\config\settings.json file +$variables = Get-Content $configPath -Raw | ConvertFrom-Json + +# 1. Obtain your OAuth token +$accessToken = Get-Content $tokenPath + +# Obtain your accountId from demo.docusign.net -- the account id is shown in +# the drop down on the upper right corner of the screen by your picture or +# the default picture. +$accountID = Get-Content $accountIdPath + +# Step 2. Create the envelope definition. +# The signer recipient includes a clientUserId setting + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$docBase64 = New-TemporaryFile + +$docPath = ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx" + +# Check the folder structure to switch paths for Quick ACG +if ((Test-Path $docPath) -eq $false) { + $docPath = "..\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx" +} + +# Fetch doc and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path $docPath))) > $docBase64 + +Write-Output "Sending the envelope request to Docusign..." +Write-Output "Please wait, this may take a few moments." + +#ds-snippet-start:Notary4Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:Notary4Step2 + +# Concatenate the different parts of the request +#ds-snippet-start:Notary4Step3 +@{ + emailSubject = "Please sign this document set"; + documents = @( + @{ + documentBase64 = "$(Get-Content $docBase64)"; + name = "Order acknowledgement"; + fileExtension = "html"; + documentId = "1"; + }; + ); + recipients = @{ + signers = @( + @{ + clientUserId = "1000"; + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "2"; + routingOrder = "1"; + notaryId = "1"; + tabs = @{ + signHereTabs = @( + @{ + documentId = "1"; + xPosition = "200"; + yPosition = "235"; + pageNumber = "1"; + }; + @{ + stampType = "stamp"; + documentId = "1"; + xPosition = "200"; + yPosition = "150"; + pageNumber = "1"; + }; + ); + }; + }; + ); + notaries = @( + @{ + name = "Notary"; + recipientId = "1"; + routingOrder = "1"; + tabs = @{ + notarySealTabs = @( + @{ + xPosition = "300"; + yPosition = "235"; + documentId = "1"; + pageNumber = "1"; + }; + ); + signHereTabs = @( + @{ + xPosition = "300"; + yPosition = "150"; + documentId = "1"; + pageNumber = "1"; + }; + ); + }; + notaryType = "remote"; + notarySourceType = "thirdparty"; + notaryThirdPartyPartner = "onenotary"; + recipientSignatureProviders = @( + @{ + sealDocumentsWithTabsOnly = "false"; + signatureProviderName = "ds_authority_idv"; + signatureProviderOptions = @{}; + }; + ); + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:Notary4Step3 + +# Step 3. Call Docusign to create the envelope +#ds-snippet-start:Notary4Step4 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers $headers ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:Notary4Step4 + +Write-Output "Response: $(Get-Content -Raw $response)" + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId +Write-Output "EnvelopeId: $envelopeId" + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $docBase64 + +Write-Output "Done." \ No newline at end of file diff --git a/examples/Notary/signatureRequestToNotaryGroup.ps1 b/examples/Notary/signatureRequestToNotaryGroup.ps1 new file mode 100644 index 0000000..796ec27 --- /dev/null +++ b/examples/Notary/signatureRequestToNotaryGroup.ps1 @@ -0,0 +1,148 @@ +$apiUri = "https://demo.docusign.net/restapi" + +# Send an envelope with three documents + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Check that we have a Notary name and email in the settings.json config file +if ((!$variables.NOTARY_EMAIL) -or (!$variables.NOTARY_NAME) -or (!$variables.NOTARY_API_ACCOUNT_ID)) { + Write-Output "NOTARY_EMAIL, NOTARY_NAME, and NOTARY_API_ACCOUNT_ID are needed. Please add the NOTARY_EMAIL, NOTARY_NAME, and NOTARY_API_ACCOUNT_ID variables to the settings.json" + exit -1 +} + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +#ds-snippet-start:Notary1Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Accept' = "application/json"; + 'Content-Type' = "application/json"; +} +#ds-snippet-end + +# document 1 (html) has tag **signature_1** +# document 2 (docx) has tag /sn1/ +# document 3 (pdf) has tag /sn1/ +# +# The envelope has two recipients. +# recipient 1 - signer +# recipient 2 - cc +# The envelope will be sent first to the signer. +# After it is signed, a copy is sent to the cc person. + + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$docBase64 = New-TemporaryFile + +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx"))) > $docBase64 + +Write-Output "Sending the envelope request to DocuSign..." +Write-Output "The envelope processing time will be about 15 seconds." + +# Concatenate the different parts of the request + +#ds-snippet-start:Notary1Step3 +@{ + emailSubject = "Please sign this document set"; + documents = @( + @{ + documentBase64 = "$(Get-Content $docBase64)"; + name = "Order acknowledgement"; + fileExtension = "html"; + documentId = "1"; + }; + ); + recipients = @{ + notaries = @( + @{ + email = $variables.NOTARY_EMAIL; + name = $variables.NOTARY_NAME; + recipientId = "1"; + routingOrder = "1"; + tabs = @{ + notarySealTabs = @( + @{ + xPosition = "300"; + yPosition = "245"; + documentId = "1"; + pageNumber = "1"; + }; + ); + signHereTabs = @( + @{ + xPosition = "300"; + yPosition = "150"; + documentId = "1"; + pageNumber = "1"; + }; + ) + }; + userId = $variables.NOTARY_API_ACCOUNT_ID; + notaryType = "remote"; + }; + ); + signers = @( + @{ + clientUserId = "12345"; + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "2"; + routingOrder = "1"; + notaryId = "1"; + tabs = @{ + signHereTabs = @( + @{ + documentId = "1"; + xPosition = "200"; + yPosition = "245"; + pageNumber = "1"; + }; + @{ + stampType = "stamp"; + documentId = "1"; + xPosition = "200"; + yPosition = "150"; + pageNumber = "1"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end + +# Create and send the envelope +#ds-snippet-start:Notary1Step4 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers $headers ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" +#ds-snippet-end +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId + +# Save the envelope id for use by other scripts +Write-Output "EnvelopeId: $envelopeId" +Write-Output $envelopeId > .\config\ENVELOPE_ID + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $docBase64 + +Write-Output "Done." diff --git a/examples/Rooms/eg001CreateRoomWithDataController.ps1 b/examples/Rooms/eg001CreateRoomWithDataController.ps1 index 3fda371..d2833aa 100644 --- a/examples/Rooms/eg001CreateRoomWithDataController.ps1 +++ b/examples/Rooms/eg001CreateRoomWithDataController.ps1 @@ -3,28 +3,26 @@ $accessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers +#ds-snippet-start:Rooms1Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms1Step2 # Get Role ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/roles" $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET -$roleId = $($response.Content | ConvertFrom-Json).roles[0].roleid - -# Get Office ID -$uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/offices" -$response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET -$officeId = $($response.Content | ConvertFrom-Json).officeSummaries.officeId +$roles = $($response.Content | ConvertFrom-Json).roles +$roleId = $roles.Where({$_.name -eq "Default Admin"}).roleId # - Construct the request body for your room +#ds-snippet-start:Rooms1Step3 $body = @" { "name": "Sample Room Creation", "roleId": "$roleId", "transactionSideId": "listbuy", - "officeId": "$officeId", "fieldData": { "data" : { "address1": "123 EZ Street", @@ -38,9 +36,11 @@ $body = @" } } "@ +#ds-snippet-end:Rooms1Step3 # a) Call the Rooms API # b) Display the JSON response +#ds-snippet-start:Rooms1Step4 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms" try { @@ -59,4 +59,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:Rooms1Step4 diff --git a/examples/Rooms/eg002CreateRoomWithTemplateController.ps1 b/examples/Rooms/eg002CreateRoomWithTemplateController.ps1 index 93ac09a..432bbc8 100644 --- a/examples/Rooms/eg002CreateRoomWithTemplateController.ps1 +++ b/examples/Rooms/eg002CreateRoomWithTemplateController.ps1 @@ -5,19 +5,23 @@ $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers # - Construct your API headers +#ds-snippet-start:Rooms2Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms2Step2 # - Retrieve rooms pre-requisite data # - Obtain our RoleID and OfficeID +#ds-snippet-start:Rooms2Step3 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/roles" $uriOfficeId = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/offices" try { $response = Invoke-RestMethod -uri $uri -headers $headers -method GET - $roleId = $response.roles[0].roleId + $roles = $response.roles + $roleId = $roles.Where({$_.name -eq "Default Admin"}).roleId $roomTemplateId = $response.roomTemplates.roomTemplateId Write-Output "roleID:" $roleId $response = Invoke-RestMethod -uri $uriOfficeId -headers $headers -method GET @@ -33,6 +37,7 @@ catch { Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line } +#ds-snippet-end:Rooms2Step3 # - Retrieve a Rooms template ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/room_templates" @@ -55,6 +60,7 @@ catch { } # Construct the JSON body for your room +#ds-snippet-start:Rooms2Step4 $body = @" { "name": "Sample Rooms Creation from Template", @@ -75,9 +81,11 @@ $body = @" } } "@ +#ds-snippet-end:Rooms2Step4 # a) Call the Rooms API # b) Display JSON response +#ds-snippet-start:Rooms2Step5 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms" try { @@ -94,4 +102,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:Rooms2Step5 diff --git a/examples/Rooms/eg003ExportDataFromRoomController.ps1 b/examples/Rooms/eg003ExportDataFromRoomController.ps1 index bc7ca9b..e86f951 100644 --- a/examples/Rooms/eg003ExportDataFromRoomController.ps1 +++ b/examples/Rooms/eg003ExportDataFromRoomController.ps1 @@ -3,10 +3,12 @@ $accessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers +#ds-snippet-start:Rooms3Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms3Step2 # Get Room ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms" @@ -15,6 +17,7 @@ $roomId = $($response.Content | ConvertFrom-Json).rooms[0].roomId # a) Call the Rooms API # b) Display JSON response +#ds-snippet-start:Rooms3Step3 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms/$roomId/field_data" try { @@ -31,4 +34,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:Rooms3Step3 diff --git a/examples/Rooms/eg004AddFormsToRoomController.ps1 b/examples/Rooms/eg004AddFormsToRoomController.ps1 index 96cc221..430041a 100644 --- a/examples/Rooms/eg004AddFormsToRoomController.ps1 +++ b/examples/Rooms/eg004AddFormsToRoomController.ps1 @@ -3,10 +3,12 @@ $accessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers +#ds-snippet-start:Rooms4Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms4Step2 # Get Room ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms" @@ -14,21 +16,25 @@ $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -meth $roomId = $($response.Content | ConvertFrom-Json).rooms[0].roomId # Get Form Library ID +#ds-snippet-start:Rooms4Step3 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/form_libraries" $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -$formLibraryId = $($response.Content | ConvertFrom-Json).formsLibrarySummaries.formsLibraryId +$formLibraryId = $($response.Content | ConvertFrom-Json).formsLibrarySummaries[0].formsLibraryId # Get Form ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/form_libraries/$formLibraryId/forms" -$response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers +$response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET $formId = $($response.Content | ConvertFrom-Json).forms[0].libraryFormId # Construct the request body for adding a form $body = @" {"formId":"$formId"} "@ +#ds-snippet-end:Rooms4Step3 + # Call the Rooms API +#ds-snippet-start:Rooms4Step4 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms/$roomId/forms" try { @@ -46,4 +52,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:Rooms4Step4 diff --git a/examples/Rooms/eg005GetRoomsWithFiltersController.ps1 b/examples/Rooms/eg005GetRoomsWithFiltersController.ps1 index d17ce8b..054bc24 100644 --- a/examples/Rooms/eg005GetRoomsWithFiltersController.ps1 +++ b/examples/Rooms/eg005GetRoomsWithFiltersController.ps1 @@ -3,17 +3,22 @@ $accessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers +#ds-snippet-start:Rooms5Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms5Step2 +#ds-snippet-start:Rooms5Step3 # Set your filtering parameters $past = (Get-Date (Get-Date).AddDays(-10) -Format "yyyy-MM-dd") # Set the date 1 day forward to account for changes made today $current = (Get-Date (Get-Date).AddDays(1) -Format "yyyy-MM-dd") +#ds-snippet-end:Rooms5Step3 # Call the v2 Rooms API +#ds-snippet-start:Rooms5Step4 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms?fieldDataChangedStartDate=$past&fieldDataChangedEndDate=$current" try { @@ -30,4 +35,5 @@ catch { } Write-Output "Error : "$_ Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:Rooms5Step4 diff --git a/examples/Rooms/eg006CreateAnExternalFormFillSessionController.ps1 b/examples/Rooms/eg006CreateAnExternalFormFillSessionController.ps1 index 96bcf22..ef7281b 100644 --- a/examples/Rooms/eg006CreateAnExternalFormFillSessionController.ps1 +++ b/examples/Rooms/eg006CreateAnExternalFormFillSessionController.ps1 @@ -3,10 +3,12 @@ $accessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers +#ds-snippet-start:Rooms6Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $accessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms6Step2 # Get Room ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/rooms" @@ -16,30 +18,47 @@ $roomId = $($response.Content | ConvertFrom-Json).rooms[0].roomId # Get Form Library ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/form_libraries" $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -$formLibraryId = $($response.Content | ConvertFrom-Json).formsLibrarySummaries.formsLibraryId +$formLibraryId = $($response.Content | ConvertFrom-Json).formsLibrarySummaries[0].formsLibraryId # Get Form ID $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/form_libraries/$formLibraryId/forms" -$response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers +$response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET $formId = $($response.Content | ConvertFrom-Json).forms[0].libraryFormId + # Construct your request body +#ds-snippet-start:Rooms6Step3 $body = @" { - "roomId": $roomId, - "formId": "$formId" + "roomId": "$roomId", + "formId": "$formId", + "xFrameAllowedUrl": "https://iframetester.com/" } "@ +#ds-snippet-end:Rooms6Step3 # a) Call the v2 Rooms API # b) Display the JSON response +#ds-snippet-start:Rooms6Step4 $uri = "https://demo.rooms.docusign.com/restapi/v2/accounts/$APIAccountId/external_form_fill_sessions" try { Write-Output "Response:" $response = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -body $body -method POST $response.Content | ConvertFrom-Json | ConvertTo-Json +#ds-snippet-end:Rooms6Step4 + +#ds-snippet-start:Rooms6Step5 + $signingUrl = $($response.Content | ConvertFrom-Json).url + + $redirectUrl = "https://iframetester.com/?url="+$signingUrl + + Write-Output "The embedded form URL is $redirectUrl" + Write-Output "Attempting to automatically open your browser..." + + Start-Process $redirectUrl +#ds-snippet-end:Rooms6Step5 } catch { Write-Output "Unable to access form fill view link " @@ -49,6 +68,14 @@ catch { if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } $int++ } - Write-Output "Error : "$_.ErrorDetails.Message + + $errorMessage = $_.ErrorDetails.Message + + if ( $errorMessage.Contains("INVALID_REQUEST_PARAMETERS") ) { Write-Output "Problem: Create a room using example 1." } + + if ( $errorMessage.Contains("PROPERTY_VALIDATION_FAILURE") -or $errorMessage.Contains("FORM_NOT_IN_ROOM")) { Write-Output "Problem: Selected room does not have any forms. Add a form to a room using example 4." } + + Write-Output "Error : "$errorMessage Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file + +} diff --git a/examples/Rooms/eg007CreateFormGroup.ps1 b/examples/Rooms/eg007CreateFormGroup.ps1 new file mode 100644 index 0000000..5760198 --- /dev/null +++ b/examples/Rooms/eg007CreateFormGroup.ps1 @@ -0,0 +1,49 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + + +# Construct your API headers +#ds-snippet-start:Rooms7Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms7Step2 + +#ds-snippet-start:Rooms7Step3 +# Construct the request body +$body = @" + { + "name": "Sample Room Form Group" + } +"@ +#ds-snippet-end:Rooms7Step3 + +# Call the Rooms API +#ds-snippet-start:Rooms7Step4 +$base_path = "https://demo.rooms.docusign.com" +$uri = "$base_path/restapi/v2/accounts/$APIAccountId/form_groups" + +try { + Write-Output "Response:" + $response = Invoke-WebRequest -uri $uri -headers $headers -method POST -body $body + $response.Content + $obj = $response.Content | ConvertFrom-Json + $formGroupID = $obj.formGroupId + + # Store formGroupID into the file .\config\FORM_GROUP_ID + $formGroupID > .\config\FORM_GROUP_ID +} +catch { + Write-Output "Unable to create a form group" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} +#ds-snippet-end:Rooms7Step4 diff --git a/examples/Rooms/eg008AccessFormGroup.ps1 b/examples/Rooms/eg008AccessFormGroup.ps1 new file mode 100644 index 0000000..44bfbfc --- /dev/null +++ b/examples/Rooms/eg008AccessFormGroup.ps1 @@ -0,0 +1,105 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Construct your API headers +#ds-snippet-start:Rooms8Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms8Step2 + +# Get form groups +#ds-snippet-start:Rooms8Step3 +$base_path = "https://demo.rooms.docusign.com" +$formGroupsUri = "$base_path/restapi/v2/accounts/$APIAccountId/form_groups" + +try { + Write-Output "Retrieving form groups..." + $response = Invoke-WebRequest -uri $formGroupsUri -headers $headers -method GET + $formGroups = ($response.Content | ConvertFrom-Json).formGroups + + if (-not $formGroups) { + Write-Output "No form groups found. Execute code example 7 - Create a form group..." + exit 1 + } + + # Display the form groups + Write-Host "Available form groups:" + for ($i = 0; $i -lt $formGroups.Count; $i++) { + Write-Host "$($i + 1): $($formGroups[$i].name) (ID: $($formGroups[$i].formGroupId))" + } + + # Prompt the user to select a form group + $selection = Read-Host "Enter the number of the form group you want to use" + if (-not ($selection -as [int]) -or $selection -lt 1 -or $selection -gt $formGroups.Count) { + Write-Output "Invalid selection. Please enter a number between 1 and $($formGroups.Count)." + exit 1 + } + + # Get the selected form group + $selectedFormGroup = $formGroups[$selection - 1] + $formGroupID = $selectedFormGroup.formGroupId + Write-Host "You selected: $($selectedFormGroup.name)" +} +catch { + Write-Output "Unable to retrieve form groups." + Write-Output "Error: $($_.Exception.Message)" + exit 1 +} +#ds-snippet-end:Rooms8Step3 + +# Get an office ID +#ds-snippet-start:Rooms8Step4 +$officeUri = "$base_path/restapi/v2/accounts/$APIAccountId/offices" + +try { + Write-Output "Retrieving office ID..." + $response = Invoke-WebRequest -uri $officeUri -headers $headers -method GET + $officeSummaries = ($response.Content | ConvertFrom-Json).officeSummaries + + if (-not $officeSummaries) { + Write-Output "No offices found." + exit 1 + } + + $officeID = $officeSummaries[0].officeId +} +catch { + Write-Output "Unable to retrieve an office ID." + Write-Output "Error: $($_.Exception.Message)" + exit 1 +} +#ds-snippet-end:Rooms8Step4 + +# Call the Rooms API to grant office access to the selected form group +#ds-snippet-start:Rooms8Step5 +$uri = "$base_path/restapi/v2/accounts/$APIAccountId/form_groups/$formGroupID/grant_office_access/$officeID" + +try { + Write-Output "Response:" + $response = Invoke-WebRequest -uri $uri -headers $headers -method POST -body $body + $response.StatusCode + if ($response.StatusCode -eq "204") { + Write-Output "Form group has been assigned to office ID." + } + + # check that we have got a 204 Status code response + if ($response.StatusCode -ne "204" ) { + Write-Output "Unable to assign the provided form group ID to the provided office ID!" + exit 1 + } +} +catch { + Write-Output "Unable to grant office access to a form group" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} +#ds-snippet-end:Rooms8Step5 diff --git a/examples/Rooms/eg009AssignFormGroup.ps1 b/examples/Rooms/eg009AssignFormGroup.ps1 new file mode 100644 index 0000000..b9ba05f --- /dev/null +++ b/examples/Rooms/eg009AssignFormGroup.ps1 @@ -0,0 +1,140 @@ +# Get required environment variables from .\config\settings.json file +$accessToken = Get-Content .\config\ds_access_token.txt +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# Construct your API headers +#ds-snippet-start:Rooms9Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:Rooms9Step2 + +# Call the Rooms API to look up your forms library ID +#ds-snippet-start:Rooms9Step3 +$base_path = "https://demo.rooms.docusign.com" +$uri = "$base_path/restapi/v2/accounts/$APIAccountId/form_libraries" +try { + Write-Output "Response:" + $response = Invoke-WebRequest -uri $uri -headers $headers -method GET + $response.Content + # Retrieve a form library ID + $obj = $response.Content | ConvertFrom-Json + $formsLibraryID = $obj.formsLibrarySummaries[0].formsLibraryId +} catch { + Write-Output "Unable to retrieve form library" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} + +# Call the Rooms API to look up a list of form IDs for the given forms library +$uri = "$base_path/restapi/v2/accounts/$APIAccountId/form_libraries/$formsLibraryID/forms" + +try { + Write-Output "" + Write-Output "Response:" + $response = Invoke-WebRequest -uri $uri -headers $headers -method GET + $response.Content + + $formsObj = $($response.Content | ConvertFrom-Json).forms + + Write-Output "" + $menu = @{} + for ($i=1;$i -le $formsObj.count; $i++) { + Write-Output "$i. $($formsObj[$i-1].name)" + $menu.Add($i,($formsObj[$i-1].libraryFormId)) + } + + do { + Write-Output "" + [int]$selection = Read-Host 'Select a form by the form name: ' + } while ($selection -gt $formsObj.count -or $selection -lt 1); + $formID = $menu.Item($selection) + + Write-Output "" + Write-Output "Form Id: $formID" +} catch { + Write-Output "Unable to retrieve a form id" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} +#ds-snippet-end:Rooms9Step3 + +#ds-snippet-start:Rooms9Step4 +$formGroupID = "" + +# Call the Rooms API to look up a list of form group IDs +$uri = "${base_path}/restapi/v2/accounts/$APIAccountId/form_groups" +$result = Invoke-WebRequest -uri $uri -UseBasicParsing -headers $headers -method GET + +Write-Output "" +Write-Output "Response:" +$result.Content + +$formGroupObj = $($result.Content | ConvertFrom-Json).formGroups +Write-Output "" + +# Setup a temporary menu option to pick a form group +$menu = @{} +for ($i=1;$i -le $formGroupObj.count; $i++) { + Write-Output "$i. $($formGroupObj[$i-1].name)" + $menu.Add($i,($formGroupObj[$i-1].formGroupId)) +} + +if ($formGroupObj.count -lt 1) { + Write-Output "A form group ID is needed. Fix: execute code example 7 - Create a form group..." + exit 1 +} + +do { + Write-Output "" + [int]$selection = Read-Host 'Select a form group: ' +} while ($selection -gt $formGroupObj.count -or $selection -lt 1); +$formGroupID = $menu.Item($selection) + +Write-Output "" +Write-Output "Form group Id: $formGroupID" +Write-Output "" +#ds-snippet-end:Rooms9Step4 + +# Construct your request body +#ds-snippet-start:Rooms9Step5 +$body = +@" + {"formId": "$formID" } +"@ +#ds-snippet-end:Rooms9Step5 + + +# Call the Rooms API +#ds-snippet-start:Rooms9Step6 +$uri = "$base_path/restapi/v2/accounts/$APIAccountId/form_groups/$formGroupID/assign_form" + +try { + $response = Invoke-WebRequest -uri $uri -headers $headers -method POST -Body $body + Write-Output $response.Status + Write-Output "Response: No JSON response body returned when setting the default office ID in a form group" +} catch { + Write-Output "Unable to assign the form to the form group" + # On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error + + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} +#ds-snippet-end:Rooms9Step6 diff --git a/examples/WebForms/eg001CreateInstance.ps1 b/examples/WebForms/eg001CreateInstance.ps1 new file mode 100644 index 0000000..59a2491 --- /dev/null +++ b/examples/WebForms/eg001CreateInstance.ps1 @@ -0,0 +1,83 @@ +. "utils/invokeScript.ps1" + +$apiUri = "https://apps-d.docusign.com/api/webforms/v1.1" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" + +# Get required variables from .\config\settings.json file +$config = Get-Content $configPath -Raw | ConvertFrom-Json + +$accessToken = Get-Content $tokenPath +$accountId = Get-Content $accountIdPath + +# Create template for the Web Form from the API +Invoke-Script -Command "`".\utils\createWebFormTemplate.ps1`"" + +$templateId = Get-Content -Path ".\config\WEB_FORM_TEMPLATE_ID" + +$webFormConfig = Get-Content -Raw demo_documents\web-form-config.json +$result = $webFormConfig -replace "template-id", $templateId +$result | Set-Content -Path demo_documents\web-form-config.json + +Write-Host "" +Write-Host "Go to your Docusign account to create the Web Form. Go to 'Templates' in your developer account, select 'Start,' select 'Web Forms,' and choose 'Upload Web Form.' Upload the JSON config file 'web-form-config.json' found under the demo_documents folder of this project. You will need to activate the web form before proceeding. Press Continue after doing so." +$choice = Read-Host + +#ds-snippet-start:WebForms1Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:WebForms1Step2 + +# List web forms in account that match the name of the web form we just created +#ds-snippet-start:WebForms1Step3 +$response = New-TemporaryFile +Invoke-RestMethod ` + -Uri "${apiUri}/accounts/${accountId}/forms?search=Web%20Form%20Example%20Template" ` + -Method 'GET ' ` + -Headers $headers ` + -OutFile $response + +$formId = $(Get-Content $response | ConvertFrom-Json).items[0].id +#ds-snippet-end:WebForms1Step3 + +#ds-snippet-start:WebForms1Step4 +$json = @" +{ + "clientUserId": "1234-5678-abcd-ijkl", + "formValues": { + "PhoneNumber": "555-555-5555", + "Yes": ["Yes"], + "Company": "Tally", + "JobTitle": "Programmer Writer" + }, + "expirationOffset": 24 +} +"@ +#ds-snippet-end:WebForms1Step4 + +#ds-snippet-start:WebForms1Step5 +Invoke-RestMethod ` + -Uri "${apiUri}/accounts/${accountId}/forms/${formId}/instances" ` + -Method 'POST' ` + -Headers $headers ` + -Body $json ` + -OutFile $response + +$responseContent = $(Get-Content $response | ConvertFrom-Json) +#ds-snippet-end:WebForms1Step5 + +Write-Host "" +Write-Host "Response:" +Write-Host $responseContent + +$formUrl = $responseContent.formUrl +$instanceToken = $responseContent.instanceToken +$integrationKey = $config.INTEGRATION_KEY_AUTH_CODE + +Invoke-Script -Command "./utils/startServerForWebFormsExample.ps1 -integrationKey $integrationKey -url $formUrl -instanceToken $instanceToken" + +Write-Output "" +Write-Output "Done." diff --git a/examples/WebForms/eg002CreateRemoteInstance.ps1 b/examples/WebForms/eg002CreateRemoteInstance.ps1 new file mode 100644 index 0000000..7d65771 --- /dev/null +++ b/examples/WebForms/eg002CreateRemoteInstance.ps1 @@ -0,0 +1,87 @@ +. "utils/invokeScript.ps1" + +$apiUri = "https://apps-d.docusign.com/api/webforms/v1.1" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" + +# Get required variables from .\config\settings.json file +$config = Get-Content $configPath -Raw | ConvertFrom-Json +$signerName = $config.SIGNER_NAME +$signerEmail = $config.SIGNER_EMAIL + +$accessToken = Get-Content $tokenPath +$accountId = Get-Content $accountIdPath + +# Create template for the Web Form from the API +Invoke-Script -Command "`".\utils\createWebFormTemplate.ps1`"" + +$templateId = Get-Content -Path ".\config\WEB_FORM_TEMPLATE_ID" + +$webFormConfig = Get-Content -Raw demo_documents\web-form-config.json +$result = $webFormConfig -replace "template-id", $templateId +$result | Set-Content -Path demo_documents\web-form-config.json + +Write-Host "" +Write-Host "Go to your Docusign account to create the Web Form. Go to 'Templates' in your developer account, select 'Start,' select 'Web Forms,' and choose 'Upload Web Form.' Upload the JSON config file 'web-form-config.json' found under the demo_documents folder of this project. You will need to activate the web form before proceeding. Press any key to continue after doing so." +$choice = Read-Host + +#ds-snippet-start:WebForms2Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +$headers.add("Accept", "application/json") +#ds-snippet-end:WebForms2Step2 + +# List web forms in account that match the name of the web form we just created +#ds-snippet-start:WebForms2Step3 +$response = New-TemporaryFile +Invoke-RestMethod ` + -Uri "${apiUri}/accounts/${accountId}/forms?search=Web%20Form%20Example%20Template" ` + -Method 'GET ' ` + -Headers $headers ` + -OutFile $response + +$formId = $(Get-Content $response | ConvertFrom-Json).items[0].id +#ds-snippet-end:WebForms2Step3 + +#ds-snippet-start:WebForms2Step4 +$json = @" +{ + "sendOption": "now", + "formValues": { + "PhoneNumber": "555-555-5555", + "Yes": ["Yes"], + "Company": "Tally", + "JobTitle": "Programmer Writer" + }, + "recipients": [ + { + "roleName": "signer", + "name": "$signerName", + "email": "$signerEmail" + } + ] +} +"@ +#ds-snippet-end:WebForms2Step4 + +#ds-snippet-start:WebForms2Step5 +Invoke-RestMethod ` + -Uri "${apiUri}/accounts/${accountId}/forms/${formId}/instances" ` + -Method 'POST' ` + -Headers $headers ` + -Body $json ` + -OutFile $response + +$responseContent = $(Get-Content $response | ConvertFrom-Json) +#ds-snippet-end:WebForms2Step5 + +Write-Host "" +Write-Host "Creating a new remote instance of the web form..." +Write-Host "" +Write-Host "Response:" +Write-Host $responseContent + +Write-Output "" +Write-Output "Done." diff --git a/examples/Workspaces/eg001CreateWorkspace.ps1 b/examples/Workspaces/eg001CreateWorkspace.ps1 new file mode 100644 index 0000000..46c0d9f --- /dev/null +++ b/examples/Workspaces/eg001CreateWorkspace.ps1 @@ -0,0 +1,54 @@ +$apiUri = "https://api-d.docusign.com/v1" + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +$workspaceName = Read-Host "Enter the name for the new workspace" + +#ds-snippet-start:Workspaces1Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Accept' = 'application/json'; + 'Content-Type' = 'application/json'; +} +#ds-snippet-end:Workspaces1Step2 + +try { + # Create the workspace definition + #ds-snippet-start:Workspaces1Step3 + $body = @{ + name = $workspaceName; + } | ConvertTo-Json + #ds-snippet-end:Workspaces1Step3 + + #ds-snippet-start:Workspaces1Step4 + $response = $(Invoke-WebRequest ` + -Uri "${apiUri}/accounts/${accountId}/workspaces" ` + -Method 'POST' ` + -headers $headers ` + -body $body) + #ds-snippet-end:Workspaces1Step4 +} catch { + Write-Output "Failed to create Workspace." + Write-Output $_ + exit 0 +} + +Write-Output "Response: $response" + +# pull out the workspaceId +$workspaceId = $($response.Content | ConvertFrom-Json).workspace_id +$workspaceCreatorId = $($response.Content | ConvertFrom-Json).created_by_user_id + +# Save the envelope id for use by other scripts +Write-Output "Workspace created! ID: $workspaceId" +Write-Output $workspaceId > .\config\WORKSPACE_ID +Write-Output $workspaceName > .\config\WORKSPACE_NAME +Write-Output $workspaceCreatorId > .\config\WORKSPACE_CREATOR_ID + +Write-Output "Done." diff --git a/examples/Workspaces/eg002AddDocumentToWorkspace.ps1 b/examples/Workspaces/eg002AddDocumentToWorkspace.ps1 new file mode 100644 index 0000000..0170b05 --- /dev/null +++ b/examples/Workspaces/eg002AddDocumentToWorkspace.ps1 @@ -0,0 +1,134 @@ +$apiUri = "https://api-d.docusign.com/v1" + +# check that a workspace exists +$workspaceId = Get-Content .\config\WORKSPACE_ID +$workspaceName = Get-Content .\config\WORKSPACE_NAME +if ([string]::IsNullOrWhiteSpace($workspaceId)) { + Write-Host "Please create a workspace before running this example" + exit 0 +} + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# temp files: +$requestData = New-TemporaryFile + +#ds-snippet-start:Workspaces2Step2 +$boundary = [System.Guid]::NewGuid().ToString() +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "multipart/form-data; boundary=${boundary}"; +} +#ds-snippet-end:Workspaces2Step2 + +# Get the current script directory +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition + +# Resolve the demo_documents directory (two levels up) +$DemoDocsPath = Resolve-Path (Join-Path $ScriptDir '..\..\demo_documents') + +Write-Host "" +Write-Host "Enter the PDF file name (e.g. World_Wide_Corp_Web_Form.pdf) from the $DemoDocsPath folder:" +Write-Host "" + +# Ask for the file until valid +while ($true) { + $FileName = Read-Host + $FilePath = Join-Path $DemoDocsPath $FileName + + if ($FileName -notmatch '\.pdf$') { + Write-Host "" + Write-Host "The file must be a PDF (must end with .pdf). Please try again." + Write-Host "" + continue + } + + if (-not (Test-Path $FilePath)) { + Write-Host "" + Write-Host "File not found in demo_documents folder. Please try again." + Write-Host "" + continue + } + + break +} + +# Ask for document name to be used in the workspace +Write-Host "" +Write-Host "Enter the name for the document in the workspace (must end with .pdf):" +Write-Host "" + +while ($true) { + $DocName = Read-Host + $DocName = $DocName.Trim() + + if ($DocName -match '\.pdf$') { + break + } else { + Write-Host "" + Write-Host "Invalid name. The document name must end with '.pdf' (e.g., example.pdf)." + Write-Host "Please try again:" + Write-Host "" + } +} + +try { + #ds-snippet-start:Workspaces2Step3 + # Create a temporary copy with the desired document name + $tempFilePath = Join-Path ([System.IO.Path]::GetTempPath()) $docName + Copy-Item -Path $filePath -Destination $tempFilePath -Force + + $boundary = [System.Guid]::NewGuid().ToString() + $LF = "`r`n" + $fileBytes = [System.IO.File]::ReadAllBytes($tempFilePath) + $fileContent = [System.Text.Encoding]::GetEncoding("iso-8859-1").GetString($fileBytes) + + # Construct the multipart form body + $bodyLines = @( + "--$boundary", + "Content-Disposition: form-data; name=`"file`"; filename=`"$docName`"", + "Content-Type: application/octet-stream$LF", + $fileContent, + "--$boundary", + "Content-Disposition: form-data; name=`"name`"$LF", + $docName, + "--$boundary--$LF" + ) + $body = $bodyLines -join $LF + #ds-snippet-end:Workspaces2Step3 + + Remove-Item $tempFilePath -Force + + #ds-snippet-start:Workspaces2Step4 + $response = Invoke-WebRequest ` + -Uri "${apiUri}/accounts/${accountId}/workspaces/${workspaceId}/documents" ` + -Method 'POST' ` + -Headers $headers ` + -ContentType "multipart/form-data; boundary=$boundary" ` + -Body $body + #ds-snippet-end:Workspaces2Step4 +} catch { + Write-Output "Failed to add document to workspace." + Write-Output $_ + exit 0 +} + +Write-Output "Response: $response" + +# pull out the documentId +$documentId = $($response.Content | ConvertFrom-Json).document_id + +# Save the document id for use by other scripts +Write-Output "Document added to the workspace '$workspaceName'!! ID: $documentId" +Write-Output $documentId > .\config\DOCUMENT_ID + +# cleanup +Remove-Item $requestData + +Write-Output "Done." diff --git a/examples/Workspaces/eg003SendEnvelopeWithRecipientInfo.ps1 b/examples/Workspaces/eg003SendEnvelopeWithRecipientInfo.ps1 new file mode 100644 index 0000000..011037d --- /dev/null +++ b/examples/Workspaces/eg003SendEnvelopeWithRecipientInfo.ps1 @@ -0,0 +1,110 @@ +$apiUri = "https://api-d.docusign.com/v1" + +# check that a workspace exists +$workspaceId = Get-Content .\config\WORKSPACE_ID +if ([string]::IsNullOrWhiteSpace($workspaceId)) { + Write-Host "Please create a workspace before running this example" + exit 0 +} + +# check that a document exists in the workspace +$documentId = Get-Content .\config\DOCUMENT_ID +if ([string]::IsNullOrWhiteSpace($documentId)) { + Write-Host "Please create a document in the workspace before running this example" + exit 0 +} + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +#ds-snippet-start:Workspaces3Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Accept' = 'application/json'; + 'Content-Type' = "application/json"; +} +#ds-snippet-end:Workspaces3Step2 + +try { + # Create the workspace envelope definition + #ds-snippet-start:Workspaces3Step3 + $body = @{ + "envelope_name" = "Example Workspace Envelope"; + "document_ids" = @("${documentId}") + } | ConvertTo-Json + #ds-snippet-end:Workspaces3Step3 + + #ds-snippet-start:Workspaces3Step4 + $response = $(Invoke-WebRequest ` + -Uri "${apiUri}/accounts/${accountId}/workspaces/${workspaceId}/envelopes" ` + -Method 'POST' ` + -headers $headers ` + -body $body) + #ds-snippet-end:Workspaces3Step4 +} catch { + Write-Output "Failed to send envelope." + Write-Output $_ + exit 0 +} + +Write-Output "Response: $response" + +# pull out the envelopeId +$envelopeId = $($response.Content | ConvertFrom-Json).envelope_id +Write-Output "Envelope created! ID: $envelopeId" + +# Set the eSignature REST API base path +$apiUri = "https://demo.docusign.net/restapi" + +#ds-snippet-start:Workspaces3Step5 +$body = @{ + emailSubject = "Please sign this document"; + recipients = @{ + signers = @( + @{ + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "1"; + routingOrder = "1"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 +#ds-snippet-end:Workspaces3Step5 + +try { + #ds-snippet-start:Workspaces3Step6 + $response = $(Invoke-WebRequest ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}" ` + -Method 'PUT' ` + -headers $headers ` + -body $body) + #ds-snippet-end:Workspaces3Step6 +} catch { + Write-Output "Failed to send envelope." + Write-Output $_ + exit 0 +} + +Write-Output "Response: $response" +Write-Output "Envelope Sent!" +Write-Output "Done." diff --git a/examples/Workspaces/eg004CreateWorkspaceWithBrand.ps1 b/examples/Workspaces/eg004CreateWorkspaceWithBrand.ps1 new file mode 100644 index 0000000..ded1364 --- /dev/null +++ b/examples/Workspaces/eg004CreateWorkspaceWithBrand.ps1 @@ -0,0 +1,69 @@ +. "utils/invokeScript.ps1" + +#ds-snippet-start:Workspaces4Step2 +# check that a brand exists +$path = ".\config\BRAND_ID" +if (-not (Test-Path $path) -or [string]::IsNullOrWhiteSpace((Get-Content $path))) { + Write-Host "No brand_id found. Attempting to run eg028CreatingABrand.ps1..." + Invoke-Script -Command "`"./examples/eSignature/eg028CreatingABrand.ps1`"" +} + +# re-check after attempt +$brandId = Get-Content .\config\BRAND_ID +if ([string]::IsNullOrWhiteSpace($brandId)) { + Write-Host "Brand creation did not produce a brand_id. Please create a brand first." + exit 1 +} +#ds-snippet-end:Workspaces4Step2 + +$apiUri = "https://api-d.docusign.com/v1" + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +#ds-snippet-start:Workspaces4Step3 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Accept' = 'application/json'; + 'Content-Type' = 'application/json'; +} +#ds-snippet-end:Workspaces4Step3 + +try { + # Create the workspace definition + #ds-snippet-start:Workspaces4Step4 + $body = @{ + name = "Example workspace"; + brand_id = "$brandId" + } | ConvertTo-Json + #ds-snippet-end:Workspaces4Step4 + + #ds-snippet-start:Workspaces4Step5 + $response = $(Invoke-WebRequest ` + -Uri "${apiUri}/accounts/${accountId}/workspaces" ` + -Method 'POST' ` + -headers $headers ` + -body $body) + #ds-snippet-end:Workspaces4Step5 +} catch { + Write-Output "Failed to create Workspace." + Write-Output $_ + exit 0 +} + +Write-Output "Response: $response" + +# pull out the workspaceId +$workspaceId = $($response.Content | ConvertFrom-Json).workspace_id + +# Save the envelope id for use by other scripts +Write-Output "Workspace created! ID: $workspaceId" +Write-Output "Brand used: $brandId" +Write-Output $workspaceId > .\config\WORKSPACE_ID + +Write-Output "Done." diff --git a/examples/Workspaces/eg005CreateUploadRequest.ps1 b/examples/Workspaces/eg005CreateUploadRequest.ps1 new file mode 100644 index 0000000..38a0f6f --- /dev/null +++ b/examples/Workspaces/eg005CreateUploadRequest.ps1 @@ -0,0 +1,82 @@ +$apiUri = "https://api-d.docusign.com/v1" + +# Check that a workspace exists +$workspaceId = Get-Content .\config\WORKSPACE_ID +if ([string]::IsNullOrWhiteSpace($workspaceId)) { + Write-Host "Please create a workspace before running this example" + exit 0 +} + +# Check that a workspace creator ID exists +$workspaceCreatorId = Get-Content .\config\WORKSPACE_CREATOR_ID +if ([string]::IsNullOrWhiteSpace($workspaceCreatorId)) { + Write-Host "No creator ID was recorded. Please run the Create Workspace example before running this code" + exit 0 +} + +# Get required variables from .\config\settings.json: +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# Calculate ISO 8601 date 7 days from now (UTC) +$dueDate = (Get-Date).ToUniversalTime().AddDays(7).ToString("yyyy-MM-ddTHH:mm:ssZ") + +#ds-snippet-start:Workspaces5Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Accept' = 'application/json'; + 'Content-Type' = 'application/json'; +} +#ds-snippet-end:Workspaces5Step2 + +try { + # Create the workspace definition + #ds-snippet-start:Workspaces5Step3 + $body = @{ + name = "Upload Request example $dueDate"; + description = 'This is an example upload request created via the workspaces API'; + status = 'draft'; + due_date = $dueDate; + assignments = @( + @{ + upload_request_responsibility_type_id = 'assignee'; + first_name = 'Test'; + last_name = 'User'; + email = $variables.SIGNER_EMAIL; + }; + @{ + assignee_user_id = "$workspaceCreatorId"; + upload_request_responsibility_type_id = 'watcher'; + }; + ); + } | ConvertTo-Json + #ds-snippet-end:Workspaces5Step3 + + #ds-snippet-start:Workspaces5Step4 + $response = $(Invoke-WebRequest ` + -Uri "${apiUri}/accounts/${accountId}/workspaces/${workspaceId}/upload-requests" ` + -Method 'POST' ` + -headers $headers ` + -body $body) + #ds-snippet-end:Workspaces5Step4 +} catch { + Write-Output "Failed to create Workspace upload request." + Write-Output $_ + exit 0 +} + +Write-Output "Response: $response" + +# pull out the workspaceId +$uploadRequestId = $($response.Content | ConvertFrom-Json).upload_request_id + +Write-Output "Workspace upload request created! ID: $uploadRequestId" + +Write-Output "Done." diff --git a/examples/eSignature/eg002SigningViaEmail.ps1 b/examples/eSignature/eg002SigningViaEmail.ps1 index 484beb3..00107b0 100644 --- a/examples/eSignature/eg002SigningViaEmail.ps1 +++ b/examples/eSignature/eg002SigningViaEmail.ps1 @@ -6,7 +6,7 @@ $apiUri = "https://demo.docusign.net/restapi" $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json -# Step 1: Obtain your OAuth token +# Obtain your OAuth token # Note: Substitute these values with your own $accessToken = Get-Content .\config\ds_access_token.txt @@ -14,7 +14,6 @@ $accessToken = Get-Content .\config\ds_access_token.txt # Note: Substitute these values with your own $accountId = Get-Content .\config\API_ACCOUNT_ID -# ***DS.snippet.0.start # document 1 (html) has tag **signature_1** # document 2 (docx) has tag /sn1/ # document 3 (pdf) has tag /sn1/ @@ -38,11 +37,12 @@ $doc3Base64 = New-TemporaryFile [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx"))) > $doc2Base64 [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $doc3Base64 -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." Write-Output "The envelope has three documents. Processing time will be about 15 seconds." Write-Output "Results:" # Concatenate the different parts of the request +#ds-snippet-start:eSign2Step2 @{ emailSubject = "Please sign this document set"; documents = @( @@ -100,8 +100,10 @@ Write-Output "Results:" }; status = "sent"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign2Step2 -# Step 3. Create and send the envelope +# Create and send the envelope +#ds-snippet-start:eSign2Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` @@ -111,13 +113,13 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign2Step3 Write-Output "Response: $(Get-Content -Raw $response)" # pull out the envelopeId $envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId -# ***DS.snippet.0.end # Save the envelope id for use by other scripts Write-Output "EnvelopeId: $envelopeId" Write-Output $envelopeId > .\config\ENVELOPE_ID diff --git a/examples/eSignature/eg003ListEnvelopes.ps1 b/examples/eSignature/eg003ListEnvelopes.ps1 index 4208133..bcbed9b 100644 --- a/examples/eSignature/eg003ListEnvelopes.ps1 +++ b/examples/eSignature/eg003ListEnvelopes.ps1 @@ -8,16 +8,17 @@ $apiUri = "https://demo.docusign.net/restapi" $accessToken = Get-Content .\config\ds_access_token.txt # Step 2. List envelope status +#ds-snippet-start:eSign3Step2 # Obtain your accountId from demo.docusign.net -- the account id is shown in # the drop down on the upper right corner of the screen by your picture or # the default picture. $accountID = Get-Content .\config\API_ACCOUNT_ID -Write-Output "Sending the list envelope status request to DocuSign..." +Write-Output "Sending the list envelope status request to Docusign..." Write-Output "Results:" # Get date in the ISO 8601 format -$fromDate = ((Get-Date).AddDays(-10d)).ToString("yyyy-MM-ddThh:mm:ssK") +$fromDate = ((Get-Date).AddDays(-10d)).ToString("yyyy-MM-ddTHH:mm:ssK") $(Invoke-RestMethod ` @@ -28,5 +29,5 @@ $(Invoke-RestMethod ` 'Content-Type' = "application/json"; } ` -Body @{ "from_date" = ${fromDate} }).envelopes - -Write-Output "Done..." +#ds-snippet-end:eSign3Step2 +Write-Output "Done." diff --git a/examples/eSignature/eg004EnvelopeInfo.ps1 b/examples/eSignature/eg004EnvelopeInfo.ps1 index 0da30d7..c61d9a1 100644 --- a/examples/eSignature/eg004EnvelopeInfo.ps1 +++ b/examples/eSignature/eg004EnvelopeInfo.ps1 @@ -25,10 +25,10 @@ else { exit 1 } -Write-Output "Sending the Envelopes::get request to DocuSign..." +Write-Output "Sending the Envelopes::get request to Docusign..." Write-Output "Results:" -# Step 2. Get envelope data +#ds-snippet-start:eSign4Step2 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}" ` -Method 'GET' ` @@ -36,6 +36,6 @@ Invoke-RestMethod ` 'Authorization' = "Bearer $accessToken"; 'Content-Type' = "application/json"; } -# ***DS.snippet.0.end +#ds-snippet-end:eSign4Step2 Write-Output "Done." diff --git a/examples/eSignature/eg005EnvelopeRecipients.ps1 b/examples/eSignature/eg005EnvelopeRecipients.ps1 index 9d321bc..21802f1 100644 --- a/examples/eSignature/eg005EnvelopeRecipients.ps1 +++ b/examples/eSignature/eg005EnvelopeRecipients.ps1 @@ -25,12 +25,11 @@ else { exit 1 } -Write-Output "Sending the EnvelopeRecipients::list request to DocuSign..." +Write-Output "Sending the EnvelopeRecipients::list request to Docusign..." Write-Output "Results:" -# Step 2. List envelope recipients -# ***DS.snippet.0.start +#ds-snippet-start:eSign5Step2 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/recipients" ` -Method 'GET' ` @@ -38,6 +37,6 @@ Invoke-RestMethod ` 'Authorization' = "Bearer $accessToken"; 'Content-Type' = "application/json"; } -# ***DS.snippet.0.end +#ds-snippet-end:eSign5Step2 Write-Output "Done." diff --git a/examples/eSignature/eg006EnvelopeDocs.ps1 b/examples/eSignature/eg006EnvelopeDocs.ps1 index b74f59d..0d124ea 100644 --- a/examples/eSignature/eg006EnvelopeDocs.ps1 +++ b/examples/eSignature/eg006EnvelopeDocs.ps1 @@ -6,7 +6,7 @@ $apiUri = "https://demo.docusign.net/restapi" # can be manually created. # ***DS.snippet.0.start -# Step 1. Obtain your Oauth access token +# Obtain your Oauth access token $accessToken = Get-Content .\config\ds_access_token.txt # Obtain your accountId from demo.docusign.net -- the account id is shown in @@ -23,17 +23,22 @@ else { exit 1 } -Write-Output "Sending the EnvelopeDocuments::list request to DocuSign..." +#ds-snippet-start:eSign6Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} +#ds-snippet-end:eSign6Step2 + +Write-Output "Sending the EnvelopeDocuments::list request to Docusign..." Write-Output "Results:" -# Step 2. List envelope documents +# List envelope documents +#ds-snippet-start:eSign6Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/documents" ` -Method 'GET' ` - -Headers @{ - 'Authorization' = "Bearer $accessToken"; - 'Content-Type' = "application/json"; -} -# ***DS.snippet.0.end + -Headers $headers | ConvertTo-Json +#ds-snippet-end:eSign6Step3 Write-Output "Done." diff --git a/examples/eSignature/eg007EnvelopeGetDoc.ps1 b/examples/eSignature/eg007EnvelopeGetDoc.ps1 index 50297f5..7cefd0b 100644 --- a/examples/eSignature/eg007EnvelopeGetDoc.ps1 +++ b/examples/eSignature/eg007EnvelopeGetDoc.ps1 @@ -6,7 +6,7 @@ $apiUri = "https://demo.docusign.net/restapi" # can be manually created. -# Step 1. Obtain your Oauth access token +# Obtain your Oauth access token $accessToken = Get-Content .\config\ds_access_token.txt # Obtain your accountId from demo.docusign.net -- the account id is shown in @@ -22,10 +22,17 @@ if (Test-Path .\config\ENVELOPE_ID) { $envelopeId = Get-Content .\config\ENVELOPE_ID } else { - Write-Output "An envelope id is needed. Fix: execute step 2 - Signing_Via_Email" + Write-Output "PROBLEM: An envelope id is needed. Fix: execute code example 2 - Signing_Via_Email" exit 1 } +#ds-snippet-start:eSign7Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } +#ds-snippet-end:eSign7Step2 + $docChoice = "1" $outputFileExtension = "pdf" @@ -36,17 +43,19 @@ Enum listDocs { CertificateOfCompletion = 4; DocumentsCombinedTogether = 5; ZIPfile = 6; + PDFPortfolio = 7; } $listDocsView = $null; do { - Write-Output 'Select the initial sending view: ' + Write-Output 'Select a document or document set to download:' Write-Output "$([int][listDocs]::Document1) - Document 1" Write-Output "$([int][listDocs]::Document2) - Document 2" Write-Output "$([int][listDocs]::Document3) - Document 3" Write-Output "$([int][listDocs]::CertificateOfCompletion) - Certificate of Completion" Write-Output "$([int][listDocs]::DocumentsCombinedTogether) - Documents combined together" Write-Output "$([int][listDocs]::ZIPfile) - ZIP file" + Write-Output "$([int][listDocs]::PDFPortfolio) - PDF Portfolio" [int]$listDocsView = Read-Host "Please make a selection" } while (-not [listDocs]::IsDefined([listDocs], $listDocsView)); @@ -62,21 +71,22 @@ elseif ($listDocsView -eq [listDocs]::ZIPfile) { $docChoice = "archive" $outputFileExtension = "zip" } +elseif ($listDocsView -eq [listDocs]::PDFPortfolio) { + $docChoice = "portfolio" + $outputFileExtension = "pdf" +} else { $docChoice = $listDocsView } -Write-Output "Sending the EnvelopeDocuments::get request to DocuSign..." -# ***DS.snippet.0.start -# Step 3. Call the eSignature API +Write-Output "Sending the EnvelopeDocuments::get request to Docusign..." +# Call the eSignature API +#ds-snippet-start:eSign7Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/documents/${docChoice}" ` -Method 'GET' ` - -Headers @{ - 'Authorization' = "Bearer $accessToken"; - 'Content-Type' = "application/json"; -} ` + -Headers $headers ` -OutFile ${outputFile}${outputFileExtension} -# ***DS.snippet.0.end +#ds-snippet-end:eSign7Step3 Write-Output "The document(s) are stored in file ${outputFile}${outputFileExtension}" -Write-Output "Done." \ No newline at end of file +Write-Output "Done." diff --git a/examples/eSignature/eg008CreateTemplate.ps1 b/examples/eSignature/eg008CreateTemplate.ps1 index 6b41ec5..0603e3b 100644 --- a/examples/eSignature/eg008CreateTemplate.ps1 +++ b/examples/eSignature/eg008CreateTemplate.ps1 @@ -16,7 +16,7 @@ $accountId = Get-Content .\config\API_ACCOUNT_ID # List the account's templates Write-Output "Checking to see if the template already exists in your account..." -$templateName = "Example Signer and CC template" +$templateName = "Example Signer and CC template v2" $response = New-TemporaryFile Invoke-RestMethod ` @@ -30,13 +30,14 @@ Invoke-RestMethod ` -OutFile $response # pull out the templateId if it was returned -$templateId = $(Get-Content $response | ConvertFrom-Json).envelopeTemplates.templateId +$templateIds = $(Get-Content $response | ConvertFrom-Json).envelopeTemplates.templateId -Write-Output "Did we find any templateIds?: $templateId" +Write-Output "Did we find any templateIds?: $templateIds" -if (-not ([string]::IsNullOrEmpty($templateId))) { +if (-not ([string]::IsNullOrEmpty($templateIds))) { Write-Output "Your account already includes the '${templateName}' template." # Save the template id for use by other scripts + $templateId = $templateIds -split ' ' | Select-Object -First 1 Write-Output "${templateId}" > .\config\TEMPLATE_ID Remove-Item $response Write-Output "Done." @@ -56,13 +57,17 @@ $requestData = New-TemporaryFile $requestDataTemp = New-TemporaryFile $doc1Base64 = New-TemporaryFile -Write-Output "Sending the template create request to DocuSign..." +Write-Output "Sending the template create request to Docusign..." # Fetch document and encode [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_fields.pdf"))) > $doc1Base64 # Concatenate the different parts of the request +#ds-snippet-start:eSign8Step2 @{ + description = "Example template created via the eSignature API"; + name = "Example Signer and CC template v2"; + shared = "false"; documents = @( @{ documentBase64 = "$(Get-Content $doc1Base64)"; @@ -72,11 +77,6 @@ Write-Output "Sending the template create request to DocuSign..." }; ); emailSubject = "Please sign this document"; - envelopeTemplateDefinition = @{ - description = "Example template created via the API"; - name = "Example Signer and CC template"; - shared = "false"; - }; recipients = @{ carbonCopies = @( @{recipientId = "2"; roleName = "cc"; routingOrder = "2"; }; @@ -161,12 +161,15 @@ Write-Output "Sending the template create request to DocuSign..." tabLabel = "text"; width = 84; xPosition = "153"; yPosition = "230"; }; + ); + numericalTabs = @( @{ + ValidationType = "Currency"; documentId = "1"; font = "helvetica"; fontSize = "size14"; height = 23; pageNumber = "1"; required = "false"; - tabLabel = "numbersOnly"; width = 84; - xPosition = "153"; yPosition = "260"; + tabLabel = "numericalCurrency"; width = 84; + xPosition = "153"; yPosition = "230"; }; ); }; @@ -175,7 +178,9 @@ Write-Output "Sending the template create request to DocuSign..." }; status = "created"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign8Step2 +#ds-snippet-start:eSign8Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/templates" ` -Method 'POST' ` @@ -185,6 +190,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign8Step3 Write-Output "Results:" Get-Content $response diff --git a/examples/eSignature/eg009UseTemplate.ps1 b/examples/eSignature/eg009UseTemplate.ps1 index 2c3b65f..d3c2dc5 100644 --- a/examples/eSignature/eg009UseTemplate.ps1 +++ b/examples/eSignature/eg009UseTemplate.ps1 @@ -2,7 +2,7 @@ $apiUri = "https://demo.docusign.net/restapi" -# Send a signing request via email using a DocuSign template +# Send a signing request via email using a Docusign template # Get required environment variables from .\config\settings.json file $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json @@ -22,14 +22,14 @@ if (-not (Test-Path .\config\TEMPLATE_ID)) { exit 0 } -# ***DS.snippet.0.start # Step 2. Create the envelope definition from a template # temp files: $response = New-TemporaryFile $requestData = New-TemporaryFile -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." +#ds-snippet-start:eSign9Step2 @{ templateId = "$(Get-Content .\config\TEMPLATE_ID)"; templateRoles = @( @@ -46,8 +46,10 @@ Write-Output "Sending the envelope request to DocuSign..." ); status = "sent"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign9Step2 # Step 3. Create and send the envelope +#ds-snippet-start:eSign9Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` @@ -57,6 +59,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign9Step3 Write-Output "Response:" diff --git a/examples/eSignature/eg010SendBinaryDocs.ps1 b/examples/eSignature/eg010SendBinaryDocs.ps1 index d5b94f5..18fb0cc 100644 --- a/examples/eSignature/eg010SendBinaryDocs.ps1 +++ b/examples/eSignature/eg010SendBinaryDocs.ps1 @@ -1,3 +1,4 @@ +#ds-snippet-start:eSign10Step3 function Add-OemContent { param( $destination, @@ -5,16 +6,16 @@ function Add-OemContent { ) Add-Content -Path $destination -Value $content -Encoding oem -NoNewline } +#ds-snippet-end:eSign10Step3 # Configuration -# 1. Get required variables from .\config\settings.json: +# Get required variables from .\config\settings.json: $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json $CC_EMAIL = $variables.CC_EMAIL $CC_NAME = $variables.CC_NAME $SIGNER_EMAIL = $variables.SIGNER_EMAIL $SIGNER_NAME = $variables.SIGNER_NAME - -# Step 2. Obtain your OAuth access token +# Obtain your OAuth access token $accessToken = Get-Content ".\config\ds_access_token.txt" # Obtain your accountId from demo.docusign.net -- the account id is shown in @@ -22,9 +23,8 @@ $accessToken = Get-Content ".\config\ds_access_token.txt" # the default picture. $accountId = Get-Content ".\config\API_ACCOUNT_ID" -# ***DS.snippet.0.start - -# Step 3. Construct the request body +#ds-snippet-start:eSign10Step3 +# Construct the request body # document 1 (html) has tag **signature_1** # document 2 (docx) has tag /sn1/ # document 3 (pdf) has tag /sn1/ @@ -45,7 +45,7 @@ $doc1 = Get-Item ".\demo_documents\doc_1.html" $doc2 = Get-Item ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx" $doc3 = Get-Item ".\demo_documents\World_Wide_Corp_lorem.pdf" -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." Write-Output "The envelope has three documents. Processing time will be about 15 seconds." Write-Output "Results:" @@ -142,26 +142,32 @@ Add-OemContent $requestData "${CRLF}" # Add closing boundary Add-OemContent $requestData "--$boundary--" Add-OemContent $requestData "${CRLF}" +#ds-snippet-end:eSign10Step3 + +#ds-snippet-start:eSign10Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "multipart/form-data; boundary=${boundary}"; +} +#ds-snippet-end:eSign10Step2 # Send request try { - # Step 4. Call the eSignature REST API + # Call the eSignature REST API + #ds-snippet-start:eSign10Step4 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` - -Headers @{ - 'Authorization' = "Bearer $accessToken"; - 'Content-Type' = "multipart/form-data; boundary=${boundary}"; - } ` + -Headers $headers ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response Write-Output "Response: $(Get-Content -Raw $response)" + #ds-snippet-end:eSign10Step4 } catch { Write-Error $_ } -# ***DS.snippet.0.end Get-Content $response diff --git a/examples/eSignature/eg011EmbeddedSending.ps1 b/examples/eSignature/eg011EmbeddedSending.ps1 index 33b8c18..d992d70 100644 --- a/examples/eSignature/eg011EmbeddedSending.ps1 +++ b/examples/eSignature/eg011EmbeddedSending.ps1 @@ -2,7 +2,7 @@ $apiUri = "https://demo.docusign.net/restapi" # Use embedded sending: # 1. create a draft envelope with three documents -# 2. Open the sending view of the DocuSign web tool +# 2. Open the sending view of the Docusign web tool # Configuration # 1. Get required variables from .\config\settings.json: @@ -24,19 +24,19 @@ $accountId = Get-Content .\config\API_ACCOUNT_ID # The sending editor can be opened in either of two views: Enum ViewType { - TaggingView = 1; - RecipientsAndDocuments = 2; + Tagger = 1; + Prepare = 2; } $startingView = $null; do { Write-Output 'Select the initial sending view: ' - Write-Output "$([int][ViewType]::TaggingView) - Tagging view" - Write-Output "$([int][ViewType]::RecipientsAndDocuments) - Recipients and documents view" + Write-Output "$([int][ViewType]::Tagger) - Tagging view" + Write-Output "$([int][ViewType]::Prepare) - Prepare view" [int]$startingView = Read-Host "Please make a selection" } while (-not [ViewType]::IsDefined([ViewType], $startingView)); +[string]$startingView = [ViewType]::GetName([ViewType], $startingView) -# ***DS.snippet.0.start # Step 2. Create the envelope # Create the document request body @@ -53,19 +53,26 @@ do { # The envelope will be sent first to the signer. # After it is signed, a copy is sent to the cc person. +#ds-snippet-start:eSign11Step2 # temp files: $requestData = New-TemporaryFile +$senderViewRequestData = New-TemporaryFile $response = New-TemporaryFile $doc1Base64 = New-TemporaryFile $doc2Base64 = New-TemporaryFile $doc3Base64 = New-TemporaryFile # Fetch docs and encode -[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\doc_1.html"))) > $doc1Base64 +$doc1String = [System.IO.File]::ReadAllText((Resolve-Path ".\demo_documents\doc_1.html")) +$doc1String = $doc1String.Replace("{USER_EMAIL}", $SIGNER_EMAIL) +$doc1String = $doc1String.Replace("{USER_FULLNAME}", $SIGNER_NAME) +$doc1String = $doc1String.Replace("{CC_EMAIL}", $CC_EMAIL) +$doc1String = $doc1String.Replace("{CC_NAME}", $CC_NAME) +[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($doc1String)) > $doc1Base64 [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx"))) > $doc2Base64 [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $doc3Base64 -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." Write-Output "The envelope has three documents. Processing time will be about 15 seconds." Write-Output "Results:" @@ -136,8 +143,10 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign11Step2 # Step 3. Create the sender view +#ds-snippet-start:eSign11Step3 # pull out the envelopeId $envelop = $response | Get-Content | ConvertFrom-Json Write-Output "Envelope received: $envelop" @@ -145,10 +154,40 @@ $envelopeId = $envelop.envelopeId Write-Output "Requesting the sender view url" -# The returnUrl is normally your own web app. DocuSign will redirect +@{ + returnUrl = "http://httpbin.org/get"; + viewAccess = "envelope"; + settings = @{ + startingScreen = $startingView; + sendButtonAction = "send"; + showBackButton = "false"; + backButtonAction = "previousPage"; + showHeaderActions = "false"; + showDiscardAction = "false"; + lockToken = ""; + recipientSettings = @{ + showEditRecipients = "false"; + showContactsList = "false"; + }; + documentSettings = @{ + showEditDocuments = "false"; + showEditDocumentVisibility = "false"; + showEditPages = "false"; + }; + taggerSettings = @{ + paletteSections = "default"; + paletteDefault = "custom"; + }; + templateSettings = @{ + showMatchingTemplatesPrompt = "true"; + }; + }; +} | ConvertTo-Json -Depth 32 >> $senderViewRequestData + +# The returnUrl is normally your own web app. Docusign will redirect # the signer to returnUrl when the embedded sending completes. # For this example, we'll use http://httpbin.org/get to show the -# query parameters passed back from DocuSign +# query parameters passed back from Docusign Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/sender" ` -Method 'POST' ` @@ -156,17 +195,12 @@ Invoke-RestMethod ` 'Authorization' = "Bearer $accessToken"; 'Content-Type' = "application/json"; } ` - -Body (@{ returnUrl = "http://httpbin.org/get"; } | ConvertTo-Json) ` + -InFile (Resolve-Path $senderViewRequestData).Path ` -OutFile $response $sendingObj = $response | Get-Content | ConvertFrom-Json $sendingUrl = $sendingObj.url -# Next, we update the returned url if we want to start with the Recipient -# and Documents view -if ($startingView -eq [ViewType]::RecipientsAndDocuments) { - $sendingUrl = $sendingUrl -replace "send=1", "send=0" -} -# ***DS.snippet.0.end +#ds-snippet-end:eSign11Step3 Write-Output "The embedded sending URL is ${sendingUrl}" Write-Output "It is only valid for five minutes. Attempting to automatically open your browser..." @@ -175,6 +209,7 @@ Start-Process $sendingUrl # cleanup Remove-Item $requestData +Remove-Item $senderViewRequestData Remove-Item $response Remove-Item $doc1Base64 Remove-Item $doc2Base64 diff --git a/examples/eSignature/eg012EmbeddedConsole.ps1 b/examples/eSignature/eg012EmbeddedConsole.ps1 index 49f07b1..dcecf22 100644 --- a/examples/eSignature/eg012EmbeddedConsole.ps1 +++ b/examples/eSignature/eg012EmbeddedConsole.ps1 @@ -1,6 +1,6 @@ $apiUri = "https://demo.docusign.net/restapi" -# Redirect to the DocuSign console web tool +# Redirect to the Docusign console web tool # Step 1. Obtain your Oauth access token @@ -19,10 +19,10 @@ if (-not (Test-Path .\config\ENVELOPE_ID)) { # Check that we have an envelope id $envelopeId = Get-Content .\config\ENVELOPE_ID -# The returnUrl is normally your own web app. DocuSign will redirect +# The returnUrl is normally your own web app. Docusign will redirect # the signer to returnUrl when the embedded signing completes. # For this example, we'll use http://httpbin.org/get to show the -# query parameters passed back from DocuSign +# query parameters passed back from Docusign # The web tool console can be opened in either of two views: # The sending editor can be opened in either of two views: @@ -42,6 +42,7 @@ do { Write-Output "Requesting the console view url" +#ds-snippet-start:eSign12Step2 $requestBody = switch ($selectedView) { { [ViewType]::FrontPage } { @{ @@ -58,7 +59,6 @@ $requestBody = switch ($selectedView) { } $requestBody -# Step 2. Call the eSignature REST API $console = Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/views/console" ` -Method "POST" ` @@ -71,8 +71,8 @@ $console = Invoke-RestMethod ` Write-Output "Results:" Write-Output "Console received: $console" $consoleUrl = $console.url +#ds-snippet-end:eSign12Step2 -# ***DS.snippet.0.end Write-Output "The console URL is $consoleUrl" Write-Output "It is only valid for five minutes. Attempting to automatically open your browser..." Start-Process $consoleUrl diff --git a/examples/eSignature/eg013AddDocToTemplate.ps1 b/examples/eSignature/eg013AddDocToTemplate.ps1 index 1ea9854..0d0fde5 100644 --- a/examples/eSignature/eg013AddDocToTemplate.ps1 +++ b/examples/eSignature/eg013AddDocToTemplate.ps1 @@ -28,16 +28,16 @@ $requestData = New-TemporaryFile $response = New-TemporaryFile $doc1Base64 = New-TemporaryFile -# ***DS.snippet.0.start # Fetch docs and encode [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\added_document.html"))) > $doc1Base64 -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." Write-Output "A template is used, it has one document. A second document will be" Write-Output "added by using Composite Templates" # Concatenate the different parts of the request # document 1 (html) has tag **signature_1** +#ds-snippet-start:eSign13Step2 @{ compositeTemplates = @( @{ @@ -63,7 +63,7 @@ Write-Output "added by using Composite Templates" }; ); }; - sequence = "1"; + sequence = "2"; }; ); serverTemplates = @( @@ -111,14 +111,17 @@ Write-Output "added by using Composite Templates" }; ); }; - sequence = "2"; + sequence = "1"; }; ); }; ); status = "sent"; } | ConvertTo-Json -Depth 32 > $requestData -# Step 3. Call DocuSign to create the envelope +#ds-snippet-end:eSign13Step2 + +# Step 3. Call Docusign to create the envelope +#ds-snippet-start:eSign13Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` @@ -128,6 +131,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign13Step3 Write-Output "Results:" Get-Content $response @@ -139,10 +143,11 @@ Write-Output "EnvelopeId: $envelopeId" # Step 4. Create the recipient view definition # that the signer will directly open in their browser to sign. # -# The returnUrl is normally your own web app. DocuSign will redirect +# The returnUrl is normally your own web app. Docusign will redirect # the signer to returnUrl when the signing completes. # For this example, we'll use http://httpbin.org/get to show the -# query parameters passed back from DocuSign +# query parameters passed back from Docusign +#ds-snippet-start:eSign13Step4 Write-Output "Requesting the url for the embedded signing..." @{ @@ -167,7 +172,7 @@ Write-Output "Response:" Get-Content $response $signingUrl = $(Get-Content $response | ConvertFrom-Json).url -# ***DS.snippet.0.end +#ds-snippet-end:eSign13Step4 Write-Output "The embedded signing URL is $signingUrl" Write-Output "It is only valid for five minutes. Attempting to automatically open your browser..." diff --git a/examples/eSignature/eg014CollectPayment.ps1 b/examples/eSignature/eg014CollectPayment.ps1 index aabf743..6bb413d 100644 --- a/examples/eSignature/eg014CollectPayment.ps1 +++ b/examples/eSignature/eg014CollectPayment.ps1 @@ -13,12 +13,11 @@ $accessToken = Get-Content .\config\ds_access_token.txt # the default picture. $accountId = Get-Content .\config\API_ACCOUNT_ID -# Step 2. Log in to DocuSign Admin and from the top +# Step 2. Log in to Docusign Admin and from the top # navigation, select Admin. From there look # to the left under INTEGRATIONS and select # Payments to retrieve your Gateway account ID. -# ***DS.snippet.0.start # Step 3. Create the envelope definition @@ -30,9 +29,10 @@ $doc1Base64 = New-TemporaryFile # Fetch doc and encode [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\order_form.html"))) > $doc1Base64 -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." # Concatenate the different parts of the request +#ds-snippet-start:eSign14Step3 @{ emailSubject = "Please complete your order"; documents = @( @@ -159,8 +159,9 @@ Write-Output "Sending the envelope request to DocuSign..." }; status = "sent"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign14Step3 -# Step 4. Call the eSignature REST API +#ds-snippet-start:eSign14Step4 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` @@ -170,7 +171,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response -# ***DS.snippet.0.end +#ds-snippet-end:eSign14Step4 Write-Output "Results:" Get-Content $response diff --git a/examples/eSignature/eg015EnvelopeTabData.ps1 b/examples/eSignature/eg015EnvelopeTabData.ps1 index 7011c5e..a2c8213 100644 --- a/examples/eSignature/eg015EnvelopeTabData.ps1 +++ b/examples/eSignature/eg015EnvelopeTabData.ps1 @@ -20,18 +20,22 @@ else { } # Step 2. Create your authorization headers +#ds-snippet-start:eSign15Step2 $headers = @{ 'Authorization' = "Bearer $accessToken"; 'Accept' = "application/json"; 'Content-Type' = "application/json"; } +#ds-snippet-end:eSign15Step2 # Step 3. a) Make a GET call to the form_data endpoint to retrieve your envelope tab values # b) Display the JSON response +#ds-snippet-start:eSign15Step3 $result = $(Invoke-WebRequest ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/form_data" ` -Method 'GET' ` -Headers $headers) +#ds-snippet-end:eSign15Step3 if ( $result.StatusCode -gt 201) { Write-Output "Retrieving envelope form data has failed." diff --git a/examples/eSignature/eg016SetTabValues.ps1 b/examples/eSignature/eg016SetTabValues.ps1 index 9cdf58b..107e9d0 100644 --- a/examples/eSignature/eg016SetTabValues.ps1 +++ b/examples/eSignature/eg016SetTabValues.ps1 @@ -13,20 +13,29 @@ $accessToken = Get-Content .\config\ds_access_token.txt # Note: Substitute these values with your own $accountId = Get-Content .\config\API_ACCOUNT_ID +# Step 2. Create your authorization headers +#ds-snippet-start:eSign16Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} +#ds-snippet-end:eSign16Step2 + # Tabs and custom fields shown in the request body in step 4 -# Step 4. Construct the request body +# Step 3. Construct the request body # Temp files: $requestData = New-TemporaryFile $response = New-TemporaryFile $doc1Base64 = New-TemporaryFile -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." # Fetch doc and encode [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_salary.docx"))) > $doc1Base64 +#ds-snippet-start:eSign16Step3 @{ customFields = @{ textCustomFields = @(@{ @@ -61,7 +70,28 @@ Write-Output "Sending the envelope request to DocuSign..." anchorXOffset = "20"; anchorYOffset = "10"; }; ); - textTabs = @(@{ + numericalTabs = @(@{ + ValidationType = "Currency"; + XPosition = "210"; + YPosition = "235"; + Height = "20"; + Width = "70"; + PageNumber = "1"; + DocumentId = "1"; + MinNumericalValue = "0"; + MaxNumericalValue = "1000000"; + TabId = "salary"; + TabLabel = "Salary"; + NumericalValue = "123000"; + LocalPolicy = @{ + CultureName = "en-US"; + CurrencyCode = "usd"; + CurrencyPositiveFormat = "csym_1_comma_234_comma_567_period_89"; + CurrencyNegativeFormat = "minus_csym_1_comma_234_comma_567_period_89"; + UseLongCurrencyFormat = "true"; + }; + }; ); + textTabs = @(@{ anchorString = "/legal/"; anchorUnits = "pixels"; anchorXOffset = "5"; @@ -85,26 +115,16 @@ Write-Output "Sending the envelope request to DocuSign..." tabId = "familiar_name"; tabLabel = "Familiar name"; value = $variables.SIGNER_NAME; - }; @{ - anchorString = "/salary/"; - anchorUnits = "pixels"; - anchorXOffset = "5"; - anchorYOffset = "-9"; - bold = "true"; - font = "helvetica"; - fontSize = "size11"; - locked = "true"; - tabId = "salary"; - tabLabel = "Salary"; - value = "$123,000.00"; - }; ); + };); }; }; ); }; status = "Sent"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign16Step3 -# Step 5. Call the eSignature REST API +# Step 4. Call the eSignature REST API +#ds-snippet-start:eSign16Step4 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` @@ -114,6 +134,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign16Step4 Write-Output "Response:" Get-Content $response @@ -128,13 +149,14 @@ Write-Output $envelopeId > .\config\ENVELOPE_ID # Step 6. Create a recipient view (an embedded signing view) # that the signer will directly open in their browser to sign # -# The return URL is normally your own web app. DocuSign will redirect -# the signer to the return URL when the DocuSign signing completes. +# The return URL is normally your own web app. Docusign will redirect +# the signer to the return URL when the Docusign signing completes. # For this example, we'll use http://httpbin.org/get to show the -# query parameters passed back from DocuSign +# query parameters passed back from Docusign Write-Output "Requesting the url for the embedded signing..." +#ds-snippet-start:eSign16Step5 @{ returnUrl = "http://httpbin.org/get"; authenticationMethod = "none"; @@ -146,15 +168,13 @@ Write-Output "Requesting the url for the embedded signing..." Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient" ` -Method 'POST' ` - -Headers @{ - 'Authorization' = "Bearer $accessToken"; - 'Content-Type' = "application/json"; -} ` + -Headers $headers ` -InFile (Resolve-Path $requestData).Path` -OutFile $response Write-Output "Response:" Get-Content $response +#ds-snippet-end:eSign16Step5 $signingUrl = $(Get-Content $response | ConvertFrom-Json).url diff --git a/examples/eSignature/eg017SetTemplateTabValues.ps1 b/examples/eSignature/eg017SetTemplateTabValues.ps1 index d2271aa..2129d22 100644 --- a/examples/eSignature/eg017SetTemplateTabValues.ps1 +++ b/examples/eSignature/eg017SetTemplateTabValues.ps1 @@ -17,6 +17,13 @@ $accountId = Get-Content .\config\API_ACCOUNT_ID $requestData = New-TemporaryFile $response = New-TemporaryFile +#ds-snippet-start:eSign17Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } +#ds-snippet-end:eSign17Step2 + # Check that we have a template ID if (Test-Path .\config\TEMPLATE_ID) { $templateId = Get-Content .\config\TEMPLATE_ID @@ -26,17 +33,52 @@ else { exit 0 } -Write-Output "Sending the envelope request to DocuSign..." +Write-Output "Sending the envelope request to Docusign..." +# Step 3. Create tabs and custom fields +#ds-snippet-start:eSign17Step3 +$text_custom_fields = @{ + "name" = "app metadata item" + "required" = "false" + "show" = "true" + "value" = "1234567" +} + +$checkbox_tabs = @{ + "selected1" = "true" + "tabLabel1" = "ckAuthorization" + "selected2" = "true" + "tabLabel2" = "ckAgreement" +} + +$list_tabs = @{ + "documentId" = "1" + "pageNumber" = "1" + "tabLabel" = "list" + "value" = "green" +} + +$radio_tabs = @{ + "selected" = "true" + "value" = "white" +} + +$text_tabs = @{ + "tabLabel" = "text" + "value" = "Jabberywocky!" +} +#ds-snippet-end:eSign17Step3 + # Tabs and custom fields shown in the request body on step 4 # Step 4. Construct the request body +#ds-snippet-start:eSign17Step4 @{ customFields = @{ textCustomFields = @( @{ - name = "app metadata item"; - required = "false"; - show = "true"; - value = "1234567"; + name = $($text_custom_fields['name']); + required = $($text_custom_fields['required']); + show = $($text_custom_fields['show']); + value = $($text_custom_fields['value']); }; ); }; @@ -49,20 +91,20 @@ Write-Output "Sending the envelope request to DocuSign..." tabs = @{ checkboxTabs = @( @{ - selected = "true"; - tabLabel = "ckAuthorization"; + selected = $($checkbox_tabs['selected1']); + tabLabel = $($checkbox_tabs['tabLabel1']); }; @{ - selected = "true"; - tabLabel = "ckAgreement"; + selected = $($checkbox_tabs['selected2']); + tabLabel = $($checkbox_tabs['tabLabel2']); }; ); listTabs = @( @{ - documentId = "1"; - pageNumber = "1"; - tabLabel = "list"; - value = "green"; + documentId = $($list_tabs['documentId']); + pageNumber = $($list_tabs['pageNumber']); + tabLabel = $($list_tabs['tabLabel']); + value = $($list_tabs['value']); }; ); radioGroupTabs = @( @@ -70,16 +112,16 @@ Write-Output "Sending the envelope request to DocuSign..." groupName = "radio1"; radios = @( @{ - selected = "true"; - value = "white"; + selected = $($radio_tabs['selected']); + value = $($radio_tabs['value']); }; ); }; ); textTabs = @( @{ - tabLabel = "text"; - value = "Jabberywocky!"; + tabLabel = $($text_tabs['tabLabel']); + value = $($text_tabs['value']); }; @{ bold = "true"; @@ -109,17 +151,17 @@ Write-Output "Sending the envelope request to DocuSign..." status = "Sent"; templateId = "$templateId"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign17Step4 # Step 5. Call the eSignature REST API +#ds-snippet-start:eSign17Step5 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` - -Headers @{ - 'Authorization' = "Bearer $accessToken"; - 'Content-Type' = "application/json"; -} ` + -Headers $headers ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign17Step5 Write-Output "Response:" Get-Content $response @@ -131,13 +173,14 @@ Write-Output "EnvelopeId: $envelopeId" # Step 6. Create a recipient view (an embedded signing view) # that the signer will directly open in their browser to sign # -# The return URL is normally your own web app. DocuSign will redirect -# the signer to the return URL when the DocuSign signing completes. +# The return URL is normally your own web app. Docusign will redirect +# the signer to the return URL when the Docusign signing completes. # For this example, we'll use http://httpbin.org/get to show the -# query parameters passed back from DocuSign +# query parameters passed back from Docusign Write-Output "Requesting the url for the embedded signing..." +#ds-snippet-start:eSign17Step6 @{ returnUrl = "http://httpbin.org/get"; authenticationMethod = "none"; @@ -155,6 +198,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path` -OutFile $response +#ds-snippet-end:eSign17Step6 Write-Output "Response:" Get-Content $response diff --git a/examples/eSignature/eg018EnvelopeCustomFieldData.ps1 b/examples/eSignature/eg018EnvelopeCustomFieldData.ps1 index c5ae849..fb7a7dc 100644 --- a/examples/eSignature/eg018EnvelopeCustomFieldData.ps1 +++ b/examples/eSignature/eg018EnvelopeCustomFieldData.ps1 @@ -25,17 +25,24 @@ else { exit 1 } -Write-Output "Sending the EnvelopeCustomFields::list request to DocuSign..." +Write-Output "Sending the EnvelopeCustomFields::list request to Docusign..." + +# Step 2. Create your authorization headers +#ds-snippet-start:eSign18Step2 +$headers = @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} +#ds-snippet-end:eSign18Step2 # Step 3. Call the eSignature REST API +#ds-snippet-start:eSign18Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/custom_fields" ` -Method 'GET' ` - -Headers @{ - 'Authorization' = "Bearer $accessToken"; - 'Content-Type' = "application/json"; -} ` + -Headers $headers ` -OutFile $response +#ds-snippet-end:eSign18Step3 Write-Output "Results:" diff --git a/examples/eSignature/eg019SigningViaEmailWithAccessCode.ps1 b/examples/eSignature/eg019SigningViaEmailWithAccessCode.ps1 index e65e025..3ab04cf 100644 --- a/examples/eSignature/eg019SigningViaEmailWithAccessCode.ps1 +++ b/examples/eSignature/eg019SigningViaEmailWithAccessCode.ps1 @@ -1,9 +1,8 @@ # https://developers.docusign.com/docs/esign-rest-api/how-to/require-access-code-recipient/ -# Get required environment variables from .\config\settings.json file -$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json -$SIGNER_EMAIL = $variables.SIGNER_EMAIL -$SIGNER_NAME = $variables.SIGNER_NAME +# Get email and name from the user +$SIGNER_EMAIL = Read-Host "Please enter a signer email address: " +$SIGNER_NAME = Read-Host "Please enter a signer name: " # Get the envelope's custom field data # This script uses the envelope ID stored in ../envelope_id. @@ -20,10 +19,12 @@ $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Step 2. Construct your API headers # Construct your API headers +#ds-snippet-start:eSign19Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign19Step2 # Step 3. Construct the request body # temp files: @@ -35,6 +36,7 @@ $docBase64 = New-TemporaryFile $AccessCode = Read-Host "Please enter an access code for recipient authentication" # Construct your envelope JSON body +#ds-snippet-start:eSign19Step3 $body = @" { "documents": [{ @@ -77,6 +79,7 @@ $body = @" "status": "Sent" } "@ +#ds-snippet-end:eSign19Step3 Write-Output "" Write-Output "Request: " Write-Output $body @@ -84,6 +87,7 @@ Write-Output $body # Step 4. Call the eSignature REST API # a) Make a POST call to the createEnvelopes endpoint to create a new envelope. # b) Display the JSON structure of the created envelope +#ds-snippet-start:eSign19Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes" try { Write-Output "Response:" @@ -98,4 +102,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign19Step4 diff --git a/examples/eSignature/eg021SigningViaEmailWithPhoneAuthentication.ps1 b/examples/eSignature/eg020SigningViaEmailWithPhoneAuthentication.ps1 similarity index 57% rename from examples/eSignature/eg021SigningViaEmailWithPhoneAuthentication.ps1 rename to examples/eSignature/eg020SigningViaEmailWithPhoneAuthentication.ps1 index 2938a72..96734fd 100644 --- a/examples/eSignature/eg021SigningViaEmailWithPhoneAuthentication.ps1 +++ b/examples/eSignature/eg020SigningViaEmailWithPhoneAuthentication.ps1 @@ -2,8 +2,6 @@ # Get required environment variables from .\config\settings.json file $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json -$SIGNER_EMAIL = $variables.SIGNER_EMAIL -$SIGNER_NAME = $variables.SIGNER_NAME $PHONE_NUMBER = $variables.PHONE_NUMBER # Get the envelope's custom field data @@ -27,16 +25,51 @@ $docBase64 = New-TemporaryFile [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $docBase64 # Construct your API headers +#ds-snippet-start:eSign20Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign20Step2 +# - Obtain your workflow ID +#ds-snippet-start:eSign20Step3 +$uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/identity_verification" + +Write-Output "Attempting to retrieve your account's workflow ID" + +Write-Output "Response:" +$result = Invoke-RestMethod -uri $uri -headers $headers -method GET +$result.content +#Obtain the workflow ID from the API response +$workflowId = [System.Linq.Enumerable]::FirstOrDefault($result.identityVerification, [func[object, bool]] { param($x) $x.defaultName -eq "Phone Authentication"}).workflowId +#ds-snippet-end:eSign20Step3 + +if ($null -eq $workflowId) +{ + throw "Please contact https://support.docusign.com to enable recipient phone authentication in your account." +} + +$isDataIncorrect = $true +while($isDataIncorrect) +{ + $SIGNER_NAME = Read-Host "Please enter name for the signer" + $SIGNER_EMAIL = Read-Host "Please enter email address for the signer" + + if ($SIGNER_EMAIL -eq $variables.SIGNER_EMAIL) { + Write-Output "" + Write-Output "For recipient authentication you must specify a different recipient from the account owner (sender) in order to ensure recipient authentication is performed." + Write-Output "" + } else { + $isDataIncorrect = $false + } +} + +$SIGNER_COUNTRY_CODE = Read-Host "Please enter a country code for recipient authentication for the signer" +$SIGNER_PHONE_NUMBER = Read-Host "Please enter a phone number for recipient authentication for the signer" -$PHONE_NUMBER = $(Read-Host "Please enter a phone number for recipient authentication [415-555-1212]" -if ($PHONE_NUMBER) {$PHONE_NUMBER} else {'415-555-1212'} -) # Construct your envelope JSON body +#ds-snippet-start:eSign20Step4 $body = @" { "documents": [{ @@ -63,33 +96,39 @@ $body = @" "pageNumber": "1", "recipientId": "1", "tabLabel": "SignHereTab", - "xPosition": "75", - "yPosition": "572" + "xPosition": "200", + "yPosition": "160" }] }, "templateAccessCodeRequired": null, "deliveryMethod": "email", "recipientId": "1", - "phoneAuthentication": { - "recordVoicePrint": false, - "validateRecipProvidedNumber": false, - "recipMayProvideNumber": true, - "senderProvidedNumbers": ["$PHONE_NUMBER"] - }, - "smsAuthentication": null, - "idCheckConfigurationName": "Phone Auth $", - "requireIdLookup": true + "identityVerification":{ + "workflowId":"$workflowId", + "steps":null,"inputOptions":[ + {"name":"phone_number_list", + "valueType":"PhoneNumberList", + "phoneNumberList":[ + { + "countryCode":"$SIGNER_COUNTRY_CODE", + "number":"$SIGNER_PHONE_NUMBER" + } + ] + }] + } }] }, "status": "Sent" } "@ +#ds-snippet-end:eSign20Step4 Write-Output "" Write-Output "Request: " Write-Output $body # a) Make a POST call to the createEnvelopes endpoint to create a new envelope. # b) Display the JSON structure of the created envelope +#ds-snippet-start:eSign20Step5 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes" try { Write-Output "Response:" @@ -105,6 +144,7 @@ catch { Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line } +#ds-snippet-end:eSign20Step5 # cleanup -Remove-Item $docBase64 \ No newline at end of file +Remove-Item $docBase64 diff --git a/examples/eSignature/eg020SigningViaEmailWithSmsAuthentication.ps1 b/examples/eSignature/eg020SigningViaEmailWithSmsAuthentication.ps1 deleted file mode 100644 index a96f89b..0000000 --- a/examples/eSignature/eg020SigningViaEmailWithSmsAuthentication.ps1 +++ /dev/null @@ -1,94 +0,0 @@ -# https://developers.docusign.com/docs/esign-rest-api/how-to/sms-auth/ - -# Get required environment variables from .\config\settings.json file -$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json -$SIGNER_EMAIL = $variables.SIGNER_EMAIL -$SIGNER_NAME = $variables.SIGNER_NAME - -# Get the envelope's custom field data -# This script uses the envelope ID stored in ../envelope_id. -# The envelope_id file is created by example eg016SetTabValues.ps1 or -# can be manually created. - -# Step 1: Obtain your OAuth token -# Note: Substitute these values with your own -$oAuthAccessToken = Get-Content .\config\ds_access_token.txt - -#Set up variables for full code example -# Note: Substitute these values with your own -$APIAccountId = Get-Content .\config\API_ACCOUNT_ID - -# Construct your API headers -$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" -$headers.add("Authorization", "Bearer $oAuthAccessToken") -$headers.add("Accept", "application/json") -$headers.add("Content-Type", "application/json") - -# temp files: -$docBase64 = New-TemporaryFile - -# Fetch docs and encode -[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $docBase64 - -$PHONE_NUMBER = $(Read-Host "Please enter an SMS number for recipient authentication [415-555-1212]" -if ($PHONE_NUMBER){$PHONE_NUMBER} else {'415-555-1212'} -) -# Construct your envelope JSON body -$body = @" -{ - "documents": [{ - "documentBase64": "$(Get-Content $docBase64)", - "documentId": "1", - "fileExtension": "pdf", - "name": "Terms of Service" - }], - "emailBlurb": "Sample text for email body", - "emailSubject": "Please Sign", - "envelopeIdStamping": "true", - "recipients": { - "signers": [{ - "name": "$SIGNER_NAME", - "email": "$SIGNER_EMAIL", - "roleName": "", - "note": "", - "routingOrder": 3, - "status": "created", - "templateAccessCodeRequired": null, - "deliveryMethod": "email", - "recipientId": "1", - "accessCode": "", - "smsAuthentication": { - "senderProvidedNumbers": ["$PHONE_NUMBER"] - }, - "idCheckConfigurationName": "SMS Auth $", - "requireIdLookup": true - }] - }, - "status": "Sent" -} -"@ -Write-Output "" -Write-Output "Request: " -Write-Output $body - -$uri = "https://demo.docusign.net/restapi/v2.1/accounts/${APIAccountId}/envelopes" - -# a) Make a POST call to the createEnvelopes endpoint to create a new envelope. -# b) Display the JSON structure of the created envelope -try { - Write-Output "Response:" - $result = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST - $result.content -} -catch { - $int = 0 - foreach ($header in $_.Exception.Response.Headers) { - if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } - $int++ - } - Write-Output "Error : "$_.ErrorDetails.Message - Write-Output "Command : "$_.InvocationInfo.Line -} - -# cleanup -Remove-Item $docBase64 \ No newline at end of file diff --git a/examples/eSignature/eg022SigningViaEmailWithKnoweldgeBasedAuthentication.ps1 b/examples/eSignature/eg022SigningViaEmailWithKnowledgeBasedAuthentication.ps1 similarity index 80% rename from examples/eSignature/eg022SigningViaEmailWithKnoweldgeBasedAuthentication.ps1 rename to examples/eSignature/eg022SigningViaEmailWithKnowledgeBasedAuthentication.ps1 index 47b2f55..1d10b93 100644 --- a/examples/eSignature/eg022SigningViaEmailWithKnoweldgeBasedAuthentication.ps1 +++ b/examples/eSignature/eg022SigningViaEmailWithKnowledgeBasedAuthentication.ps1 @@ -2,8 +2,6 @@ # Get required environment variables from .\config\settings.json file $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json -$SIGNER_EMAIL = $variables.SIGNER_EMAIL -$SIGNER_NAME = $variables.SIGNER_NAME # Get the envelope's custom field data # This script uses the envelope ID stored in ../envelope_id. @@ -26,12 +24,30 @@ $docBase64 = New-TemporaryFile [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $docBase64 # Construct your API headers +#ds-snippet-start:eSign22Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign22Step2 + +$isDataIncorrect = $true +while($isDataIncorrect) +{ + $SIGNER_NAME = Read-Host "Please enter name for the signer" + $SIGNER_EMAIL = Read-Host "Please enter email address for the signer" + + if ($SIGNER_EMAIL -eq $variables.SIGNER_EMAIL) { + Write-Output "" + Write-Output "For recipient authentication you must specify a different recipient from the account owner (sender) in order to ensure recipient authentication is performed." + Write-Output "" + } else { + $isDataIncorrect = $false + } +} # Construct your envelope JSON body +#ds-snippet-start:eSign22Step3 $body = @" { "documents": [{ @@ -69,12 +85,14 @@ $body = @" "status": "Sent" } "@ +#ds-snippet-end:eSign22Step3 Write-Output "" Write-Output "Request: " Write-Output $body # a) Make a POST call to the createEnvelopes endpoint to create a new envelope. # b) Display the JSON structure of the created envelope +#ds-snippet-start:eSign22Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes" try { Write-Output "Response:" @@ -90,6 +108,7 @@ catch { Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line } +#ds-snippet-end:eSign22Step4 # cleanup Remove-Item $docBase64 \ No newline at end of file diff --git a/examples/eSignature/eg023SigningViaEmailWithIDVAuthentication.ps1 b/examples/eSignature/eg023SigningViaEmailWithIDVAuthentication.ps1 index 8df5d62..f6414a6 100644 --- a/examples/eSignature/eg023SigningViaEmailWithIDVAuthentication.ps1 +++ b/examples/eSignature/eg023SigningViaEmailWithIDVAuthentication.ps1 @@ -26,38 +26,50 @@ $docBase64 = New-TemporaryFile [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $docBase64 # - Construct your API headers +#ds-snippet-start:eSign23Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json, text/plain, */*") $headers.add("Content-Type", "application/json;charset=UTF-8") $headers.add("Accept-Encoding", "gzip, deflate, br") $headers.add("Accept-Language", "en-US,en;q=0.9") +#ds-snippet-end:eSign23Step2 # - Obtain your workflow ID +#ds-snippet-start:eSign23Step3 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/identity_verification" - Write-Output "Attempting to retrieve your account's workflow ID" -try { - Write-Output "Response:" - $result = Invoke-RestMethod -uri $uri -headers $headers -method GET - $result.content - #Obtain the workflow ID from the API response - $workflowId = $result.identityVerification.workflowId +Write-Output "Response:" +$result = Invoke-RestMethod -uri $uri -headers $headers -method GET +$result.content +#Obtain the workflow ID from the API response +$workflowId = [System.Linq.Enumerable]::FirstOrDefault($result.identityVerification, [func[object, bool]] { param($x) $x.defaultName -eq "DocuSign ID Verification"}).workflowId +#ds-snippet-end:eSign23Step3 + +if ($null -eq $workflowId) +{ + throw "Please contact https://support.docusign.com to enable IDV in your account." } -catch { - $int = 0 - foreach ($header in $_.Exception.Response.Headers) { - #On error, display the error, the line that triggered the error, and the TraceToken - if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } - $int++ + +$isDataIncorrect = $true +while($isDataIncorrect) +{ + $SIGNER_NAME = Read-Host "Please enter name for the signer" + $SIGNER_EMAIL = Read-Host "Please enter email address for the signer" + + if ($SIGNER_EMAIL -eq $variables.SIGNER_EMAIL) { + Write-Output "" + Write-Output "For recipient authentication you must specify a different recipient from the account owner (sender) in order to ensure recipient authentication is performed." + Write-Output "" + } else { + $isDataIncorrect = $false } - Write-Output "Error : "$_.ErrorDetails.Message - Write-Output "Command : "$_.InvocationInfo.Line } # - Construct your envelope JSON body # Note: If you did not successfully obtain your workflow ID, this step will fail. +#ds-snippet-start:eSign23Step4 $body = @" { "documents": [{ @@ -82,8 +94,8 @@ $body = @" "pageNumber": "1", "recipientId":"1", "tabLabel": "SignHereTab", - "xPosition": "75", - "yPosition": "572" + "xPosition": "200", + "yPosition": "160" }] }, "templateAccessCodeRequired": null, @@ -100,17 +112,25 @@ $body = @" "status": "Sent" } "@ +#ds-snippet-end:eSign23Step4 Write-Output "" Write-Output "Request: " Write-Output $body # a) Make a POST call to the createEnvelopes endpoint to create a new envelope. # b) Display the JSON structure of the created envelope +#ds-snippet-start:eSign23Step5 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes" try { Write-Output "Response:" - $result = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST - $result.content + $result = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST + $response = $result.content | ConvertFrom-Json + + $envelopeId = $response.envelopeId + + # Save the IDV envelope id for use by other scripts + Write-Output "IDV EnvelopeId: " $envelopeId + Write-Output $envelopeId > .\config\IDV_ENVELOPE_ID } catch { $int = 0 @@ -121,6 +141,7 @@ catch { Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line } +#ds-snippet-end:eSign23Step5 # cleanup Remove-Item $docBase64 \ No newline at end of file diff --git a/examples/eSignature/eg024CreatingPermissionProfiles.ps1 b/examples/eSignature/eg024CreatingPermissionProfiles.ps1 index fb7f5ca..6b3c8e3 100644 --- a/examples/eSignature/eg024CreatingPermissionProfiles.ps1 +++ b/examples/eSignature/eg024CreatingPermissionProfiles.ps1 @@ -8,16 +8,19 @@ $oAuthAccessToken = Get-Content .\config\ds_access_token.txt # Note: Substitute these values with your own $APIAccountId = Get-Content .\config\API_ACCOUNT_ID -$PROFILE_NAME = Read-Host "Please enter a new permission profile name" +$PROFILE_NAME = Read-Host "Please enter a new permission profile name: " $PROFILE_NAME > .\config\PROFILE_NAME # Construct your API headers +#ds-snippet-start:eSign24Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign24Step2 # Construct the request body for your permission profile +#ds-snippet-start:eSign24Step3 $body = @" { "permissionProfileName": "${PROFILE_NAME}", @@ -31,7 +34,6 @@ $body = @" "allowedAddressBookAccess":"personalAndShared", "allowedTemplateAccess":"share", "enableRecipientViewingNotifications":"true", - "enableRecipientViewingNotifications":"true", "enableSequentialSigningInterface":"true", "receiveCompletedSelfSignedDocumentsAsEmailLinks":"false", "signingUiVersion":"v2", @@ -51,9 +53,11 @@ $body = @" } } "@ +#ds-snippet-end:eSign24Step3 # a) Call the eSignature API # b) Display the JSON response +#ds-snippet-start:eSign24Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/permission_profiles/" try { @@ -71,4 +75,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign24Step4 \ No newline at end of file diff --git a/examples/eSignature/eg025SettingPermissionProfiles.ps1 b/examples/eSignature/eg025SettingPermissionProfiles.ps1 index 8848f74..192d76f 100644 --- a/examples/eSignature/eg025SettingPermissionProfiles.ps1 +++ b/examples/eSignature/eg025SettingPermissionProfiles.ps1 @@ -19,15 +19,18 @@ else { # Step 2. Construct your API headers # Construct your API headers +#ds-snippet-start:eSign25Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign25Step2 $GROUP_NAME = Read-Host "Please enter a NEW group name" # Step 3. Construct the request body # Create a Group and get a Group ID +#ds-snippet-start:eSign25Step3 $body = @" { "groups": [ @@ -37,6 +40,7 @@ $body = @" ] } "@ +#ds-snippet-end:eSign25Step3 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/groups" $response = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST $groupId = $($response.Content | ConvertFrom-Json).groups.groupId @@ -57,6 +61,7 @@ $body = @" # a) Call the eSignature API # b) Display the JSON response +#ds-snippet-start:eSign25Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/groups" $response = $null @@ -74,4 +79,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign25Step4 \ No newline at end of file diff --git a/examples/eSignature/eg026UpdatingIndividualPermission.ps1 b/examples/eSignature/eg026UpdatingIndividualPermission.ps1 index 3b514f3..f07ff02 100644 --- a/examples/eSignature/eg026UpdatingIndividualPermission.ps1 +++ b/examples/eSignature/eg026UpdatingIndividualPermission.ps1 @@ -9,10 +9,12 @@ $oAuthAccessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers +#ds-snippet-start:eSign26Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign26Step2 # Check that we have an profile name if (Test-Path .\config\PROFILE_NAME) { @@ -33,6 +35,7 @@ else { } # Construct your request body +#ds-snippet-start:eSign26Step3 $body = @" { "permissionProfileName": "${profileName}", @@ -65,9 +68,11 @@ $body = @" } } "@ +#ds-snippet-end:eSign26Step3 # a) Call the eSignature API # b) Display the JSON response +#ds-snippet-start:eSign26Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/permission_profiles/${profileId}" try { @@ -84,4 +89,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign26Step4 \ No newline at end of file diff --git a/examples/eSignature/eg027DeletingPermissions.ps1 b/examples/eSignature/eg027DeletingPermissions.ps1 index 9dfbdac..6d062da 100644 --- a/examples/eSignature/eg027DeletingPermissions.ps1 +++ b/examples/eSignature/eg027DeletingPermissions.ps1 @@ -18,13 +18,16 @@ else { } # Construct your API headers +#ds-snippet-start:eSign27Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign27Step2 # a) Call the eSignature API # b) Display the JSON response +#ds-snippet-start:eSign27Step3 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/permission_profiles/$profileID" try { @@ -40,4 +43,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign27Step3 \ No newline at end of file diff --git a/examples/eSignature/eg028CreatingABrand.ps1 b/examples/eSignature/eg028CreatingABrand.ps1 index ce8bb36..92d4a9b 100644 --- a/examples/eSignature/eg028CreatingABrand.ps1 +++ b/examples/eSignature/eg028CreatingABrand.ps1 @@ -9,23 +9,28 @@ $oAuthAccessToken = Get-Content .\config\ds_access_token.txt $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Construct your API headers +#ds-snippet-start:eSign28Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign28Step2 $brandName = Read-Host "Please enter a NEW brand name" # Construct the request body +#ds-snippet-start:eSign28Step3 $body = @" { "brandName": "${brandName}", "defaultBrandLanguage": "en" } "@ +#ds-snippet-end:eSign28Step3 # a) Call the eSignature API # b) Display the JSON response +#ds-snippet-start:eSign28Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/brands" try { @@ -43,4 +48,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign28Step4 \ No newline at end of file diff --git a/examples/eSignature/eg029ApplyingBrandEnvelope.ps1 b/examples/eSignature/eg029ApplyingBrandEnvelope.ps1 index ac62de5..3607d69 100644 --- a/examples/eSignature/eg029ApplyingBrandEnvelope.ps1 +++ b/examples/eSignature/eg029ApplyingBrandEnvelope.ps1 @@ -23,12 +23,15 @@ else { } # Construct your API headers +#ds-snippet-start:eSign29Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign29Step2 # Construct your request body +#ds-snippet-start:eSign29Step3 $body = @" { "documents": [{ @@ -65,12 +68,14 @@ $body = @" "status": "Sent" } "@ +#ds-snippet-end:eSign29Step3 Write-Output "" Write-Output "Request: " Write-Output $body # a) Make a POST call to the createEnvelopes endpoint to create a new envelope. # b) Display the JSON structure of the created envelope +#ds-snippet-start:eSign29Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes" try { Write-Output "Response:" @@ -85,4 +90,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign29Step4 \ No newline at end of file diff --git a/examples/eSignature/eg030ApplyingBrandTemplate.ps1 b/examples/eSignature/eg030ApplyingBrandTemplate.ps1 index c220e10..b47058e 100644 --- a/examples/eSignature/eg030ApplyingBrandTemplate.ps1 +++ b/examples/eSignature/eg030ApplyingBrandTemplate.ps1 @@ -34,12 +34,15 @@ else { } # Construct your API headers +#ds-snippet-start:eSign30Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign30Step2 # Construct your request body +#ds-snippet-start:eSign30Step3 $body = @" { "templateId": "${templateID}", @@ -59,9 +62,11 @@ $body = @" "status": "sent" } "@ +#ds-snippet-end:eSign30Step3 # a) Make a POST call to the createEnvelopes endpoint to create a new envelope. # b) Display the JSON structure of the created envelope +#ds-snippet-start:eSign30Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes" try { @@ -77,4 +82,5 @@ catch { } Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line -} \ No newline at end of file +} +#ds-snippet-end:eSign30Step4 \ No newline at end of file diff --git a/examples/eSignature/eg031BulkSending.ps1 b/examples/eSignature/eg031BulkSending.ps1 index 21a759a..abcbf11 100644 --- a/examples/eSignature/eg031BulkSending.ps1 +++ b/examples/eSignature/eg031BulkSending.ps1 @@ -10,18 +10,28 @@ $APIAccountId = Get-Content .\config\API_ACCOUNT_ID # Get required environment variables from .\config\settings.json file $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json -$CC_EMAIL = $variables.CC_EMAIL -$CC_NAME = $variables.CC_NAME -$SIGNER_EMAIL = $variables.SIGNER_EMAIL -$SIGNER_NAME = $variables.SIGNER_NAME +$SIGNER1_EMAIL = Read-Host "Please enter Bulk copy #1 signer email address" +$SIGNER1_NAME = Read-Host "Please enter Bulk copy #1 signer name" +$CC1_EMAIL = Read-Host "Please enter Bulk copy #1 carbon copy email address" +$CC1_NAME = Read-Host "Please enter Bulk copy #1 carbon copy name" +$SIGNER2_EMAIL = Read-Host "Please enter Bulk copy #2 signer email address" +$SIGNER2_NAME = Read-Host "Please enter Bulk copy #2 signer name" +$CC2_EMAIL = Read-Host "Please enter Bulk copy #2 carbon copy email address" +$CC2_NAME = Read-Host "Please enter Bulk copy #2 carbon copy name" + +$doc1Base64 = New-TemporaryFile +# Fetch doc and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $doc1Base64 # Construct your API headers +#ds-snippet-start:eSign31Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json, text/plain, */*") $headers.add("Content-Type", "application/json;charset=UTF-8") $headers.add("Accept-Encoding", "gzip, deflate, br") $headers.add("Accept-Language", "en-US,en;q=0.9") +#ds-snippet-end:eSign31Step2 $continue = $true @@ -29,40 +39,33 @@ $continue = $true # Submit the Bulk List # Create a temporary file to store the JSON body # The JSON body must contain the recipient role, recipientId, name, and email. +#ds-snippet-start:eSign31Step3 $body = @" { "name": "sample.csv", "bulkCopies": [{ "recipients": [{ - "recipientId": "39542944", - "role": "signer", - "tabs": [], - "name": "${SIGNER_NAME}", - "email": "${SIGNER_EMAIL}" + "roleName": "signer", + "name": "${SIGNER1_NAME}", + "email": "${SIGNER1_EMAIL}" }, { - "recipientId": "84754526", - "role": "cc", - "tabs": [], - "name": "${CC_NAME}", - "email": "${CC_EMAIL}" + "roleName": "cc", + "name": "${CC1_NAME}", + "email": "${CC1_EMAIL}" }], "customFields": [] }, { "recipients": [{ - "recipientId": "39542944", - "role": "signer", - "tabs": [], - "name": "${SIGNER_NAME}", - "email": "${SIGNER_EMAIL}" + "roleName": "signer", + "name": "${SIGNER2_NAME}", + "email": "${SIGNER2_EMAIL}" }, { - "recipientId": "84754526", - "role": "cc", - "tabs": [], - "name": "${CC_NAME}", - "email": "${CC_EMAIL}" + "roleName": "cc", + "name": "${CC2_NAME}", + "email": "${CC2_EMAIL}" }], "customFields": [] }] @@ -96,23 +99,52 @@ catch { Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line } - +#ds-snippet-end:eSign31Step3 # Step 4. Create an envelope # Create your draft envelope -$base = "DQoNCg0KCQkJCXRleHQgZG9jDQoNCg0KDQoNCg0KUk0gIwlSTSAjCVJNICMNCg0KDQoNClxzMVwNCg0KLy9hbmNoMSANCgkvL2FuY2gyDQoJCS8vYW5jaDM=" +#ds-snippet-start:eSign31Step4 $body = @" { "documents": [{ - "documentBase64": "$base", + "documentBase64": "$(Get-Content $doc1Base64)", "documentId": "1", "fileExtension": "txt", "name": "NDA" }], "envelopeIdStamping": "true", "emailSubject": "Please sign", - "cdse_mode": "true", "recipients": { - }, + "signers": [{ + "name": "Multi Bulk Recipient::signer", + "email": "multiBulkRecipients-signer@docusign.com", + "roleName": "signer", + "routingOrder": "1", + "recipientId" : "1", + "recipientType" : "signer", + "delieveryMethod" : "Email", + "status": "created", + "tabs": { + "signHereTabs": [{ + "documentId": "1", + "name": "SignHereTab", + "pageNumber": "1", + "recipientId": "1", + "tabLabel": "SignHereTab", + "xPosition": "200", + "yPosition": "160" + }]} + }], + "carbonCopies": [{ + "name": "Multi Bulk Recipient::cc", + "email": "multiBulkRecipients-cc@docusign.com", + "roleName": "cc", + "routingOrder": "2", + "recipientId" : "2", + "recipientType" : "cc", + "delieveryMethod" : "Email", + "status": "created" + }] + }, "status": "created" } "@ @@ -143,11 +175,12 @@ if ($continue -eq $true) { Write-Output "Command : "$_.InvocationInfo.Line } } - +#ds-snippet-end:eSign31Step4 # Step 5. Attach your bulk list ID to the envelope # Add an envelope custom field set to the value of your listId # This Custom Field is used for tracking your Bulk Send via the Envelopes::Get method # Create a temporary file to store the JSON body +#ds-snippet-start:eSign31Step5 $body = @" { "listCustomFields": [], @@ -181,67 +214,11 @@ catch { Write-Output "Error : "$_.ErrorDetails.Message Write-Output "Command : "$_.InvocationInfo.Line } - -# Step 6. Add placeholder recipients -# Add placeholder recipients. -# Note: The name / email format used is: -# Name: Multi Bulk Recipients::{rolename} -# Email: MultiBulkRecipients-{rolename}@docusign.com -$body = @" -{ - "signers": [{ - "name": "Multi Bulk Recipient::cc", - "email": "multiBulkRecipients-cc@docusign.com", - "roleName": "cc", - "routingOrder": 1, - "status": "created", - "templateAccessCodeRequired": null, - "deliveryMethod": "email", - "recipientId": "84754526", - "recipientType": "signer" - }, - { - "name": "Multi Bulk Recipient::signer", - "email": "multiBulkRecipients-signer@docusign.com", - "roleName": "signer", - "routingOrder": 1, - "status": "created", - "templateAccessCodeRequired": null, - "deliveryMethod": "email", - "recipientId": "39542944", - "recipientType": "signer" - }] -} -"@ - -$uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes/$envelopeId/recipients" - -if ($continue -eq $true) { - try { - Write-Output @" - - Adding placeholder recipients to the envelope -"@ - $response = Invoke-RestMethod -uri $uri -headers $headers -body $body -method POST - $response | ConvertTo-Json - } - catch { - Write-Output "Adding placeholder recipients has failed" - #On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error #On failure, display a notification, X-DocuSign-TraceToken, error message, and the command that triggered the error - $continue = $false - $int = 0 - foreach ($header in $_.Exception.Response.Headers) { - if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } - $int++ - } - Write-Output "Error : "$_.ErrorDetails.Message - Write-Output "Command : "$_.InvocationInfo.Line - } -} - -# Step 7. Initiate bulk send +#ds-snippet-end:eSign31Step5 +# Step 6. Initiate bulk send # Initiate the Bulk Send # Target endpoint: {ACCOUNT_ID}/bulk_send_lists/{LIST_ID}/send +#ds-snippet-start:eSign31Step6 $body = @" { "listId": "${listId}", @@ -273,10 +250,11 @@ if ($continue -eq $true) { Write-Output "Command : "$_.InvocationInfo.Line } } - -# Step 8. Confirm successful batch send +#ds-snippet-end:eSign31Step6 +# Step 7. Confirm successful batch send # Confirm successful batch send # Note: Depending on the number of Bulk Recipients, it may take some time for the Bulk Send to complete. For 2000 recipients this can take ~1 hour. +#ds-snippet-start:eSign31Step7 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/bulk_send_batch/$bulkBatchId" if ($continue -eq $true) { $response = $null @@ -300,3 +278,4 @@ if ($continue -eq $true) { Write-Output "Command : "$_.InvocationInfo.Line } } +#ds-snippet-end:eSign31Step7 diff --git a/examples/eSignature/eg032PauseSignatureWorkflow.ps1 b/examples/eSignature/eg032PauseSignatureWorkflow.ps1 index 490df95..eb4ef1d 100644 --- a/examples/eSignature/eg032PauseSignatureWorkflow.ps1 +++ b/examples/eSignature/eg032PauseSignatureWorkflow.ps1 @@ -20,12 +20,15 @@ $requestData = New-TemporaryFile $response = New-TemporaryFile # Step 2. Construct your API headers +#ds-snippet-start:eSign32Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign32Step2 # Step 3. Construct the request body +#ds-snippet-start:eSign32Step3 @{ documents = @( @@ -91,8 +94,10 @@ $headers.add("Content-Type", "application/json") }; status = "sent" } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign32Step3 # Step 4. Call the eSignature API +#ds-snippet-start:eSign32Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/envelopes" Invoke-RestMethod ` -Uri $uri ` @@ -103,6 +108,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign32Step4 # Get EnvelopeID $envelopeId = $($(Get-Content $response) | ConvertFrom-Json).envelopeId diff --git a/examples/eSignature/eg033UnpauseSignatureWorkflow.ps1 b/examples/eSignature/eg033UnpauseSignatureWorkflow.ps1 index 485befe..c0df3de 100644 --- a/examples/eSignature/eg033UnpauseSignatureWorkflow.ps1 +++ b/examples/eSignature/eg033UnpauseSignatureWorkflow.ps1 @@ -22,20 +22,25 @@ else { } # Step 2. Construct your API headers +#ds-snippet-start:eSign33Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign33Step2 # Step 3.Construct the JSON body for your envelope +#ds-snippet-start:eSign33Step3 @{ workflow = @{ workflowStatus = "in_progress"; }; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign33Step3 # Step 4. Call the eSignature API +#ds-snippet-start:eSign33Step4 $uri = "https://demo.docusign.net/restapi/v2.1/accounts/${APIaccountId}/envelopes/${envelopeId}?resend_envelope=true" Invoke-RestMethod ` -Uri $uri ` @@ -47,6 +52,7 @@ Invoke-RestMethod ` } ` -InFile (Resolve-Path $requestData).Path ` -OutFile $response +#ds-snippet-end:eSign33Step4 Write-Output "" Write-Output "Request: $(Get-Content -Raw $requestData)" diff --git a/examples/eSignature/eg034UseConditionalRecipients.ps1 b/examples/eSignature/eg034UseConditionalRecipients.ps1 index 2f9cd20..ff86d11 100644 --- a/examples/eSignature/eg034UseConditionalRecipients.ps1 +++ b/examples/eSignature/eg034UseConditionalRecipients.ps1 @@ -8,26 +8,43 @@ $oAuthAccessToken = Get-Content .\config\ds_access_token.txt # Note: Substitute these values with your own $APIAccountId = Get-Content .\config\API_ACCOUNT_ID +# Get required environment variables from .\config\settings.json file +$configFile = ".\config\settings.json" +$config = Get-Content $configFile -Raw | ConvertFrom-Json + +if($config.SIGNER_NOT_CHECKED_EMAIL -eq "{SIGNER_NOT_CHECKED_EMAIL}" ){ + $config.SIGNER_NOT_CHECKED_EMAIL = Read-Host "Enter an email address to route to when the checkbox is not checked" + $config.SIGNER_NOT_CHECKED_NAME = Read-Host "Enter a name to route to when the checkbox is not checked" + Write-Output "" + write-output $config | ConvertTo-Json | Set-Content $configFile + } + # Get required environment variables from .\config\settings.json file $variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json -$SIGNER1_EMAIL = $variables.CC_EMAIL -$SIGNER1_NAME = $variables.CC_NAME +$SIGNER1_EMAIL = $variables.SIGNER_EMAIL +$SIGNER1_NAME = $variables.SIGNER_NAME $SIGNER_WHEN_CHECKED_EMAIL = $variables.CC_EMAIL $SIGNER_WHEN_CHECKED_NAME = $variables.CC_NAME $SIGNER_NOT_CHECKED_EMAIL = $variables.SIGNER_NOT_CHECKED_EMAIL $SIGNER_NOT_CHECKED_NAME = $variables.SIGNER_NOT_CHECKED_NAME +Write-Output "SIGNER_NOT_CHECKED_EMAIL is $SIGNER_NOT_CHECKED_EMAIL" +Write-Output "SIGNER_NOT_CHECKED_NAME is $SIGNER_NOT_CHECKED_NAME" + # Step 2. Construct your API headers +#ds-snippet-start:eSign34Step2 $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.add("Authorization", "Bearer $oAuthAccessToken") $headers.add("Accept", "application/json") $headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign34Step2 # Create temp files $requestData = New-TemporaryFile $response = New-TemporaryFile # Step 3. Construct the request body +#ds-snippet-start:eSign34Step3 @{ documents = @( @@ -90,6 +107,7 @@ $response = New-TemporaryFile tabId = "ApprovalTab"; operator = "equals"; value = "false"; + tabType = "checkbox"; tabLabel = "ApproveWhenChecked" }; ); @@ -105,6 +123,7 @@ $response = New-TemporaryFile tabId = "ApprovalTab"; operator = "equals"; value = "true"; + tabType = "checkbox"; tabLabel = "ApproveWhenChecked" }; ); @@ -180,9 +199,13 @@ $response = New-TemporaryFile }; status = "Sent" } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign34Step3 # Step 4. Call the eSignature API +#ds-snippet-start:eSign34Step4 try { + Write-Output "Request: " + Write-Output $requestData $uri = "https://demo.docusign.net/restapi/v2.1/accounts/${APIaccountId}/envelopes" Invoke-RestMethod ` -Uri $uri ` @@ -201,7 +224,7 @@ catch [System.Net.WebException] { if ( $errorCode -eq "WORKFLOW_UPDATE_RECIPIENTROUTING_NOT_ALLOWED" ) { Write-Output "" Write-Output "The following Error happened: WORKFLOW_UPDATE_RECIPIENTROUTING_NOT_ALLOWED" - Write-Output "Please contact DocuSign support..." + Write-Output "Please contact Docusign support..." } else { $_ @@ -210,7 +233,8 @@ catch [System.Net.WebException] { catch { Write-Output $_ } +#ds-snippet-end:eSign34Step4 # Delete temp files Remove-Item $requestData -Remove-Item $response \ No newline at end of file +Remove-Item $response diff --git a/examples/eSignature/eg035ScheduledSending.ps1 b/examples/eSignature/eg035ScheduledSending.ps1 new file mode 100644 index 0000000..1397119 --- /dev/null +++ b/examples/eSignature/eg035ScheduledSending.ps1 @@ -0,0 +1,113 @@ +#Scheduled Sending +$apiUri = "https://demo.docusign.net/restapi" + +# Send an envelope with one document + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +$accessToken = Get-Content .\config\ds_access_token.txt + +# Obtain your accountId from demo.docusign.net -- the account id is shown in +# the drop down on the upper right corner of the screen by your picture or +# the default picture. +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# document 1 (pdf) has tag /sn1/ +# +# The envelope has one recipient. +# recipient 1 - signer +# The envelope will be scheduled to go to the signer. + + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$docBase64 = New-TemporaryFile + +# Fetch docs and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $docBase64 + +Write-Output "Sending the envelope request to Docusign..." +Write-Output "The envelope has one document. Processing time will be about 15 seconds." +Write-Output "Results:" + +# Step 2. Create the envelope definition +$ResumeDate = Read-Host "Please enter the future date for when you want to schedule this envelope as YYYY-MM-DD: " + +#ds-snippet-start:eSign35Step2 +@{ + emailSubject = "Please sign this document set"; + documents = @( + @{ + documentBase64 = "$(Get-Content $docBase64)"; + name = "Lorem Ipsum"; + fileExtension = "pdf"; + documentId = "1"; + }; ); + workflow = @{ + scheduledSending = @{ + rules = @( + @{ + resumeDate = $ResumeDate; + }; ); + }; + }; + recipients = @{ + signers = @( + @{ + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "1"; + routingOrder = "1"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "**signature_1**"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign35Step2 + +# Step 3. Create and send the envelope +#ds-snippet-start:eSign35Step3 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" +#ds-snippet-end:eSign35Step3 + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId + +# Save the envelope id for use by other scripts +Write-Output "EnvelopeId: $envelopeId" +Write-Output $envelopeId > .\config\ENVELOPE_ID + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $docBase64 + +Write-Output "Done." diff --git a/examples/eSignature/eg036DelayedRouting.ps1 b/examples/eSignature/eg036DelayedRouting.ps1 new file mode 100644 index 0000000..89a7e4c --- /dev/null +++ b/examples/eSignature/eg036DelayedRouting.ps1 @@ -0,0 +1,135 @@ +#Delayed Routing +$apiUri = "https://demo.docusign.net/restapi" + +# Send an envelope with one document + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +$accessToken = Get-Content .\config\ds_access_token.txt + +# Obtain your accountId from demo.docusign.net -- the account id is shown in +# the drop down on the upper right corner of the screen by your picture or +# the default picture. +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# document (pdf) has tag /sn1/ +# +# The envelope has two recipients. +# recipient 1 - signer +# recipient 2 - signer +# The envelope will be sent first to the signer. +# After it is signed, a copy is sent to the cc person. + + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$docBase64 = New-TemporaryFile + +# Fetch doc and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $docBase64 + +Write-Output "Sending the envelope request to Docusign..." +Write-Output "The envelope has three documents. Processing time will be about 15 seconds." +Write-Output "Results:" + +$Signer2Email = Read-Host "Please enter the email address for the second signer: " +$Signer2Name = Read-Host "Please enter the name for the second signer: " +$DelayInHours= Read-Host "Please enter the delay (in hours): " +$DelayTimeSpan = New-TimeSpan -Hours $DelayInHours -Minutes 0 + +#ds-snippet-start:eSign36Step2 +@{ + emailSubject = "Please sign this document set"; + documents = @( + @{ + documentBase64 = "$(Get-Content $docBase64)"; + name = "Lorem Ipsum"; + fileExtension = "pdf"; + documentId = "1"; + }; ); + workflow = @{ + workflowSteps = @( + @{ + action = "pause_before"; + triggerOnItem = "routing_order"; + itemId = "2"; + delayedRouting = @{ + rules = @( + @{ + delay = $DelayTimeSpan.ToString(); + }; + ); + }; + }; + ); + }; + recipients = @{ + signers = @( + @{ + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "1"; + routingOrder = "1"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + @{ + email = $Signer2Email; + name = $Signer2Name; + recipientId = "2"; + routingOrder = "2"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "120"; + anchorYOffset = "10"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign36Step2 + +# Create and send the envelope +#ds-snippet-start:eSign36Step3 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign36Step3 + +Write-Output "Response: $(Get-Content -Raw $response)" + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId + +# Save the envelope id for use by other scripts +Write-Output "EnvelopeId: $envelopeId" +Write-Output $envelopeId > .\config\ENVELOPE_ID + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $docBase64 + +Write-Output "Done." diff --git a/examples/eSignature/eg035SMSDelivery.ps1 b/examples/eSignature/eg037SMSDelivery.ps1 similarity index 77% rename from examples/eSignature/eg035SMSDelivery.ps1 rename to examples/eSignature/eg037SMSDelivery.ps1 index a5875b9..75c3904 100644 --- a/examples/eSignature/eg035SMSDelivery.ps1 +++ b/examples/eSignature/eg037SMSDelivery.ps1 @@ -1,4 +1,4 @@ -#SMS Delivery +#SMS or WhatsApp Delivery $apiUri = "https://demo.docusign.net/restapi" # Send an envelope with three documents @@ -13,7 +13,6 @@ $accessToken = Get-Content .\config\ds_access_token.txt # the default picture. $accountId = Get-Content .\config\API_ACCOUNT_ID -# ***DS.snippet.0.start # document 1 (html) has tag **signature_1** # document 2 (docx) has tag /sn1/ # document 3 (pdf) has tag /sn1/ @@ -37,16 +36,28 @@ $doc3Base64 = New-TemporaryFile [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx"))) > $doc2Base64 [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $doc3Base64 -Write-Output "Sending the envelope request to DocuSign..." -Write-Output "The envelope has three documents. Processing time will be about 15 seconds." -Write-Output "Results:" - # Step 2. Create the envelope definition -$SMSCountryPrefix = Read-Host "Please enter a country phone number prefix for the Signer" -$SMSNumber = Read-Host "Please enter an SMS-enabled Phone number for the Signer" -$SMSCCCountryPrefix = Read-Host "Please enter a country phone number prefix for the Carbon Copied recipient" -$SMSNumberCC = Read-Host "Please enter an SMS-enabled Phone number for the Carbon Copied recipient" +Write-Output 'Please choose a message delivery type: ' +Write-Output "$(1) SMS" +Write-Output "$(2) WhatsApp" + +[int]$MSGType = Read-Host "Select 1 or 2" + +if ($MSGType -eq 1){ + $selected = "SMS" +} +else { + $selected = "WhatsApp" +} + +$MSGCountryPrefix = Read-Host "Please enter a country phone number prefix for the Signer" +$MSGNumber = Read-Host "Please enter a Mobile number for the Signer" +$MSGCCCountryPrefix = Read-Host "Please enter a country phone number prefix for the Carbon Copied recipient" +$MSGNumberCC = Read-Host "Please enter a Mobile number for the Carbon Copied recipient" + + +#ds-snippet-start:eSign37Step2 @{ emailSubject = "Please sign this document set"; documents = @( @@ -71,37 +82,28 @@ $SMSNumberCC = Read-Host "Please enter an SMS-enabled Phone number for the Carbo recipients = @{ carbonCopies = @( @{ - email = $variables.CC_EMAIL; + phoneNumber = @{ + countryCode = $MSGCCCountryPrefix; + number = $MSGNumberCC; + } name = $variables.CC_NAME; recipientId = "2"; routingOrder = "2"; - additionalNotifications = @( - @{ - secondaryDeliveryMethod = "SMS"; - phoneNumber = @{ - countryCode = $SMSCCCountryPrefix; - number = $SMSNumberCC; - } - } - ); + deliveryMethod = $selected; }; ); signers = @( @{ - email = $variables.SIGNER_EMAIL; + phoneNumber = @{ + countryCode = $MSGCountryPrefix; + number = $MSGNumber; + + } name = $variables.SIGNER_NAME; recipientId = "1"; routingOrder = "1"; - additionalNotifications = @( - @{ - secondaryDeliveryMethod = "SMS"; - phoneNumber = @{ - countryCode = $SMSCountryPrefix; - number = $SMSNumber; - } - } - ); + deliveryMethod = $selected; tabs = @{ signHereTabs = @( @{ @@ -123,8 +125,16 @@ $SMSNumberCC = Read-Host "Please enter an SMS-enabled Phone number for the Carbo }; status = "sent"; } | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign37Step2 + +Write-Output "Sending the envelope request to Docusign..." +Write-Output "The envelope has three documents. Processing time will be about 15 seconds." +Write-Output "Results:" +Write-Output $requestData # Step 3. Create and send the envelope +# Create and send the envelope +#ds-snippet-start:eSign37Step3 Invoke-RestMethod ` -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` -Method 'POST' ` @@ -136,11 +146,11 @@ Invoke-RestMethod ` -OutFile $response Write-Output "Response: $(Get-Content -Raw $response)" +#ds-snippet-end:eSign37Step3 # pull out the envelopeId $envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId -# ***DS.snippet.0.end # Save the envelope id for use by other scripts Write-Output "EnvelopeId: $envelopeId" Write-Output $envelopeId > .\config\ENVELOPE_ID diff --git a/examples/eSignature/eg038ResponsiveSigning.ps1 b/examples/eSignature/eg038ResponsiveSigning.ps1 new file mode 100644 index 0000000..0395b34 --- /dev/null +++ b/examples/eSignature/eg038ResponsiveSigning.ps1 @@ -0,0 +1,191 @@ +$apiUri = "https://demo.docusign.net/restapi" + +# Responsive signing + +# Get required variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + +# 1. Obtain your OAuth token +$accessToken = Get-Content .\config\ds_access_token.txt + +# Obtain your accountId from demo.docusign.net -- the account id is shown in +# the drop down on the upper right corner of the screen by your picture or +# the default picture. +$accountID = Get-Content .\config\API_ACCOUNT_ID + +# Step 2. Create the envelope definition. +# The signer recipient includes a clientUserId setting +# +# The envelope will be sent first to the signer. +# After it is signed, a copy is sent to the cc person. + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$doc_html = New-TemporaryFile + +# Fetch doc +[IO.File]::ReadAllText(".\demo_documents\order_form.html") > $doc_html + +# Insert inner HTML + +((Get-Content $doc_html) ` + -replace '/sn1/', '' ` + -replace '/l1q/', '' ` + -replace '/l2q/', '') | Set-Content $doc_html + +Write-Output "Sending the envelope request to Docusign..." + +$price1 = 5 +$price2 = 150 + +# Concatenate the different parts of the request +#ds-snippet-start:eSign38Step2 +@{ + emailSubject = "Example Signing Document"; + documents = @( + @{ + name = "doc1.html"; + documentId = "1"; + htmlDefinition = @{ + source = "$(Get-Content $doc_html)"; + }; + }; + ); + recipients = @{ + signers = @( + @{ + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "1"; + routingOrder = "1"; + clientUserId = "1000"; + roleName = "Signer"; + tabs = @{ + formulaTabs = @( + @{ + font = "helvetica"; + fontSize = "size11"; + fontColor = "black"; + anchorString = "/l1e/"; + anchorYOffset = "-8"; + anchorUnits = "pixels"; + anchorXOffset = "105"; + tabLabel = "l1e"; + formula = "[l1q] * $price1"; + roundDecimalPlaces = "0"; + required = "true"; + locked = "true"; + disableAutoSize = "false"; + }; + @{ + font = "helvetica"; + fontSize = "size11"; + fontColor = "black"; + anchorString = "/l2e/"; + anchorYOffset = "-8"; + anchorUnits = "pixels"; + anchorXOffset = "105"; + tabLabel = "l2e"; + formula = "[l2q] * $price2"; + roundDecimalPlaces = "0"; + required = "true"; + locked = "true"; + disableAutoSize = "false"; + }; + @{ + font = "helvetica"; + fontSize = "size11"; + fontColor = "black"; + anchorString = "/l3t/"; + anchorYOffset = "-8"; + anchorUnits = "pixels"; + anchorXOffset = "105"; + tabLabel = "l3t"; + formula = "[l1e] + [l2e]"; + roundDecimalPlaces = "0"; + required = "true"; + locked = "true"; + disableAutoSize = "false"; + bold = "true"; + }; + ); + }; + }; + ); + carbonCopies = @( + @{ + email = $variables.CC_EMAIL; + name = $variables.CC_NAME; + recipientId = "2"; + routingOrder = "2"; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign38Step2 + +# Step 3. Call Docusign to create the envelope +#ds-snippet-start:eSign38Step3 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign38Step3 + +Write-Output "Response: $(Get-Content -Raw $response)" + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId +Write-Output "EnvelopeId: $envelopeId" + +# Step 4. Create a recipient view definition +# The signer will directly open this link from the browser to sign. +# +# The returnUrl is normally your own web app. Docusign will redirect +# the signer to returnUrl when the signing completes. +# For this example, we'll use http://httpbin.org/get to show the +# query parameters passed back from Docusign + +Write-Output "Requesting the url for the embedded signing..." + +$json = [ordered]@{ + 'returnUrl' = 'http://httpbin.org/get'; + 'authenticationMethod' = 'none'; + 'email' = $variables.SIGNER_EMAIL; + 'userName' = $variables.SIGNER_NAME; + 'clientUserId' = 1000 +} | ConvertTo-Json -Compress + + +# Step 5. Create the recipient view and begin the Docusign signing +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -Body $json ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" +$signingUrl = $(Get-Content $response | ConvertFrom-Json).url + +Write-Output "The embedded signing URL is $signingUrl" +Write-Output "It is only valid for five minutes. Attempting to automatically open your browser..." + +Start-Process $signingUrl + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $doc_html + +Write-Output "Done." diff --git a/examples/eSignature/eg039SigningInPerson.ps1 b/examples/eSignature/eg039SigningInPerson.ps1 new file mode 100644 index 0000000..9a0b805 --- /dev/null +++ b/examples/eSignature/eg039SigningInPerson.ps1 @@ -0,0 +1,146 @@ +$apiUri = "https://demo.docusign.net/restapi" +$authorizationEndpoint = "https://account-d.docusign.com/oauth" + +# Step 1: Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# Step 2. Create the envelope definition. +# +# document 1 (PDF) has tag /sn1/ +# recipient 1 - signer +# The envelope will be sent first to the signer. +# After it is signed, a copy is sent to the cc person. + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$docBase64 = New-TemporaryFile + +# Fetch doc and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $docBase64 + +$signerName = Read-Host "Please enter the name of the in person signer" + +# Get the current user email +Invoke-RestMethod ` + -Uri "${authorizationEndpoint}/userinfo" ` + -Method 'GET' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Cache-Control' = "no-store"; + 'Pragma' = "no-cache"; +} ` + -OutFile $response + +$hostEmail = $(Get-Content $response | ConvertFrom-Json).email +$hostName = $(Get-Content $response | ConvertFrom-Json).name + +Write-Output "Sending the envelope request to Docusign..." + +# Concatenate the different parts of the request +#ds-snippet-start:eSign39Step2 +@{ + emailSubject = "Please sign this document set"; + documents = @( + @{ + documentBase64 = "$(Get-Content $docBase64)"; + name = "Lorem Ipsum"; + fileExtension = "pdf"; + documentId = "1"; + }; + ); + recipients = @{ + inPersonSigners = @( + @{ + hostEmail = $hostEmail; + hostName = $hostName; + signerName = $signerName; + recipientId = "1"; + routingOrder = "1"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign39Step2 + +# Step 3. Call Docusign to create the envelope +#ds-snippet-start:eSign39Step3 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId +Write-Output "EnvelopeId: $envelopeId" +#ds-snippet-end:eSign39Step3 + +# Step 4. Create a recipient view definition +# The signer will directly open this link from the browser to sign. +# +# The returnUrl is normally your own web app. Docusign will redirect +# the signer to returnUrl when the signing completes. +# For this example, we'll use http://httpbin.org/get to show the +# query parameters passed back from Docusign + +Write-Output "Requesting the url for the embedded signing..." + +#ds-snippet-start:eSign39Step4 +$json = [ordered]@{ + 'returnUrl' = 'http://httpbin.org/get'; + 'authenticationMethod' = 'none'; + 'email' = $hostEmail; + 'userName' = $hostName; +} | ConvertTo-Json -Compress +#ds-snippet-end:eSign39Step4 + +# Step 5. Create the recipient view and begin the Docusign signing +#ds-snippet-start:eSign39Step5 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -Body $json ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" +$signingUrl = $(Get-Content $response | ConvertFrom-Json).url +#ds-snippet-end:eSign39Step4 + +Write-Output "The embedded signing URL is $signingUrl" +Write-Output "It is only valid for five minutes. Attempting to automatically open your browser..." + +Start-Process $signingUrl + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $docBase64 + +Write-Output "Done." diff --git a/examples/eSignature/eg040SetDocumentVisibility.ps1 b/examples/eSignature/eg040SetDocumentVisibility.ps1 new file mode 100644 index 0000000..a44ef2a --- /dev/null +++ b/examples/eSignature/eg040SetDocumentVisibility.ps1 @@ -0,0 +1,173 @@ +$apiUri = "https://demo.docusign.net/restapi" + +# Send an envelope and set document visibility + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + + +# Step 1: Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# document 1 (html) has tag **signature_1** +# document 2 (docx) has tag /sn1/ +# document 3 (pdf) has tag /sn1/ +# +# The envelope has two recipients. +# recipient 1 - signer 1 +# recipient 2 - signer 2 +# recipient 3 - cc +# The envelope will be sent first to the signer. +# After it is signed, a copy is sent to the cc person. + + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$doc1Base64 = New-TemporaryFile +$doc2Base64 = New-TemporaryFile +$doc3Base64 = New-TemporaryFile + +$SIGNER1_EMAIL = Read-Host 'Please enter signer #1 email address' +$SIGNER1_NAME = Read-Host 'Please enter signer #1 name' +$SIGNER2_EMAIL = Read-Host 'Please enter signer #2 email address' +$SIGNER2_NAME = Read-Host 'Please enter signer #2 name' +$CC_EMAIL = Read-Host 'Please enter carbon copy email address' +$CC_NAME = Read-Host 'Please enter carbon copy name' + +#ds-snippet-start:eSign40Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign40Step2 + + +# Fetch docs and encode +$doc1String = [System.IO.File]::ReadAllText((Resolve-Path ".\demo_documents\doc_1.html")) +$doc1String = $doc1String.Replace("{USER_EMAIL}", $SIGNER1_EMAIL) +$doc1String = $doc1String.Replace("{USER_FULLNAME}", $SIGNER1_NAME) +$doc1String = $doc1String.Replace("{CC_EMAIL}", $CC_EMAIL) +$doc1String = $doc1String.Replace("{CC_NAME}", $CC_NAME) +[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($doc1String)) > $doc1Base64 +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx"))) > $doc2Base64 +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $doc3Base64 + +Write-Output "Sending the envelope request to Docusign..." +Write-Output "The envelope has three documents. Processing time will be about 15 seconds." +Write-Output "Results:" + +# Concatenate the different parts of the request +#ds-snippet-start:eSign40Step3 +@{ + emailSubject = "Please sign this document set"; + enforceSignerVisibility = "true"; + documents = @( + @{ + documentBase64 = "$(Get-Content $doc1Base64)"; + name = "Order acknowledgement"; + fileExtension = "html"; + documentId = "1"; + }; + @{ + documentBase64 = "$(Get-Content $doc2Base64)"; + name = "Battle Plan"; + fileExtension = "docx"; + documentId = "2"; + }; + @{ + documentBase64 = "$(Get-Content $doc3Base64)"; + name = "Lorem Ipsum"; + fileExtension = "pdf"; + documentId = "3"; + }; ); + recipients = @{ + carbonCopies = @( + @{ + email = $CC_EMAIL; + name = $CC_NAME; + recipientId = "3"; + routingOrder = "3"; + }; + ); + signers = @( + @{ + email = $SIGNER1_EMAIL; + name = $SIGNER1_NAME; + recipientId = "1"; + routingOrder = "1"; + excludedDocuments = @(2, 3) + tabs = @{ + signHereTabs = @( + @{ + anchorString = "**signature_1**"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + @{ + email = $SIGNER2_EMAIL; + name = $SIGNER2_NAME; + recipientId = "2"; + routingOrder = "2"; + excludedDocuments = @(1) + tabs = @{ + signHereTabs = @( + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign40Step3 + +# Step 3. Create and send the envelope' +#ds-snippet-start:eSign40Step4 +try { + Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers $headers ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign40Step4 + + Write-Output "Response: $(Get-Content -Raw $response)" + + # pull out the envelopeId + $envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId + + Write-Output "EnvelopeId: $envelopeId" +} +catch { + $errorMessage = $_.ErrorDetails.Message + Write-Host $errorMessage + Write-Host "" + + if ( $errorMessage.Contains("ACCOUNT_LACKS_PERMISSIONS") ) { + Write-Host "See https://developers.docusign.com/docs/esign-rest-api/how-to/set-document-visibility in the Docusign Developer Center for instructions on how to enable document visibility in your developer account." + } +} + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $doc1Base64 +Remove-Item $doc2Base64 +Remove-Item $doc3Base64 + +Write-Output "Done." diff --git a/examples/eSignature/eg041EmbeddedSigningCFR.ps1 b/examples/eSignature/eg041EmbeddedSigningCFR.ps1 new file mode 100644 index 0000000..feb582e --- /dev/null +++ b/examples/eSignature/eg041EmbeddedSigningCFR.ps1 @@ -0,0 +1,191 @@ +$apiUri = "https://demo.docusign.net/restapi" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" + + +# Check the folder structure to switch paths for Quick ACG +if ((Test-Path $configPath) -eq $false) { + $configPath = "..\config\settings.json" +} +if ((Test-Path $tokenPath) -eq $false) { + $tokenPath = "..\config\ds_access_token.txt" +} +if ((Test-Path $accountIdPath) -eq $false) { + $accountIdPath = "..\config\API_ACCOUNT_ID" +} + +# Use embedded signing + +# Get required variables from .\config\settings.json file +$variables = Get-Content $configPath -Raw | ConvertFrom-Json +$SIGNER_EMAIL = $variables.SIGNER_EMAIL +$SIGNER_NAME = $variables.SIGNER_NAME +$PHONE_NUMBER = $variables.PHONE_NUMBER + +# 1. Obtain your OAuth token +$oAuthAccessToken = Get-Content .\config\ds_access_token.txt + +# Obtain your accountId from demo.docusign.net -- the account id is shown in +# the drop down on the upper right corner of the screen by your picture or +# the default picture. +$accountID = Get-Content $accountIdPath +$APIAccountId = Get-Content .\config\API_ACCOUNT_ID + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$doc1Base64 = New-TemporaryFile + +$docPath = ".\demo_documents\World_Wide_Corp_lorem.pdf" + +# Check the folder structure to switch paths for Quick ACG +if ((Test-Path $docPath) -eq $false) { + $docPath = "..\demo_documents\World_Wide_Corp_lorem.pdf" +} + +# Fetch doc and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path $docPath))) > $doc1Base64 +# - Obtain your workflow ID +#ds-snippet-start:eSign41Step2 +$uri = "https://demo.docusign.net/restapi/v2.1/accounts/$APIAccountId/identity_verification" +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $oAuthAccessToken") +$headers.add("Accept", "application/json") +$headers.add("Content-Type", "application/json") +Write-Output "Attempting to retrieve your account's workflow ID" + +$result = Invoke-RestMethod -uri $uri -headers $headers -method GET +$result.content +#Obtain the workflow ID from the API response +$workflowId = [System.Linq.Enumerable]::FirstOrDefault($result.identityVerification, [func[object, bool]] { param($x) $x.defaultName -eq "SMS for Access & Signatures"}).workflowId +#ds-snippet-end:eSign41Step2 + +if ($null -eq $workflowId) +{ + throw "Please contact https://support.docusign.com to enable recipient phone authentication in your account." +} + +# Concatenate the different parts of the request +$SIGNER_COUNTRY_CODE = Read-Host "Please enter a country phone number prefix for the Signer" + +$SIGNER_PHONE_NUMBER = Read-Host "Please enter an SMS-enabled Phone number for the Signer" + +Write-Output "Sending the envelope request to Docusign..." + +# Construct your envelope JSON body +#ds-snippet-start:eSign41Step3 +$body = @" +{ + "documents": [{ + "documentBase64": "$(Get-Content $doc1Base64)", + "documentId": "1", + "fileExtension": "pdf", + "name": "Lorem" + }], + "emailBlurb": "Please let us know if you have any questions.", + "emailSubject": "Part 11 Example Consent Form", + "envelopeIdStamping": "true", + "recipients": { + "signers": [{ + "name": "$SIGNER_NAME", + "email": "$SIGNER_EMAIL", + "roleName": "", + "note": "", + "routingOrder": 2, + "clientUserID": 1000, + "status": "created", + "tabs": { + "signHereTabs": [{ + "documentId": "1", + "name": "SignHereTab", + "pageNumber": "1", + "recipientId": "1", + "tabLabel": "SignHereTab", + "xPosition": "200", + "yPosition": "150" + }] + }, + "templateAccessCodeRequired": null, + "deliveryMethod": "email", + "recipientId": "1", + "identityVerification":{ + "workflowId":"$workflowId", + "steps":null,"inputOptions":[ + {"name":"phone_number_list", + "valueType":"PhoneNumberList", + "phoneNumberList":[ + { + "countryCode":"$SIGNER_COUNTRY_CODE", + "number":"$SIGNER_PHONE_NUMBER" + } + ] + }] + } + }] + }, + "status": "Sent" +} +"@ +#ds-snippet-end:eSign41Step3 +Write-Output "" + +#ds-snippet-start:eSign41Step4 +$uri = "${apiUri}/v2.1/accounts/$APIAccountId/envelopes" +$result = Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST -UseBasicParsing -OutFile $response +$result.content + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId +#ds-snippet-end:eSign41Step4 + +# Create a recipient view definition +# The signer will directly open this link from the browser to sign. +# +# The returnUrl is normally your own web app. Docusign will redirect +# the signer to returnUrl when the signing completes. +# For this example, we'll use http://httpbin.org/get to show the +# query parameters passed back from Docusign + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile + +Write-Output "Requesting the url for the embedded signing..." +#ds-snippet-start:eSign41Step5 +$json = [ordered]@{ + 'returnUrl' = 'http://httpbin.org/get'; + 'authenticationMethod' = 'none'; + 'email' = $variables.SIGNER_EMAIL; + 'userName' = $variables.SIGNER_NAME; + 'clientUserId' = 1000 +} | ConvertTo-Json -Compress +#ds-snippet-end:eSign41Step5 + +# Create the recipient view and begin the Docusign signing +#ds-snippet-start:eSign41Step6 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $oAuthAccessToken"; + 'Content-Type' = "application/json"; +} ` + -Body $json ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" +$signingUrl = $(Get-Content $response | ConvertFrom-Json).url +#ds-snippet-end:eSign41Step6 + +Write-Output "The embedded signing URL is $signingUrl" +Write-Output "It is only valid for five minutes. Attempting to automatically open your browser..." + +Start-Process $signingUrl + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $doc1Base64 + +Write-Output "Done." diff --git a/examples/eSignature/eg042DocumentGeneration.ps1 b/examples/eSignature/eg042DocumentGeneration.ps1 new file mode 100644 index 0000000..a0902e6 --- /dev/null +++ b/examples/eSignature/eg042DocumentGeneration.ps1 @@ -0,0 +1,316 @@ +$apiUri = "https://demo.docusign.net/restapi" + +# Step 1= Obtain your OAuth token +# Note= Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note= Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + + +# Create a template +# +# The envelope has one document and one signer/recipient +# The document must be a Word docx file +# Adding five DocGen fields that will be dynamically set later + +# temp files= +$requestData = New-TemporaryFile +$requestDataTemp = New-TemporaryFile +$doc1Base64 = New-TemporaryFile +$response = New-TemporaryFile + + +$templateName = "Example document generation template" + +Write-Output "Sending the template create request to Docusign..." + +# Fetch document and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\Offer_Letter_Dynamic_Table.docx"))) > $doc1Base64 + +# Concatenate the different parts of the request +#ds-snippet-start:eSign42Step2 +@{ + description = "Example template created via the API"; + name = "${templateName}"; + shared = "false"; + documents = @( + @{ + documentBase64 = "$(Get-Content $doc1Base64)"; + documentId = "1"; + fileExtension = "docx"; + order = "1"; + pages = "1"; + name = "Offer Letter Demo"; + isDocGenDocument = "true"; + + }; + ); + emailSubject = "Please sign this document"; + recipients = @{ + signers = @( + @{ + recipientId = "1"; + roleName = "signer"; + routingOrder = "1"; + }; + ); + }; + status = "created"; + } | ConvertTo-Json -Depth 32 > $requestData + +Write-Output "${apiUri}/v2.1/accounts/${accountId}/templates" + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/templates" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response + +# pull out the template id +$templateId = $(Get-Content $response | ConvertFrom-Json).templateId +#ds-snippet-end:eSign42Step2 +Write-Output "Template '${templateName}' was created! Template ID ${templateId}." + +# Add a document with merge fields to your template +#ds-snippet-start:eSign42Step3 +@{ + documents = @( + @{ + documentBase64 = "$(Get-Content $doc1Base64)"; + documentId = "1"; + fileExtension = "docx"; + order = "1"; + pages = "1"; + name = "Offer Letter Demo"; + + }; + ); +} | ConvertTo-Json -Depth 32 > $requestData + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/templates/${templateId}/documents/1" ` + -Method 'PUT' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign42Step3 + +# Add tabs to the template +#ds-snippet-start:eSign42Step4 +@{ + signHereTabs = @( + @{ + anchorString = "Employee Signature"; + anchorUnits = "pixels"; + anchorXOffset = "5"; + anchorYOffset = "-22"; + }; + ); + dateSignedTabs = @( + @{ + anchorString = "Date Signed"; + anchorUnits = "pixels"; + anchorYOffset = "-22"; + }; + ); +} | ConvertTo-Json -Depth 32 > $requestData + + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/templates/${templateId}/recipients/1/tabs" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign42Step4 + +# Create an envelope draft from a template +# Leave envelope in "draft"/"created" status, don't send it yet +#ds-snippet-start:eSign42Step5 +@{ + templateId = "${templateId}"; + templateRoles = @( + @{ + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + roleName = "signer"; + }; + ); + status = "created"; +} | ConvertTo-Json -Depth 32 > $requestData + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response + +# pull out the envelope id +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId +#ds-snippet-end:eSign42Step5 + +Write-Output "Envelope '${templateName}' draft was created! Envelope ID ${envelopeId}." + +# Get DocGenFormFields +#ds-snippet-start:eSign42Step6 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/docGenFormFields" ` + -Method 'GET' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` +-OutFile $response + +Write-Output "Response:" +Get-Content $response + +# pull out the document id value +$documentId = $(Get-Content $response | ConvertFrom-Json).docGenFormFields[0].documentId +#ds-snippet-end:eSign42Step6 + +Write-Output "Document ID ${documentId}." + +# Build the request to update the data fields in the envelope that we created from the template +# Collect user data to send to API/Document fields +$CandidateName = Read-Host "Enter candidate name" +$ManagerName = Read-Host "Enter manager name" +$StartDate = Read-Host "Enter start date" +Write-Host "Enter salary: $" -NoNewLine +$Salary = Read-Host +$Rsus = Read-Host "Enter RSUs" +Write-Output "Choose job title" +Write-Output "1 - Software Engineer" +Write-Output "2 - Account Executive" +$JobTitle = "Software Engineer" +$JobNumber = Read-Host +if ($JobNumber -eq "2") { + $JobTitle = "Account Executive" +} + +#ds-snippet-start:eSign42Step7 +@{ + docGenFormFields = @( + @{ + documentId = "${documentId}"; + docGenFormFieldList = @( + @{ + name = "Candidate_Name"; + value = "${CandidateName}"; + }; + @{ + name = "Job_Title"; + value = "${JobTitle}"; + }; + @{ + name = "Manager_Name"; + value = "${ManagerName}"; + }; + @{ + name = "Start_Date"; + value = "${StartDate}"; + }; + @{ + name = "Compensation_Package"; + type = "TableRow"; + rowValues = @( + @{ + docGenFormFieldList = @( + @{ + name = "Compensation_Component"; + value = "Salary"; + }; + @{ + name = "Details"; + value = "$" + "${Salary}"; + } + ) + }; + @{ + docGenFormFieldList = @( + @{ + name = "Compensation_Component"; + value = "Bonus"; + }; + @{ + name = "Details"; + value = "20%"; + } + ) + }; + @{ + docGenFormFieldList = @( + @{ + name = "Compensation_Component"; + value = "RSUs"; + }; + @{ + name = "Details"; + value = $rsus; + } + ) + } + ) + } + ) + } + ) +} | ConvertTo-Json -Depth 32 > $requestData + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/docgenformfields" ` + -Method 'PUT' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign42Step7 + +# Send the envelope +#ds-snippet-start:eSign42Step8 +@{ + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/" ` + -Method 'PUT' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign42Step8 +Write-Output "Response:" +Get-Content $response + + +# cleanup +Remove-Item $requestData +Remove-Item $requestDataTemp +Remove-Item $response +Remove-Item $doc1Base64 + +Write-Output "Done." diff --git a/examples/eSignature/eg043SharedAccess.ps1 b/examples/eSignature/eg043SharedAccess.ps1 new file mode 100644 index 0000000..f9104bc --- /dev/null +++ b/examples/eSignature/eg043SharedAccess.ps1 @@ -0,0 +1,223 @@ +. "utils/invokeScript.ps1" + +$apiUri = "https://demo.docusign.net/restapi" +$accountUri = "https://account-d.docusign.com" + +$accessToken = Get-Content .\config\ds_access_token.txt +$accountId = Get-Content .\config\API_ACCOUNT_ID + +$requestData = New-TemporaryFile +$requestDataTemp = New-TemporaryFile +$response = New-TemporaryFile + +#ds-snippet-start:eSign43Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign43Step2 + +Write-Output "" +$agentName = Read-Host "Please enter the name of the new agent" +Write-Output "" +$agentEmail = Read-Host "Please enter the email address of the new agent" +Write-Output "" +$activation = Read-Host "Please input an activation code for the new agent. Save this code. You'll need it when activating the new agent." + +#ds-snippet-start:eSign43Step3 +try { + # Check, if the agent already exists + Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/users?email=${agentEmail}&status=Active" ` + -Method 'GET' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -OutFile $response + + Write-Output "" + Write-Output "Response:" + Write-Output "" + Get-Content $response + + $agentUserId = $(Get-Content $response | ConvertFrom-Json).users.userId +} catch { + # Create a new agent in the account + @{ + newUsers = @( + @{ + activationAccessCode = $activation; + userName = $agentName; + email = $agentEmail; + }; + ); + } | ConvertTo-Json -Depth 32 > $requestData + + Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/users" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response + + Write-Output "" + Write-Output "Response:" + Write-Output "" + Get-Content $response + + $agentUserId = $(Get-Content $response | ConvertFrom-Json).newUsers.userId + #ds-snippet-end:eSign43Step3 +} + +Write-Output "" +Write-Output "Agent has been created. Please go to the agent's email to activate the agent, and press 1 to continue the example: " + +$choice = Read-Host +if ($choice -ne "1") { + Write-Output "Closing the example... " + exit 1 +} + +try { + # Get user id of the currently logged user + Invoke-RestMethod ` + -Uri "${accountUri}/oauth/userinfo" ` + -Method 'GET' ` + -Headers @{ + 'Cache-Control' = "no-store"; + 'Pragma' = "cache"; + 'Authorization' = "Bearer $accessToken"; + } ` + -Body @{ "from_date" = ${fromDate} } ` + -OutFile $response + + $userId = $(Get-Content $response | ConvertFrom-Json).sub +} catch { + Write-Output "Unable to retrieve Bulk Status." + + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} + +#ds-snippet-start:eSign43Step4 +$isUserActivated = 0; + +do { + try { + # Check, if authorization exists + Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/users/${agentUserId}/authorizations/agent?permissions=manage" ` + -Method 'GET' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -OutFile $response + + if ([string]::IsNullOrEmpty($(Get-Content $response | ConvertFrom-Json).authorizations)) { + # Sharing the envelope with the agent + $body = @" + { + "agentUser": + { + "userId": "${agentUserId}", + "accountId": "${accountId}" + }, + "permission": "manage" + } +"@ + + Write-Output "" + $uri = "${apiUri}/v2.1/accounts/${accountId}/users/${userId}/authorization" + + $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" + $headers.add("Authorization", "Bearer $accessToken") + $headers.add("Content-Type", "application/json") + + Invoke-WebRequest -uri $uri -headers $headers -body $body -method POST -UseBasicParsing -OutFile $response + + Write-Output "Response:" + Write-Output "$(Get-Content -Raw $response)" + } + + $isUserActivated = 1; + #ds-snippet-end:eSign43Step4 + } catch { + Write-Output "Agent has been created. Please go to the agent's email to activate the agent, and press 1 to continue the example: " + + $choice = Read-Host + if ($choice -ne "1") { + Write-Output "Closing the example... " + exit 1 + } + } +} while (!$isUserActivated) + +# Principal is told to log out and log in as the new agent +Write-Output "" +Write-Output "Please go to the principal's developer account at admindemo.docusign.com and log out, then come back to this terminal. Press 1 to continue: " + +$choice = Read-Host +if ($choice -ne "1") { + Write-Output "Closing the example... " + exit 1 +} + +Invoke-Script -Command "`".\utils\sharedAccess.ps1`"" + +#ds-snippet-start:eSign43Step5 +try { + # Make the API call to check the envelope + # Get date in the ISO 8601 format + $fromDate = ((Get-Date).AddDays(-10d)).ToString("yyyy-MM-ddThh:mm:ssK") + + $response = New-TemporaryFile + + Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'GET' ` + -Headers @{ + 'X-DocuSign-Act-On-Behalf' = $userId; + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -Body @{ "from_date" = ${fromDate} } ` + -OutFile $response + + if ([string]::IsNullOrEmpty($response)) + { + Write-Output "" + Write-Output "Response body is empty because there are no envelopes in the account. Please run example 2 and re-run this example." + } else { + Write-Output "" + Write-Output "Response:" + Write-Output "" + + Get-Content $response + } + #ds-snippet-end:eSign43Step5 +} catch { + Write-Output "Unable to retrieve Bulk Status." + + foreach ($header in $_.Exception.Response.Headers) { + if ($header -eq "X-DocuSign-TraceToken") { Write-Output "TraceToken : " $_.Exception.Response.Headers[$int] } + $int++ + } + Write-Output "Error : "$_.ErrorDetails.Message + Write-Output "Command : "$_.InvocationInfo.Line +} + +# cleanup +Remove-Item $requestData +Remove-Item $requestDataTemp +Remove-Item $response + +Write-Output "" +Write-Output "Done." diff --git a/examples/eSignature/eg044FocusedView.ps1 b/examples/eSignature/eg044FocusedView.ps1 new file mode 100644 index 0000000..fe7f569 --- /dev/null +++ b/examples/eSignature/eg044FocusedView.ps1 @@ -0,0 +1,145 @@ +$apiUri = "https://demo.docusign.net/restapi" +$configPath = ".\config\settings.json" +$tokenPath = ".\config\ds_access_token.txt" +$accountIdPath = ".\config\API_ACCOUNT_ID" + +# Get required variables from .\config\settings.json file +$variables = Get-Content $configPath -Raw | ConvertFrom-Json + +# 1. Obtain your OAuth token +$accessToken = Get-Content $tokenPath + +# Obtain your accountId from demo.docusign.net -- the account id is shown in +# the drop down on the upper right corner of the screen by your picture or +# the default picture. +$accountID = Get-Content $accountIdPath + +# Step 2. Create the envelope definition. + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile +$doc1Base64 = New-TemporaryFile + +$docPath = ".\demo_documents\World_Wide_Corp_lorem.pdf" + +# Check the folder structure to switch paths for Quick ACG +if ((Test-Path $docPath) -eq $false) { + $docPath = "..\demo_documents\World_Wide_Corp_lorem.pdf" +} + +# Fetch doc and encode +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path $docPath))) > $doc1Base64 + +Write-Output "Sending the envelope request to Docusign..." +Write-Output "" + +# Concatenate the different parts of the request +#ds-snippet-start:eSign44Step2 +@{ + emailSubject = "Please sign this document set"; + documents = @( + @{ + documentBase64 = "$(Get-Content $doc1Base64)"; + name = "Lorem Ipsum"; + fileExtension = "pdf"; + documentId = "1"; + }; + ); + recipients = @{ + signers = @( + @{ + email = $variables.SIGNER_EMAIL; + name = $variables.SIGNER_NAME; + recipientId = "1"; + routingOrder = "1"; + clientUserId = "1000"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign44Step2 + +# Step 3. Call Docusign to create the envelope +#ds-snippet-start:eSign44Step3 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign44Step3 + +Write-Output "Response: $(Get-Content -Raw $response)" +Write-Output "" + +# pull out the envelopeId +$envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId +Write-Output "EnvelopeId: $envelopeId" +Write-Output "" + +# Step 4. Create a recipient view definition +# The signer will directly open this link from the browser to sign. +# +# The returnUrl is normally your own web app. Docusign will redirect +# the signer to returnUrl when the signing completes. +# For this example, we'll use http://httpbin.org/get to show the +# query parameters passed back from Docusign + +#ds-snippet-start:eSign44Step4 +Write-Output "Requesting the url for the embedded signing..." +Write-Output "" + +$json = @{ + returnUrl = "http://httpbin.org/get" + authenticationMethod = "none" + email = $variables.SIGNER_EMAIL + userName = $variables.SIGNER_NAME + clientUserId = 1000 + frameAncestors = @("http://localhost:8080", "https://apps-d.docusign.com") + messageOrigins = @("https://apps-d.docusign.com") +} + +$jsonString = $json | ConvertTo-Json +#ds-snippet-end:eSign44Step4 + +# Step 5. Create the recipient view and begin the Docusign signing +#ds-snippet-start:eSign44Step5 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -Body $jsonString ` + -OutFile $response + +Write-Output "Response: $(Get-Content -Raw $response)" + +$signingUrl = $(Get-Content $response | ConvertFrom-Json).url +#ds-snippet-end:eSign44Step5 + +Start-Process -NoNewWindow -FilePath "powershell" -ArgumentList "-File .\utils\startServerForFocusedView.ps1 -signingURL $signingUrl" + +# cleanup +Remove-Item $requestData +Remove-Item $response +Remove-Item $doc1Base64 + +Write-Output "" +Write-Output "Done." \ No newline at end of file diff --git a/examples/eSignature/eg045DeleteRestoreEnvelope.ps1 b/examples/eSignature/eg045DeleteRestoreEnvelope.ps1 new file mode 100644 index 0000000..8b8114d --- /dev/null +++ b/examples/eSignature/eg045DeleteRestoreEnvelope.ps1 @@ -0,0 +1,152 @@ +$apiUri = "https://demo.docusign.net/restapi" + +# Delete and Undelete an Envelope + +# Get required environment variables from .\config\settings.json file +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json + + +# Obtain your OAuth token +# Note: Substitute these values with your own +$accessToken = Get-Content .\config\ds_access_token.txt + +# Set up variables for full code example +# Note: Substitute these values with your own +$accountId = Get-Content .\config\API_ACCOUNT_ID + +# temp files: +$requestData = New-TemporaryFile +$response = New-TemporaryFile + +$recycle_bin_folder_id = "recyclebin" + +Write-Output "Select the envelope ID to use for the delete and undelete operations." +if (Test-Path .\config\ENVELOPE_ID) { + $envelopeIdFromFile = Get-Content .\config\ENVELOPE_ID + + $userSavedEnvelope = Read-Host "Use the envelope ID from 'config/ENVELOPE_ID' (${envelopeIdFromFile})? (y/n)" + switch ($userSavedEnvelope.ToLower()) { + "y" { + $envelopeId = $envelopeIdFromFile + } + default { + $envelopeId = Read-Host "Please enter the new envelope ID" + } + } +} else { + $envelopeId = Read-Host "No envelope ID found. Please enter the envelope ID" +} + +if (-not $envelopeId) { + Write-Output "ERROR: No envelope ID was provided" + exit 1 +} + +Write-Output "Deleting the Envelope with ID: ${envelopeId}" +Write-Output "Sending PUT request to Docusign..." +Write-Output "Results:" + +#ds-snippet-start:eSign45Step2 +$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$headers.add("Authorization", "Bearer $accessToken") +$headers.add("Content-Type", "application/json") +#ds-snippet-end:eSign45Step2 + +# Concatenate the different parts of the request +#ds-snippet-start:eSign45Step3 +@{ + envelopeIds = @("$envelopeId") +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign45Step3 + +# Create and send the folders request +#ds-snippet-start:eSign45Step4 +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/folders/${recycle_bin_folder_id}" ` + -Method 'PUT' ` + -Headers $headers ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response ` +#ds-snippet-end:eSign45Step4 + +Write-Output "The deleted envelope is now in your Docusign Recycle Bin." +Write-Output "You can check your web app to confirm the deletion." + +Read-Host "Press Enter to proceed with undeleting the envelope from the Recycle Bin..." +$destinationFolderName = Read-Host "Please enter the name of the folder to undelete the envelope to (e.g., 'Sent Items') or press Enter to use the default" + +if (-not $destinationFolderName) { + $destinationFolderName = "Sent Items" + Write-Output "The undeleted item will be moved to the Sent Items folder" +} + +Write-Output "Searching for folder with name: '${destinationFolderName}'..." + +#ds-snippet-start:eSign45Step5 +function Get-FolderIdByName { + param ( + [object]$folders, + [string]$targetName + ) + + foreach ($folder in $folders) { + # Check this folder + if ($folder.name -eq $targetName) { + return $folder.folderId + } + + # If this folder has subfolders, search inside them + if ($folder.folders) { + $result = Get-FolderIdByName -folders $folder.folders -targetName $targetName + if ($result) { + return $result + } + } + } + + return $null +} + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/folders" ` + -Method 'GET' ` + -Headers $headers ` + -OutFile $response + +$folders = $(Get-Content $response | ConvertFrom-Json).folders +$folderId = Get-FolderIdByName -folders $folders -targetName $destinationFolderName +#ds-snippet-end:eSign45Step5 + +if (-not $folderId) { + Write-Output "ERROR: Could not find a folder with the name '${destinationFolderName}'. Please check the spelling." + exit 1 +} + +Write-Output "Found folder ID: ${folderId} for folder name: '${destinationFolderName}'" + +Write-Output "Undeleting the Envelope from Recycle Bin to the '${destinationFolderName}' folder." +Write-Output "Sending PUT request to Docusign..." +Write-Output "Results:" + +#ds-snippet-start:eSign45Step6 +@{ + envelopeIds = @("$envelopeId"); + fromFolderId = "$recycle_bin_folder_id" +} | ConvertTo-Json -Depth 32 > $requestData + +# Create and send the folders request +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/folders/${folderId}" ` + -Method 'PUT' ` + -Headers $headers ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response +#ds-snippet-end:eSign45Step6 + +Write-Output "The envelope has been undeleted and is now in your '${destinationFolderName}' folder." + +# cleanup +Remove-Item $requestData +Remove-Item $response + +Write-Output "Done." diff --git a/examples/eSignature/eg046MultipleDelivery.ps1 b/examples/eSignature/eg046MultipleDelivery.ps1 new file mode 100644 index 0000000..2c5ba39 --- /dev/null +++ b/examples/eSignature/eg046MultipleDelivery.ps1 @@ -0,0 +1,163 @@ +#Email and SMS or WhatsApp Delivery +$apiUri = "https://demo.docusign.net/restapi" + +$variables = Get-Content .\config\settings.json -Raw | ConvertFrom-Json +$accessToken = Get-Content .\config\ds_access_token.txt +$accountId = Get-Content .\config\API_ACCOUNT_ID + +$doc1Base64 = New-TemporaryFile +$doc2Base64 = New-TemporaryFile +$doc3Base64 = New-TemporaryFile + +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\doc_1.html"))) > $doc1Base64 +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_Battle_Plan_Trafalgar.docx"))) > $doc2Base64 +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents\World_Wide_Corp_lorem.pdf"))) > $doc3Base64 + +Write-Host "Choose a message delivery type:" +Write-Host "1 - SMS" +Write-Host "2 - WhatsApp" + +do { + $choice = Read-Host "Select 1 or 2" +} while ($choice -notin @("1", "2")) + +if ($choice -eq "1") { + $deliveryMethod = "SMS" +} +else { + $deliveryMethod = "WhatsApp" +} + +$signerCountry = Read-Host "Please enter a country phone number prefix for the Signer" +$signerNumber = Read-Host "Please enter a Mobile number for the Signer" + +$ccCountry = Read-Host "Please enter a country phone number prefix for the Carbon Copied recipient" +$ccNumber = Read-Host "Please enter a Mobile number for the Carbon Copied recipient" + +#ds-snippet-start:eSign46Step2 +$requestData = New-TemporaryFile +$response = New-TemporaryFile + +@{ + emailSubject = "Please sign this document set"; + documents = @( + @{ + documentBase64 = "$(Get-Content $doc1Base64)"; + name = "Order acknowledgement"; + fileExtension = "html"; + documentId = "1"; + }; + @{ + documentBase64 = "$(Get-Content $doc2Base64)"; + name = "Battle Plan"; + fileExtension = "docx"; + documentId = "2"; + }; + @{ + documentBase64 = "$(Get-Content $doc3Base64)"; + name = "Lorem Ipsum"; + fileExtension = "pdf"; + documentId = "3"; + }; + ); + recipients = @{ + carbonCopies = @( + @{ + additionalNotifications = @( + @{ + secondaryDeliveryMethod = $deliveryMethod; + phoneNumber = @{ + countryCode = $ccCountry; + number = $ccNumber; + }; + }; + ); + name = $variables.CC_NAME; + email = $variables.CC_EMAIL; + recipientId = "2"; + routingOrder = "2"; + deliveryMethod = "Email"; + }; + ); + signers = @( + @{ + additionalNotifications = @( + @{ + secondaryDeliveryMethod = $deliveryMethod; + phoneNumber = @{ + countryCode = $signerCountry; + number = $signerNumber; + }; + }; + ); + name = $variables.SIGNER_NAME; + email = $variables.SIGNER_EMAIL; + recipientId = "1"; + routingOrder = "1"; + deliveryMethod = "Email"; + tabs = @{ + signHereTabs = @( + @{ + anchorString = "**signature_1**"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + @{ + anchorString = "/sn1/"; + anchorUnits = "pixels"; + anchorXOffset = "20"; + anchorYOffset = "10"; + }; + ); + }; + }; + ); + }; + status = "sent"; +} | ConvertTo-Json -Depth 32 > $requestData +#ds-snippet-end:eSign46Step2 + +Write-Output "Sending the envelope request to Docusign..." +Write-Output "The envelope has three documents. Processing time will be about 15 seconds." +Write-Output "Results:" +Write-Output $requestData + +try { + #ds-snippet-start:eSign46Step3 + Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/envelopes" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -InFile (Resolve-Path $requestData).Path ` + -OutFile $response + + Write-Output "Response: $(Get-Content -Raw $response)" + + $envelopeId = $(Get-Content $response | ConvertFrom-Json).envelopeId + #ds-snippet-end:eSign46Step3 + Write-Output "Response: $response" + + $envelopeId = $($response.envelopeId) + + Write-Output "EnvelopeId: $envelopeId" + Write-Output $envelopeId > .\config\ENVELOPE_ID +} catch { + Write-Error ( + "This account does not have sufficient permissions to send envelopes via SMS. " + + "To enable multi-channel delivery, please contact DocuSign Support: " + + "https://developers.docusign.com/support" + ) + exit 1 +} + +Remove-Item $requestData +Remove-Item $response +Remove-Item $doc1Base64 +Remove-Item $doc2Base64 +Remove-Item $doc3Base64 + +Write-Output "Done." diff --git a/launcher.ps1 b/launcher.ps1 index 43d7284..57cb624 100644 --- a/launcher.ps1 +++ b/launcher.ps1 @@ -1,6 +1,9 @@ +. "utils/invokeScript.ps1" + $ErrorActionPreference = "Stop" # force stop on failure $configFile = ".\config\settings.json" +$emailAddressFile = ".\config\ESIGN_CLM_USER_EMAIL" if ((Test-Path $configFile) -eq $False) { Write-Output "Error: " @@ -8,21 +11,85 @@ if ((Test-Path $configFile) -eq $False) { Write-Output "Next, fill in your API credentials, Signer name and email to continue." } +# Check that we have an email address stored after running the 2 Admin code example +# in case the file was created before - delete it +if (Test-Path $emailAddressFile) { + Remove-Item $emailAddressFile +} + # Get required environment variables from .\config\settings.json file $config = Get-Content $configFile -Raw | ConvertFrom-Json +function isCFR { + $response = New-TemporaryFile + $accessToken = Get-Content .\config\ds_access_token.txt + $accountId = Get-Content .\config\API_ACCOUNT_ID + + Invoke-RestMethod ` + -Uri "https://demo.docusign.net/restapi/v2.1/accounts/$accountId/settings" ` + -Method 'GET' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; + } ` + -OutFile $response + $env:CFR_STATUS = Select-String -Pattern '"require21CFRpt11Compliance":"true"' -Path $response +} -# Fill in Quickstart Carbon Copy config values -if($config.CC_EMAIL -eq "{CC_EMAIL}" ){ - Write-Output "It looks like this is your first time running the launcher from Quickstart. " - $config.CC_EMAIL = Read-Host "Enter a CC email address to receive copies of envelopes" - $config.CC_NAME = Read-Host "Enter a name for your CC recipient" - $config.SIGNER_NOT_CHECKED_EMAIL = "Enter an email address to route to when the checkbox is not checked" - $config.SIGNER_NOT_CHECKED_NAME = "Enter a name address to route to when the checkbox is not checked" - Write-Output "" - write-output $config | ConvertTo-Json | Set-Content $configFile +function checkEmailAddresses { + + if (-not [system.Text.RegularExpressions.Regex]::IsMatch($config.SIGNER_EMAIL, + "^(?("")("".+?(? .\config\WEB_FORM_TEMPLATE_ID + Remove-Item $response + Write-Output "Done." + exit 0 +} + +$requestData = New-TemporaryFile +$requestDataTemp = New-TemporaryFile +$doc1Base64 = New-TemporaryFile + +Write-Output "Sending the template create request to Docusign..." + +[Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Resolve-Path ".\demo_documents/World_Wide_Corp_Web_Form.pdf"))) > $doc1Base64 + +$json = @" +{ + "description": "Example template created via the API", + "name": "Web Form Example Template", + "shared": "false", + "documents": [ + { + "documentBase64": "$(Get-Content $doc1Base64)", + "documentId": "1", + "fileExtension": "pdf", + "name": "World_Wide_Web_Form" + } + ], + "emailSubject": "Please sign this document", + "recipients": { + "signers": [ + { + "recipientId": "1", + "roleName": "signer", + "routingOrder": "1", + "tabs": { + "checkboxTabs": [ + { + "documentId": "1", + "tabLabel": "Yes", + "anchorString": "/SMS/", + "anchorUnits": "pixels", + "anchorXOffset": "0", + "anchorYOffset": "0" + } + ], + "signHereTabs": [ + { + "documentId": "1", + "tabLabel": "Signature", + "anchorString": "/SignHere/", + "anchorUnits": "pixels", + "anchorXOffset": "20", + "anchorYOffset": "10" + } + ], + "textTabs": [ + { + "documentId": "1", + "tabLabel": "FullName", + "anchorString": "/FullName/", + "anchorUnits": "pixels", + "anchorXOffset": "0", + "anchorYOffset": "0" + }, + { + "documentId": "1", + "tabLabel": "PhoneNumber", + "anchorString": "/PhoneNumber/", + "anchorUnits": "pixels", + "anchorXOffset": "0", + "anchorYOffset": "0" + }, + { + "documentId": "1", + "tabLabel": "Company", + "anchorString": "/Company/", + "anchorUnits": "pixels", + "anchorXOffset": "0", + "anchorYOffset": "0" + }, + { + "documentId": "1", + "tabLabel": "JobTitle", + "anchorString": "/Title/", + "anchorUnits": "pixels", + "anchorXOffset": "0", + "anchorYOffset": "0" + } + ], + "dateSignedTabs": [ + { + "documentId": "1", + "tabLabel": "DateSigned", + "anchorString": "/Date/", + "anchorUnits": "pixels", + "anchorXOffset": "0", + "anchorYOffset": "0" + } + ] + } + } + ] + }, + "status": "created" +} +"@ + +Invoke-RestMethod ` + -Uri "${apiUri}/v2.1/accounts/${accountId}/templates" ` + -Method 'POST' ` + -Headers @{ + 'Authorization' = "Bearer $accessToken"; + 'Content-Type' = "application/json"; +} ` + -Body $json ` + -OutFile $response + +Write-Output "Results:" +Get-Content $response + +$templateId = $(Get-Content $response | ConvertFrom-Json).templateId + +Write-Output "Template '${templateName}' was created! Template ID ${templateId}." +Write-Output ${templateId} > .\config\WEB_FORM_TEMPLATE_ID + +Remove-Item $requestData +Remove-Item $requestDataTemp +Remove-Item $response +Remove-Item $doc1Base64 + +Write-Output "Done." diff --git a/utils/invokeScript.ps1 b/utils/invokeScript.ps1 new file mode 100644 index 0000000..838a894 --- /dev/null +++ b/utils/invokeScript.ps1 @@ -0,0 +1,28 @@ +function Invoke-Script { + param ( + [string]$Command + ) + + # Get the path to the PowerShell executable + $powershellPath = if ([System.Environment]::OSVersion.Platform -eq "Win32NT") { + try { + (Get-Command powershell.exe -ErrorAction Stop).Source + } catch { + "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" + } + } else { + try { + (Get-Command pwsh -ErrorAction Stop).Source + } catch { + "/usr/local/bin/pwsh" + } + } + + $powershellPath = $powershellPath -replace ' ', '` ' # add ` if the path has spaces + + # Execute the script using the appropriate PowerShell executable + $fullCommand = "$powershellPath -File $Command" + + # Execute the command + Invoke-Expression $fullCommand +} diff --git a/utils/sharedAccess.ps1 b/utils/sharedAccess.ps1 new file mode 100644 index 0000000..482a630 --- /dev/null +++ b/utils/sharedAccess.ps1 @@ -0,0 +1,44 @@ +. "utils/invokeScript.ps1" + +$configFile = ".\config\settings.json" + +if ((Test-Path $configFile) -eq $False) { + Write-Output "Error: " + Write-Output "First copy the file '.\config\settings.example.json' to '$configFile'." + Write-Output "Next, fill in your API credentials, Signer name and email to continue." +} + +# Get required environment variables from .\config\settings.json file +$config = Get-Content $configFile -Raw | ConvertFrom-Json + +Enum AuthType { + CodeGrant = 1; + JWT = 2; + Exit = 3; +} + +$AuthTypeView = $null; +do { + Write-Output "" + Write-Output 'Choose an OAuth Strategy: ' + Write-Output "$([int][AuthType]::CodeGrant)) Authorization Code Grant" + Write-Output "$([int][AuthType]::JWT)) Json Web Token (JWT)" + Write-Output "$([int][AuthType]::Exit)) Exit" + [int]$AuthTypeView = Read-Host "Choose an OAuth Strategy. Then log in as the new user that you just created." +} while (-not [AuthType]::IsDefined([AuthType], $AuthTypeView)); + +if ($AuthTypeView -eq [AuthType]::Exit) { + exit 1; +} +elseif ($AuthTypeView -eq [AuthType]::CodeGrant) { + Invoke-Script -Command "`".\OAuth\code_grant.ps1`" -clientId $($config.INTEGRATION_KEY_AUTH_CODE) -clientSecret $($config.SECRET_KEY) -apiVersion $("eSignature") -targetAccountId $($config.TARGET_ACCOUNT_ID)" + if ((Test-Path "./config/ds_access_token.txt") -eq $false) { + Write-Error "Failed to retrieve OAuth Access token, check your settings.json and that port 8080 is not in use" -ErrorAction Stop + } +} +elseif ($AuthTypeView -eq [AuthType]::JWT) { + Invoke-Script -Command "`".\OAuth\jwt.ps1`" -clientId $($config.INTEGRATION_KEY_AUTH_CODE) -apiVersion $("eSignature") -targetAccountId $($config.TARGET_ACCOUNT_ID)" + if ((Test-Path "./config/ds_access_token.txt") -eq $false) { + Write-Error "Failed to retrieve OAuth Access token, check your settings.json and that port 8080 is not in use" -ErrorAction Stop + } +} diff --git a/utils/startServerForFocusedView.ps1 b/utils/startServerForFocusedView.ps1 new file mode 100644 index 0000000..2a14c14 --- /dev/null +++ b/utils/startServerForFocusedView.ps1 @@ -0,0 +1,108 @@ +param( + [Parameter(Mandatory = $true)] + [string]$signingURL + ) + +$port = '8080' +$ip = 'localhost' + +# Get required environment variables from ..\config\settings.json file +$configFile = ".\config\settings.json" +$config = Get-Content $configFile -Raw | ConvertFrom-Json +$integrationKey = $config.INTEGRATION_KEY_AUTH_CODE + +$socket = 'http://' + $ip + ':' + $port + '/' + +$responseOk = @" + +
+

The document has been embedded with focused view.

+
+ + + + + Signing + + + +
+ + +

Continue

+ + + + +"@ + +[Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } + +$listener = New-Object System.Net.HttpListener +$listener.Prefixes.Add($socket) +$listener.Start() +Start-Process $socket + +$context = $listener.GetContext() +$response = $context.Response + +$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseOk) +$response.ContentType = "text/html" +$response.ContentLength64 = $buffer.Length +$output = $response.OutputStream +$output.Write($buffer, 0, $buffer.Length) +$output.Close() diff --git a/utils/startServerForWebFormsExample.ps1 b/utils/startServerForWebFormsExample.ps1 new file mode 100644 index 0000000..627ac23 --- /dev/null +++ b/utils/startServerForWebFormsExample.ps1 @@ -0,0 +1,122 @@ +param( + [Parameter(Mandatory = $true)] + [string]$integrationKey, + [Parameter(Mandatory = $true)] + [string]$url, + [Parameter(Mandatory = $true)] + [string]$instanceToken + ) + +$port = '8080' +$ip = 'localhost' + +$socket = 'http://' + $ip + ':' + $port + '/' + +$responseOk = @" + + + + + + + +
+
+

Embedded Web Form Example

+
+

Web Form will render here

+
+
+
+ + + + + + + +"@ + +[Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } + +$listener = New-Object System.Net.HttpListener +$listener.Prefixes.Add($socket) +$listener.Start() +Start-Process $socket + +$context = $listener.GetContext() +$response = $context.Response + +$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseOk) +$response.ContentType = "text/html" +$response.ContentLength64 = $buffer.Length +$output = $response.OutputStream +$output.Write($buffer, 0, $buffer.Length) +$output.Close()